aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.mailmap21
-rw-r--r--AUTHORS24
-rw-r--r--VERSION2
-rw-r--r--accounts/abi/bind/backends/simulated.go2
-rw-r--r--accounts/keystore/account_cache.go2
-rw-r--r--accounts/keystore/account_cache_test.go2
-rw-r--r--accounts/keystore/keystore.go2
-rw-r--r--accounts/keystore/keystore_test.go2
-rw-r--r--build/ci-notes.md12
-rw-r--r--build/ci.go32
-rw-r--r--build/deb.control4
-rw-r--r--build/deb.rules2
-rw-r--r--build/update-license.go24
-rw-r--r--cmd/faucet/faucet.go455
-rw-r--r--cmd/faucet/faucet.html143
-rw-r--r--cmd/faucet/website.go235
-rw-r--r--cmd/geth/accountcmd.go11
-rw-r--r--cmd/geth/chaincmd.go2
-rw-r--r--cmd/geth/config.go186
-rw-r--r--cmd/geth/consolecmd_test.go36
-rw-r--r--cmd/geth/dao_test.go24
-rw-r--r--cmd/geth/main.go62
-rw-r--r--cmd/geth/usage.go23
-rw-r--r--cmd/puppeth/module.go152
-rw-r--r--cmd/puppeth/module_dashboard.go537
-rw-r--r--cmd/puppeth/module_ethstats.go159
-rw-r--r--cmd/puppeth/module_faucet.go210
-rw-r--r--cmd/puppeth/module_nginx.go106
-rw-r--r--cmd/puppeth/module_node.go222
-rw-r--r--cmd/puppeth/puppeth.go55
-rw-r--r--cmd/puppeth/ssh.go195
-rw-r--r--cmd/puppeth/wizard.go229
-rw-r--r--cmd/puppeth/wizard_dashboard.go132
-rw-r--r--cmd/puppeth/wizard_ethstats.go79
-rw-r--r--cmd/puppeth/wizard_faucet.go172
-rw-r--r--cmd/puppeth/wizard_genesis.go136
-rw-r--r--cmd/puppeth/wizard_intro.go153
-rw-r--r--cmd/puppeth/wizard_netstats.go235
-rw-r--r--cmd/puppeth/wizard_network.go194
-rw-r--r--cmd/puppeth/wizard_nginx.go58
-rw-r--r--cmd/puppeth/wizard_node.go153
-rw-r--r--cmd/rlpdump/main.go4
-rw-r--r--cmd/swarm/cleandb.go16
-rw-r--r--cmd/swarm/list.go2
-rw-r--r--cmd/swarm/main.go64
-rw-r--r--cmd/swarm/manifest.go2
-rw-r--r--cmd/utils/customflags.go53
-rw-r--r--cmd/utils/flags.go553
-rw-r--r--cmd/wnode/main.go3
-rw-r--r--common/compiler/solidity.go95
-rw-r--r--common/compiler/solidity_test.go37
-rw-r--r--common/math/big.go3
-rw-r--r--common/math/big_test.go2
-rw-r--r--consensus/clique/api.go24
-rw-r--r--consensus/clique/clique.go12
-rw-r--r--consensus/clique/snapshot.go52
-rw-r--r--consensus/clique/snapshot_test.go1
-rw-r--r--consensus/consensus.go8
-rw-r--r--consensus/ethash/consensus.go135
-rw-r--r--consensus/ethash/ethash.go13
-rw-r--r--console/console_test.go2
-rw-r--r--core/blockchain.go5
-rw-r--r--core/chain_makers.go2
-rw-r--r--core/dao_test.go8
-rw-r--r--core/evm.go27
-rw-r--r--core/headerchain.go3
-rw-r--r--core/state_processor.go7
-rw-r--r--core/vm/gas_table.go16
-rw-r--r--core/vm/gas_table_test.go16
-rw-r--r--core/vm/int_pool_verifier.go16
-rw-r--r--core/vm/int_pool_verifier_empty.go16
-rw-r--r--core/vm/interface.go2
-rw-r--r--core/vm/memory_table.go16
-rw-r--r--core/vm/runtime/fuzz.go16
-rw-r--r--core/vm/stack_table.go16
-rw-r--r--crypto/signature_cgo.go2
-rw-r--r--crypto/signature_nocgo.go2
-rw-r--r--crypto/signature_test.go2
-rw-r--r--eth/api.go2
-rw-r--r--eth/api_backend.go2
-rw-r--r--eth/backend.go96
-rw-r--r--eth/config.go117
-rw-r--r--eth/downloader/modes.go33
-rw-r--r--eth/gasprice/gasprice.go4
-rw-r--r--eth/gen_config.go180
-rw-r--r--eth/handler.go12
-rw-r--r--eth/handler_test.go18
-rw-r--r--eth/helper_test.go11
-rw-r--r--eth/protocol.go5
-rw-r--r--eth/protocol_test.go11
-rw-r--r--eth/sync_test.go5
-rw-r--r--ethstats/ethstats.go13
-rw-r--r--event/example_feed_test.go2
-rw-r--r--event/example_scope_test.go2
-rw-r--r--event/example_subscription_test.go2
-rw-r--r--event/subscription.go2
-rw-r--r--event/subscription_test.go2
-rw-r--r--internal/ethapi/backend.go2
-rw-r--r--internal/ethapi/tracer.go84
-rw-r--r--internal/guide/guide.go2
-rw-r--r--internal/web3ext/web3ext.go17
-rw-r--r--les/api_backend.go2
-rw-r--r--les/backend.go17
-rw-r--r--les/distributor.go2
-rw-r--r--les/distributor_test.go2
-rw-r--r--les/odr_test.go4
-rw-r--r--les/peer.go5
-rw-r--r--light/lightchain.go3
-rw-r--r--light/odr_test.go4
-rw-r--r--miner/worker.go12
-rw-r--r--mobile/geth.go46
-rw-r--r--mobile/p2p.go2
-rw-r--r--mobile/primitives.go2
-rw-r--r--node/api.go22
-rw-r--r--node/config.go91
-rw-r--r--node/config_test.go3
-rw-r--r--node/defaults.go26
-rw-r--r--node/node.go33
-rw-r--r--node/node_example_test.go20
-rw-r--r--node/node_test.go4
-rw-r--r--p2p/dial.go28
-rw-r--r--p2p/dial_test.go100
-rw-r--r--p2p/discover/node.go14
-rw-r--r--p2p/discv5/node.go14
-rw-r--r--p2p/netutil/net.go25
-rw-r--r--p2p/server.go36
-rw-r--r--params/bootnodes.go6
-rw-r--r--params/version.go28
-rw-r--r--rpc/client_test.go4
-rw-r--r--rpc/http.go11
-rw-r--r--rpc/server.go4
-rw-r--r--rpc/websocket.go6
-rw-r--r--swarm/api/api.go219
-rw-r--r--swarm/api/client/client.go16
-rw-r--r--swarm/api/client/client_test.go2
-rw-r--r--swarm/api/filesystem.go5
-rw-r--r--swarm/api/fuse.go133
-rw-r--r--swarm/api/http/server_test.go28
-rw-r--r--swarm/api/http/templates.go16
-rw-r--r--swarm/api/manifest.go3
-rw-r--r--swarm/api/storage.go2
-rw-r--r--swarm/api/swarmfs_test.go115
-rw-r--r--swarm/api/uri.go2
-rw-r--r--swarm/api/uri_test.go2
-rw-r--r--swarm/fuse/fuse_dir.go155
-rw-r--r--swarm/fuse/fuse_file.go144
-rw-r--r--swarm/fuse/fuse_root.go (renamed from swarm/api/swarmfs.go)28
-rw-r--r--swarm/fuse/swarmfs.go64
-rw-r--r--swarm/fuse/swarmfs_fallback.go (renamed from swarm/api/swarmfs_fallback.go)7
-rw-r--r--swarm/fuse/swarmfs_test.go897
-rw-r--r--swarm/fuse/swarmfs_unix.go (renamed from swarm/api/swarmfs_unix.go)160
-rw-r--r--swarm/fuse/swarmfs_util.go144
-rw-r--r--swarm/swarm.go18
-rw-r--r--swarm/testutil/http.go16
-rw-r--r--trie/iterator.go138
-rw-r--r--trie/iterator_test.go97
-rw-r--r--vendor/github.com/naoina/go-stringutil/LICENSE19
-rw-r--r--vendor/github.com/naoina/go-stringutil/README.md13
-rw-r--r--vendor/github.com/naoina/go-stringutil/da.go253
-rw-r--r--vendor/github.com/naoina/go-stringutil/strings.go320
-rw-r--r--vendor/github.com/naoina/toml/LICENSE19
-rw-r--r--vendor/github.com/naoina/toml/README.md392
-rw-r--r--vendor/github.com/naoina/toml/ast/ast.go192
-rw-r--r--vendor/github.com/naoina/toml/config.go86
-rw-r--r--vendor/github.com/naoina/toml/decode.go478
-rw-r--r--vendor/github.com/naoina/toml/encode.go398
-rw-r--r--vendor/github.com/naoina/toml/error.go107
-rw-r--r--vendor/github.com/naoina/toml/parse.go376
-rw-r--r--vendor/github.com/naoina/toml/parse.peg145
-rw-r--r--vendor/github.com/naoina/toml/parse.peg.go2556
-rw-r--r--vendor/github.com/naoina/toml/util.go65
-rw-r--r--vendor/github.com/olekukonko/tablewriter/LICENCE.md19
-rw-r--r--vendor/github.com/olekukonko/tablewriter/README.md204
-rw-r--r--vendor/github.com/olekukonko/tablewriter/csv.go52
-rw-r--r--vendor/github.com/olekukonko/tablewriter/table.go662
-rw-r--r--vendor/github.com/olekukonko/tablewriter/test.csv4
-rw-r--r--vendor/github.com/olekukonko/tablewriter/test_info.csv4
-rw-r--r--vendor/github.com/olekukonko/tablewriter/util.go72
-rw-r--r--vendor/github.com/olekukonko/tablewriter/wrap.go103
-rw-r--r--vendor/golang.org/x/crypto/curve25519/const_amd64.h8
-rw-r--r--vendor/golang.org/x/crypto/curve25519/const_amd64.s20
-rw-r--r--vendor/golang.org/x/crypto/curve25519/cswap_amd64.s88
-rw-r--r--vendor/golang.org/x/crypto/curve25519/curve25519.go841
-rw-r--r--vendor/golang.org/x/crypto/curve25519/doc.go23
-rw-r--r--vendor/golang.org/x/crypto/curve25519/freeze_amd64.s73
-rw-r--r--vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s1377
-rw-r--r--vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go240
-rw-r--r--vendor/golang.org/x/crypto/curve25519/mul_amd64.s169
-rw-r--r--vendor/golang.org/x/crypto/curve25519/square_amd64.s132
-rw-r--r--vendor/golang.org/x/crypto/ed25519/ed25519.go181
-rw-r--r--vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go1422
-rw-r--r--vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go1771
-rw-r--r--vendor/golang.org/x/crypto/ssh/buffer.go98
-rw-r--r--vendor/golang.org/x/crypto/ssh/certs.go503
-rw-r--r--vendor/golang.org/x/crypto/ssh/channel.go633
-rw-r--r--vendor/golang.org/x/crypto/ssh/cipher.go627
-rw-r--r--vendor/golang.org/x/crypto/ssh/client.go211
-rw-r--r--vendor/golang.org/x/crypto/ssh/client_auth.go475
-rw-r--r--vendor/golang.org/x/crypto/ssh/common.go371
-rw-r--r--vendor/golang.org/x/crypto/ssh/connection.go143
-rw-r--r--vendor/golang.org/x/crypto/ssh/doc.go18
-rw-r--r--vendor/golang.org/x/crypto/ssh/handshake.go625
-rw-r--r--vendor/golang.org/x/crypto/ssh/kex.go540
-rw-r--r--vendor/golang.org/x/crypto/ssh/keys.go905
-rw-r--r--vendor/golang.org/x/crypto/ssh/mac.go61
-rw-r--r--vendor/golang.org/x/crypto/ssh/messages.go758
-rw-r--r--vendor/golang.org/x/crypto/ssh/mux.go330
-rw-r--r--vendor/golang.org/x/crypto/ssh/server.go491
-rw-r--r--vendor/golang.org/x/crypto/ssh/session.go627
-rw-r--r--vendor/golang.org/x/crypto/ssh/tcpip.go407
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/terminal.go951
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/util.go119
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go12
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/util_linux.go11
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go58
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go73
-rw-r--r--vendor/golang.org/x/crypto/ssh/terminal/util_windows.go155
-rw-r--r--vendor/golang.org/x/crypto/ssh/transport.go375
-rw-r--r--vendor/vendor.json48
-rw-r--r--whisper/mailserver/mailserver.go16
-rw-r--r--whisper/mailserver/server_test.go16
221 files changed, 30429 insertions, 1461 deletions
diff --git a/.mailmap b/.mailmap
index a36ddc1dc..d51c7b609 100644
--- a/.mailmap
+++ b/.mailmap
@@ -69,7 +69,7 @@ RJ Catalano <rj@erisindustries.com>
Nchinda Nchinda <nchinda2@gmail.com>
-Aron Fischer <homotopycolimit@users.noreply.github.com>
+Aron Fischer <github@aron.guru> <homotopycolimit@users.noreply.github.com>
Vlad Gluhovsky <gluk256@users.noreply.github.com>
@@ -90,3 +90,22 @@ Nick Johnson <arachnid@notdot.net>
Henning Diedrich <hd@eonblast.com>
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
+Felix Lange <fjl@twurst.com>
+Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com>
+
+Максим Чусовлянов <mchusovlianov@gmail.com>
+
+Louis Holbrook <dev@holbrook.no>
+Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
+
+Thomas Bocek <tom@tomp2p.net>
+
+Victor Tran <vu.tran54@gmail.com>
+
+Justin Drake <drakefjustin@gmail.com>
+
+Frank Wang <eternnoir@gmail.com>
+
+Gary Rong <garyrong0905@gmail.com>
+
+Guillaume Nicolas <guin56@gmail.com>
diff --git a/AUTHORS b/AUTHORS
index 50f3c713d..faa19d281 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,24 +3,30 @@
Ales Katona <ales@coinbase.com>
Alex Leverington <alex@ethdev.com>
Alexandre Van de Sande <alex.vandesande@ethdev.com>
-Aron Fischer <homotopycolimit@users.noreply.github.com>
+Aron Fischer <github@aron.guru>
Bas van Kervel <bas@ethdev.com>
Benjamin Brent <benjamin@benjaminbrent.com>
+Brian Schroeder <bts@gmail.com>
Casey Detrio <cdetrio@gmail.com>
Christoph Jentzsch <jentzsch.software@gmail.com>
Daniel A. Nagy <nagy.da@gmail.com>
+Diego Siqueira <DiSiqueira@users.noreply.github.com>
Elliot Shepherd <elliot@identitii.com>
Enrique Fynn <enriquefynn@gmail.com>
Ethan Buchman <ethan@coinculture.info>
Fabian Vogelsteller <fabian@frozeman.de>
Fabio Berger <fabioberger1991@gmail.com>
Felix Lange <fjl@twurst.com>
+Frank Wang <eternnoir@gmail.com>
+Gary Rong <garyrong0905@gmail.com>
Gregg Dourgarian <greggd@tempworks.com>
+Guillaume Nicolas <guin56@gmail.com>
Gustav Simonsson <gustav.simonsson@gmail.com>
Hao Bryan Cheng <haobcheng@gmail.com>
Henning Diedrich <hd@eonblast.com>
Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
Jae Kwon <jkwon.work@gmail.com>
+Jamie Pitts <james.pitts@gmail.com>
Jason Carver <jacarver@linkedin.com>
Jeff R. Allen <jra@nella.org>
Jeffrey Wilcke <jeffrey@ethereum.org>
@@ -28,15 +34,20 @@ Jens Agerberg <github@agerberg.me>
Jonathan Brown <jbrown@bluedroplet.com>
Joseph Chow <ethereum@outlook.com>
Justin Clark-Casey <justincc@justincc.org>
+Justin Drake <drakefjustin@gmail.com>
Kenji Siu <kenji@isuntv.com>
Kobi Gurkan <kobigurk@gmail.com>
Lefteris Karapetsas <lefteris@refu.co>
Leif Jurvetson <leijurv@gmail.com>
+Lewis Marshall <lewis@lmars.net>
+Louis Holbrook <dev@holbrook.no>
+Luca Zeug <luclu@users.noreply.github.com>
Maran Hidskes <maran.hidskes@gmail.com>
Marek Kotewicz <marek.kotewicz@gmail.com>
Martin Holst Swende <martin@swende.se>
Matthew Di Ferrante <mattdf@users.noreply.github.com>
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
+Micah Zoltu <micah@zoltu.net>
Nchinda Nchinda <nchinda2@gmail.com>
Nick Dodson <silentcicero@outlook.com>
Nick Johnson <arachnid@notdot.net>
@@ -47,17 +58,28 @@ RJ Catalano <rj@erisindustries.com>
Ramesh Nair <ram@hiddentao.com>
Ricardo Catalinas Jiménez <r@untroubled.be>
Rémy Roy <remyroy@remyroy.com>
+Shintaro Kaneko <kaneshin0120@gmail.com>
Stein Dekker <dekker.stein@gmail.com>
Steven Roose <stevenroose@gmail.com>
Taylor Gerring <taylor.gerring@gmail.com>
Thomas Bocek <tom@tomp2p.net>
Tosh Camille <tochecamille@gmail.com>
+Valentin Wüstholz <wuestholz@users.noreply.github.com>
+Victor Farazdagi <simple.square@gmail.com>
+Victor Tran <vu.tran54@gmail.com>
Viktor Trón <viktor.tron@gmail.com>
Ville Sundell <github@solarius.fi>
Vincent G <caktux@gmail.com>
Vitalik Buterin <v@buterin.com>
+Vivek Anand <vivekanand1101@users.noreply.github.com>
Vlad Gluhovsky <gluk256@users.noreply.github.com>
Yohann Léon <sybiload@gmail.com>
Yoichi Hirai <i@yoichihirai.com>
+Zahoor Mohamed <zahoor@zahoor.in>
Zsolt Felföldi <zsfelfoldi@gmail.com>
+holisticode <holistic.computing@gmail.com>
+ken10100147 <sunhongping@kanjian.com>
+ligi <ligi@ligi.de>
+xiekeyang <xiekeyang@users.noreply.github.com>
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
+Максим Чусовлянов <mchusovlianov@gmail.com>
diff --git a/VERSION b/VERSION
index dc1e644a1..9c6d6293b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.6.0
+1.6.1
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 43f563159..159fca136 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -248,7 +248,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
// Execute the call.
msg := callmsg{call}
- evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
+ evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go
index fb4086c3a..dc6ac6ccb 100644
--- a/accounts/keystore/account_cache.go
+++ b/accounts/keystore/account_cache.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
index 554196321..ab8aa9e6c 100644
--- a/accounts/keystore/account_cache_test.go
+++ b/accounts/keystore/account_cache_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go
index a01ff17e3..a81098227 100644
--- a/accounts/keystore/keystore.go
+++ b/accounts/keystore/keystore.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go
index 60f2606ee..5d89a4dbb 100644
--- a/accounts/keystore/keystore_test.go
+++ b/accounts/keystore/keystore_test.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/build/ci-notes.md b/build/ci-notes.md
index cd2ba8bb2..7574bfffa 100644
--- a/build/ci-notes.md
+++ b/build/ci-notes.md
@@ -5,9 +5,9 @@ for Ubuntu. Packages are built for the all Ubuntu versions which are supported b
Canonical:
- Trusty Tahr (14.04 LTS)
-- Wily Werewolf (15.10)
- Xenial Xerus (16.04 LTS)
- Yakkety Yak (16.10)
+- Zesty Zapus (17.04)
Packages of develop branch commits have suffix -unstable and cannot be installed alongside
the stable version. Switching between release streams requires user intervention.
@@ -21,18 +21,18 @@ variable which Travis CI makes available to certain builds.
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
version that is available in the main Ubuntu repository. In order to make this possible,
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
-golang-1.7, which is co-installable alongside the regular golang package. PPA dependencies
+golang-1.8, which is co-installable alongside the regular golang package. PPA dependencies
can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies
## Building Packages Locally (for testing)
You need to run Ubuntu to do test packaging.
-Add the gophers PPA and install Go 1.7 and Debian packaging tools:
+Add the gophers PPA and install Go 1.8 and Debian packaging tools:
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
$ sudo apt-get update
- $ sudo apt-get install build-essential golang-1.7 devscripts debhelper
+ $ sudo apt-get install build-essential golang-1.8 devscripts debhelper
Create the source packages:
@@ -40,10 +40,10 @@ Create the source packages:
Then go into the source package directory for your running distribution and build the package:
- $ cd dist/ethereum-unstable-1.5.0+xenial
+ $ cd dist/ethereum-unstable-1.6.0+xenial
$ dpkg-buildpackage
Built packages are placed in the dist/ directory.
$ cd ..
- $ dpkg-deb -c geth-unstable_1.5.0+xenial_amd64.deb
+ $ dpkg-deb -c geth-unstable_1.6.0+xenial_amd64.deb
diff --git a/build/ci.go b/build/ci.go
index cb89e914b..8b6ebe74c 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -74,42 +74,47 @@ var (
executablePath("bootnode"),
executablePath("evm"),
executablePath("geth"),
- executablePath("swarm"),
+ executablePath("puppeth"),
executablePath("rlpdump"),
+ executablePath("swarm"),
}
// A debian package is created for all executables listed here.
debExecutables = []debExecutable{
{
- Name: "geth",
- Description: "Ethereum CLI client.",
+ Name: "abigen",
+ Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
},
{
Name: "bootnode",
Description: "Ethereum bootnode.",
},
{
- Name: "rlpdump",
- Description: "Developer utility tool that prints RLP structures.",
- },
- {
Name: "evm",
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
},
{
- Name: "swarm",
- Description: "Ethereum Swarm daemon and tools",
+ Name: "geth",
+ Description: "Ethereum CLI client.",
},
{
- Name: "abigen",
- Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
+ Name: "puppeth",
+ Description: "Ethereum private network manager.",
+ },
+ {
+ Name: "rlpdump",
+ Description: "Developer utility tool that prints RLP structures.",
+ },
+ {
+ Name: "swarm",
+ Description: "Ethereum Swarm daemon and tools",
},
}
// Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it.
// Note: wily is unsupported because it was officially deprecated on lanchpad.
- debDistros = []string{"trusty", "xenial", "yakkety"}
+ debDistros = []string{"trusty", "xenial", "yakkety", "zesty"}
)
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@@ -287,7 +292,8 @@ func doTest(cmdline []string) {
// Run analysis tools before the tests.
build.MustRun(goTool("vet", packages...))
if *misspell {
- spellcheck(packages)
+ // TODO(karalabe): Reenable after false detection is fixed: https://github.com/client9/misspell/issues/105
+ // spellcheck(packages)
}
// Run the actual tests.
gotest := goTool("test", buildFlags(env)...)
diff --git a/build/deb.control b/build/deb.control
index d39393d29..7394754ef 100644
--- a/build/deb.control
+++ b/build/deb.control
@@ -2,7 +2,7 @@ Source: {{.Name}}
Section: science
Priority: extra
Maintainer: {{.Author}}
-Build-Depends: debhelper (>= 8.0.0), golang-1.7
+Build-Depends: debhelper (>= 8.0.0), golang-1.8
Standards-Version: 3.9.5
Homepage: https://ethereum.org
Vcs-Git: git://github.com/ethereum/go-ethereum.git
@@ -13,7 +13,7 @@ Architecture: any
Depends: ${misc:Depends}, {{.ExeList}}
Description: Meta-package to install geth and other tools
Meta-package to install geth and other tools
-
+
{{range .Executables}}
Package: {{$.ExeName .}}
Conflicts: {{$.ExeConflicts .}}
diff --git a/build/deb.rules b/build/deb.rules
index 11efe15fc..b3fe5267f 100644
--- a/build/deb.rules
+++ b/build/deb.rules
@@ -5,7 +5,7 @@
#export DH_VERBOSE=1
override_dh_auto_build:
- build/env.sh /usr/lib/go-1.7/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
+ build/env.sh /usr/lib/go-1.8/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
override_dh_auto_test:
diff --git a/build/update-license.go b/build/update-license.go
index ee5281410..81b1a507d 100644
--- a/build/update-license.go
+++ b/build/update-license.go
@@ -49,11 +49,13 @@ var (
// don't relicense vendored sources
"crypto/sha3/", "crypto/ecies/", "log/",
"crypto/secp256k1/curve.go",
+ "consensus/ethash/xor.go",
+ "internal/jsre/deps",
+ "cmd/internal/browser",
// don't license generated files
"contracts/chequebook/contract/",
"contracts/ens/contract/",
"contracts/release/contract.go",
- "p2p/discv5/nodeevent_string.go",
}
// paths with this prefix are licensed as GPL. all other files are LGPL.
@@ -284,6 +286,9 @@ func getInfo(files <-chan string, out chan<- *info, wg *sync.WaitGroup) {
if !stat.Mode().IsRegular() {
continue
}
+ if isGenerated(file) {
+ continue
+ }
info, err := fileInfo(file)
if err != nil {
fmt.Printf("ERROR %s: %v\n", file, err)
@@ -294,6 +299,23 @@ func getInfo(files <-chan string, out chan<- *info, wg *sync.WaitGroup) {
wg.Done()
}
+func isGenerated(file string) bool {
+ fd, err := os.Open(file)
+ if err != nil {
+ return false
+ }
+ defer fd.Close()
+ buf := make([]byte, 2048)
+ n, _ := fd.Read(buf)
+ buf = buf[:n]
+ for _, l := range bytes.Split(buf, []byte("\n")) {
+ if bytes.HasPrefix(l, []byte("// Code generated")) {
+ return true
+ }
+ }
+ return false
+}
+
// fileInfo finds the lowest year in which the given file was committed.
func fileInfo(file string) (*info, error) {
info := &info{file: file, Year: int64(time.Now().Year())}
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
new file mode 100644
index 000000000..fd34cdec1
--- /dev/null
+++ b/cmd/faucet/faucet.go
@@ -0,0 +1,455 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+// faucet is a Ether faucet backed by a light client.
+package main
+
+//go:generate go-bindata -nometadata -o website.go faucet.html
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "html/template"
+ "io/ioutil"
+ "math/big"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/ethclient"
+ "github.com/ethereum/go-ethereum/ethstats"
+ "github.com/ethereum/go-ethereum/les"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/discover"
+ "github.com/ethereum/go-ethereum/p2p/discv5"
+ "github.com/ethereum/go-ethereum/p2p/nat"
+ "github.com/ethereum/go-ethereum/params"
+ "golang.org/x/net/websocket"
+)
+
+var (
+ genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with")
+ apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection")
+ ethPortFlag = flag.Int("ethport", 30303, "Listener port for the devp2p connection")
+ bootFlag = flag.String("bootnodes", "", "Comma separated bootnode enode URLs to seed with")
+ netFlag = flag.Int("network", 0, "Network ID to use for the Ethereum protocol")
+ statsFlag = flag.String("ethstats", "", "Ethstats network monitoring auth string")
+
+ netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet")
+ payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request")
+ minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds")
+
+ accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with")
+ accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds")
+
+ githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access")
+ githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with")
+
+ logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
+)
+
+var (
+ ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
+)
+
+func main() {
+ // Parse the flags and set up the logger to print everything requested
+ flag.Parse()
+ log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+
+ // Load up and render the faucet website
+ tmpl, err := Asset("faucet.html")
+ if err != nil {
+ log.Crit("Failed to load the faucet template", "err", err)
+ }
+ period := fmt.Sprintf("%d minute(s)", *minutesFlag)
+ if *minutesFlag%60 == 0 {
+ period = fmt.Sprintf("%d hour(s)", *minutesFlag/60)
+ }
+ website := new(bytes.Buffer)
+ template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
+ "Network": *netnameFlag,
+ "Amount": *payoutFlag,
+ "Period": period,
+ })
+ // Load and parse the genesis block requested by the user
+ blob, err := ioutil.ReadFile(*genesisFlag)
+ if err != nil {
+ log.Crit("Failed to read genesis block contents", "genesis", *genesisFlag, "err", err)
+ }
+ genesis := new(core.Genesis)
+ if err = json.Unmarshal(blob, genesis); err != nil {
+ log.Crit("Failed to parse genesis block json", "err", err)
+ }
+ // Convert the bootnodes to internal enode representations
+ var enodes []*discv5.Node
+ for _, boot := range strings.Split(*bootFlag, ",") {
+ if url, err := discv5.ParseNode(boot); err == nil {
+ enodes = append(enodes, url)
+ } else {
+ log.Error("Failed to parse bootnode URL", "url", boot, "err", err)
+ }
+ }
+ // Load up the account key and decrypt its password
+ if blob, err = ioutil.ReadFile(*accPassFlag); err != nil {
+ log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err)
+ }
+ pass := string(blob)
+
+ ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP)
+ if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil {
+ log.Crit("Failed to read account key contents", "file", *accJSONFlag, "err", err)
+ }
+ acc, err := ks.Import(blob, pass, pass)
+ if err != nil {
+ log.Crit("Failed to import faucet signer account", "err", err)
+ }
+ ks.Unlock(acc, pass)
+
+ // Assemble and start the faucet light service
+ faucet, err := newFaucet(genesis, *ethPortFlag, enodes, *netFlag, *statsFlag, ks, website.Bytes())
+ if err != nil {
+ log.Crit("Failed to start faucet", "err", err)
+ }
+ defer faucet.close()
+
+ if err := faucet.listenAndServe(*apiPortFlag); err != nil {
+ log.Crit("Failed to launch faucet API", "err", err)
+ }
+}
+
+// request represents an accepted funding request.
+type request struct {
+ Username string `json:"username"` // GitHub user for displaying an avatar
+ Account common.Address `json:"account"` // Ethereum address being funded
+ Time time.Time `json:"time"` // Timestamp when te request was accepted
+ Tx *types.Transaction `json:"tx"` // Transaction funding the account
+}
+
+// faucet represents a crypto faucet backed by an Ethereum light client.
+type faucet struct {
+ config *params.ChainConfig // Chain configurations for signing
+ stack *node.Node // Ethereum protocol stack
+ client *ethclient.Client // Client connection to the Ethereum chain
+ index []byte // Index page to serve up on the web
+
+ keystore *keystore.KeyStore // Keystore containing the single signer
+ account accounts.Account // Account funding user faucet requests
+ nonce uint64 // Current pending nonce of the faucet
+ price *big.Int // Current gas price to issue funds with
+
+ conns []*websocket.Conn // Currently live websocket connections
+ history map[string]time.Time // History of users and their funding requests
+ reqs []*request // Currently pending funding requests
+ update chan struct{} // Channel to signal request updates
+
+ lock sync.RWMutex // Lock protecting the faucet's internals
+}
+
+func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network int, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
+ // Assemble the raw devp2p protocol stack
+ stack, err := node.New(&node.Config{
+ Name: "geth",
+ Version: params.Version,
+ DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"),
+ P2P: p2p.Config{
+ NAT: nat.Any(),
+ NoDiscovery: true,
+ DiscoveryV5: true,
+ ListenAddr: fmt.Sprintf(":%d", port),
+ DiscoveryV5Addr: fmt.Sprintf(":%d", port+1),
+ MaxPeers: 25,
+ BootstrapNodesV5: enodes,
+ },
+ })
+ if err != nil {
+ return nil, err
+ }
+ // Assemble the Ethereum light client protocol
+ if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
+ cfg := eth.DefaultConfig
+ cfg.SyncMode = downloader.LightSync
+ cfg.NetworkId = network
+ cfg.Genesis = genesis
+ return les.New(ctx, &cfg)
+ }); err != nil {
+ return nil, err
+ }
+ // Assemble the ethstats monitoring and reporting service'
+ if stats != "" {
+ if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
+ var serv *les.LightEthereum
+ ctx.Service(&serv)
+ return ethstats.New(stats, nil, serv)
+ }); err != nil {
+ return nil, err
+ }
+ }
+ // Boot up the client and ensure it connects to bootnodes
+ if err := stack.Start(); err != nil {
+ return nil, err
+ }
+ for _, boot := range enodes {
+ old, _ := discover.ParseNode(boot.String())
+ stack.Server().AddPeer(old)
+ }
+ // Attach to the client and retrieve and interesting metadatas
+ api, err := stack.Attach()
+ if err != nil {
+ stack.Stop()
+ return nil, err
+ }
+ client := ethclient.NewClient(api)
+
+ return &faucet{
+ config: genesis.Config,
+ stack: stack,
+ client: client,
+ index: index,
+ keystore: ks,
+ account: ks.Accounts()[0],
+ history: make(map[string]time.Time),
+ update: make(chan struct{}, 1),
+ }, nil
+}
+
+// close terminates the Ethereum connection and tears down the faucet.
+func (f *faucet) close() error {
+ return f.stack.Stop()
+}
+
+// listenAndServe registers the HTTP handlers for the faucet and boots it up
+// for service user funding requests.
+func (f *faucet) listenAndServe(port int) error {
+ go f.loop()
+
+ http.HandleFunc("/", f.webHandler)
+ http.Handle("/api", websocket.Handler(f.apiHandler))
+
+ return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
+}
+
+// webHandler handles all non-api requests, simply flattening and returning the
+// faucet website.
+func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) {
+ w.Write(f.index)
+}
+
+// apiHandler handles requests for Ether grants and transaction statuses.
+func (f *faucet) apiHandler(conn *websocket.Conn) {
+ // Start tracking the connection and drop at the end
+ f.lock.Lock()
+ f.conns = append(f.conns, conn)
+ f.lock.Unlock()
+
+ defer func() {
+ f.lock.Lock()
+ for i, c := range f.conns {
+ if c == conn {
+ f.conns = append(f.conns[:i], f.conns[i+1:]...)
+ break
+ }
+ }
+ f.lock.Unlock()
+ }()
+ // Send a few initial stats to the client
+ balance, _ := f.client.BalanceAt(context.Background(), f.account.Address, nil)
+ nonce, _ := f.client.NonceAt(context.Background(), f.account.Address, nil)
+
+ websocket.JSON.Send(conn, map[string]interface{}{
+ "funds": balance.Div(balance, ether),
+ "funded": nonce,
+ "peers": f.stack.Server().PeerCount(),
+ "requests": f.reqs,
+ })
+ header, _ := f.client.HeaderByNumber(context.Background(), nil)
+ websocket.JSON.Send(conn, header)
+
+ // Keep reading requests from the websocket until the connection breaks
+ for {
+ // Fetch the next funding request and validate against github
+ var msg struct {
+ URL string `json:"url"`
+ }
+ if err := websocket.JSON.Receive(conn, &msg); err != nil {
+ return
+ }
+ if !strings.HasPrefix(msg.URL, "https://gist.github.com/") {
+ websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"})
+ continue
+ }
+ log.Info("Faucet funds requested", "gist", msg.URL)
+
+ // Retrieve the gist from the GitHub Gist APIs
+ parts := strings.Split(msg.URL, "/")
+ req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
+ if *githubUser != "" {
+ req.SetBasicAuth(*githubUser, *githubToken)
+ }
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ continue
+ }
+ var gist struct {
+ Owner struct {
+ Login string `json:"login"`
+ } `json:"owner"`
+ Files map[string]struct {
+ Content string `json:"content"`
+ } `json:"files"`
+ }
+ err = json.NewDecoder(res.Body).Decode(&gist)
+ res.Body.Close()
+ if err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ continue
+ }
+ if gist.Owner.Login == "" {
+ websocket.JSON.Send(conn, map[string]string{"error": "Nice try ;)"})
+ continue
+ }
+ // Iterate over all the files and look for Ethereum addresses
+ var address common.Address
+ for _, file := range gist.Files {
+ if len(file.Content) == 2+common.AddressLength*2 {
+ address = common.HexToAddress(file.Content)
+ }
+ }
+ if address == (common.Address{}) {
+ websocket.JSON.Send(conn, map[string]string{"error": "No Ethereum address found to fund"})
+ continue
+ }
+ // Ensure the user didn't request funds too recently
+ f.lock.Lock()
+ var (
+ fund bool
+ elapsed time.Duration
+ )
+ if elapsed = time.Since(f.history[gist.Owner.Login]); elapsed > time.Duration(*minutesFlag)*time.Minute {
+ // User wasn't funded recently, create the funding transaction
+ tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(21000), f.price, nil)
+ signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId)
+ if err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ f.lock.Unlock()
+ continue
+ }
+ // Submit the transaction and mark as funded if successful
+ if err := f.client.SendTransaction(context.Background(), signed); err != nil {
+ websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
+ f.lock.Unlock()
+ continue
+ }
+ f.reqs = append(f.reqs, &request{
+ Username: gist.Owner.Login,
+ Account: address,
+ Time: time.Now(),
+ Tx: signed,
+ })
+ f.history[gist.Owner.Login] = time.Now()
+ fund = true
+ }
+ f.lock.Unlock()
+
+ // Send an error if too frequent funding, othewise a success
+ if !fund {
+ websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("User already funded %s ago", common.PrettyDuration(elapsed))})
+ continue
+ }
+ websocket.JSON.Send(conn, map[string]string{"success": fmt.Sprintf("Funding request accepted for %s into %s", gist.Owner.Login, address.Hex())})
+ select {
+ case f.update <- struct{}{}:
+ default:
+ }
+ }
+}
+
+// loop keeps waiting for interesting events and pushes them out to connected
+// websockets.
+func (f *faucet) loop() {
+ // Wait for chain events and push them to clients
+ heads := make(chan *types.Header, 16)
+ sub, err := f.client.SubscribeNewHead(context.Background(), heads)
+ if err != nil {
+ log.Crit("Failed to subscribe to head events", "err", err)
+ }
+ defer sub.Unsubscribe()
+
+ for {
+ select {
+ case head := <-heads:
+ // New chain head arrived, query the current stats and stream to clients
+ balance, _ := f.client.BalanceAt(context.Background(), f.account.Address, nil)
+ balance = new(big.Int).Div(balance, ether)
+
+ price, _ := f.client.SuggestGasPrice(context.Background())
+ nonce, _ := f.client.NonceAt(context.Background(), f.account.Address, nil)
+
+ f.lock.Lock()
+ f.price, f.nonce = price, nonce
+ for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
+ f.reqs = f.reqs[1:]
+ }
+ f.lock.Unlock()
+
+ f.lock.RLock()
+ for _, conn := range f.conns {
+ if err := websocket.JSON.Send(conn, map[string]interface{}{
+ "funds": balance,
+ "funded": f.nonce,
+ "peers": f.stack.Server().PeerCount(),
+ "requests": f.reqs,
+ }); err != nil {
+ log.Warn("Failed to send stats to client", "err", err)
+ conn.Close()
+ continue
+ }
+ if err := websocket.JSON.Send(conn, head); err != nil {
+ log.Warn("Failed to send header to client", "err", err)
+ conn.Close()
+ }
+ }
+ f.lock.RUnlock()
+
+ case <-f.update:
+ // Pending requests updated, stream to clients
+ f.lock.RLock()
+ for _, conn := range f.conns {
+ if err := websocket.JSON.Send(conn, map[string]interface{}{"requests": f.reqs}); err != nil {
+ log.Warn("Failed to send requests to client", "err", err)
+ conn.Close()
+ }
+ }
+ f.lock.RUnlock()
+ }
+ }
+}
diff --git a/cmd/faucet/faucet.html b/cmd/faucet/faucet.html
new file mode 100644
index 000000000..570145ea2
--- /dev/null
+++ b/cmd/faucet/faucet.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <title>{{.Network}}: GitHub Faucet</title>
+
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
+
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-noty/2.4.1/packaged/jquery.noty.packaged.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.0/moment.min.js"></script>
+
+ <style>
+ .vertical-center {
+ min-height: 100%;
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ }
+ .progress {
+ position: relative;
+ }
+ .progress span {
+ position: absolute;
+ display: block;
+ width: 100%;
+ color: white;
+ }
+ pre {
+ padding: 6px;
+ margin: 0;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div class="vertical-center">
+ <div class="container">
+ <div class="row" style="margin-bottom: 16px;">
+ <div class="col-lg-12">
+ <h1 style="text-align: center;"><i class="fa fa-bath" aria-hidden="true"></i> {{.Network}} GitHub Authenticated Faucet <i class="fa fa-github-alt" aria-hidden="true"></i></h1>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-lg-8 col-lg-offset-2">
+ <div class="input-group">
+ <input id="gist" type="text" class="form-control" placeholder="GitHub Gist URL containing your Ethereum address...">
+ <span class="input-group-btn">
+ <button class="btn btn-default" type="button" onclick="submit()">Give me Ether!</button>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class="row" style="margin-top: 32px;">
+ <div class="col-lg-6 col-lg-offset-3">
+ <div class="panel panel-small panel-default">
+ <div class="panel-body" style="padding: 0; overflow: auto; max-height: 300px;">
+ <table id="requests" class="table table-condensed" style="margin: 0;"></table>
+ </div>
+ <div class="panel-footer">
+ <table style="width: 100%"><tr>
+ <td style="text-align: center;"><i class="fa fa-rss" aria-hidden="true"></i> <span id="peers"></span> peers</td>
+ <td style="text-align: center;"><i class="fa fa-database" aria-hidden="true"></i> <span id="block"></span> blocks</td>
+ <td style="text-align: center;"><i class="fa fa-heartbeat" aria-hidden="true"></i> <span id="funds"></span> Ethers</td>
+ <td style="text-align: center;"><i class="fa fa-university" aria-hidden="true"></i> <span id="funded"></span> funded</td>
+ </tr></table>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" style="margin-top: 32px;">
+ <div class="col-lg-12">
+ <h3>How does this work?</h3>
+ <p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to GitHub accounts. Anyone having a GitHub account may request funds within the permitted limit of <strong>{{.Amount}} Ether(s) / {{.Period}}</strong>.</p>
+ <p>To request funds, simply create a <a href="https://gist.github.com/" target="_about:blank">GitHub Gist</a> with your Ethereum address pasted into the contents (the file name doesn't matter), copy paste the gists URL into the above input box and fire away! You can track the current pending requests below the input field to see how much you have to wait until your turn comes.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ // Global variables to hold the current status of the faucet
+ var attempt = 0;
+ var server;
+
+ // Define the function that submits a gist url to the server
+ var submit = function() {
+ server.send(JSON.stringify({url: $("#gist")[0].value}));
+ };
+ // Define a method to reconnect upon server loss
+ var reconnect = function() {
+ if (attempt % 2 == 0) {
+ server = new WebSocket("wss://" + location.host + "/api");
+ } else {
+ server = new WebSocket("ws://" + location.host + "/api");
+ }
+ attempt++;
+
+ server.onmessage = function(event) {
+ var msg = JSON.parse(event.data);
+ if (msg === null) {
+ return;
+ }
+
+ if (msg.funds !== undefined) {
+ $("#funds").text(msg.funds);
+ }
+ if (msg.funded !== undefined) {
+ $("#funded").text(msg.funded);
+ }
+ if (msg.peers !== undefined) {
+ $("#peers").text(msg.peers);
+ }
+ if (msg.number !== undefined) {
+ $("#block").text(parseInt(msg.number, 16));
+ }
+ if (msg.error !== undefined) {
+ noty({layout: 'topCenter', text: msg.error, type: 'error'});
+ }
+ if (msg.success !== undefined) {
+ noty({layout: 'topCenter', text: msg.success, type: 'success'});
+ }
+ if (msg.requests !== undefined && msg.requests !== null) {
+ var content = "";
+ for (var i=0; i<msg.requests.length; i++) {
+ content += "<tr><td><div style=\"background: url('https://github.com/" + msg.requests[i].username + ".png?size=64'); background-size: cover; width:32px; height: 32px; border-radius: 4px;\"></div></td><td><pre>" + msg.requests[i].account + "</pre></td><td style=\"width: 100%; text-align: center; vertical-align: middle;\">" + moment.duration(moment(msg.requests[i].time).unix()-moment().unix(), 'seconds').humanize(true) + "</td></tr>";
+ }
+ $("#requests").html("<tbody>" + content + "</tbody>");
+ }
+ }
+ server.onclose = function() { setTimeout(reconnect, 3000); };
+ server.onerror = function() { setTimeout(reconnect, 3000); };
+ }
+ // Establish a websocket connection to the API server
+ reconnect();
+ </script>
+ </body>
+</html>
diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go
new file mode 100644
index 000000000..32650fec4
--- /dev/null
+++ b/cmd/faucet/website.go
@@ -0,0 +1,235 @@
+// Code generated by go-bindata.
+// sources:
+// faucet.html
+// DO NOT EDIT!
+
+package main
+
+import (
+ "bytes"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+func bindataRead(data []byte, name string) ([]byte, error) {
+ gz, err := gzip.NewReader(bytes.NewBuffer(data))
+ if err != nil {
+ return nil, fmt.Errorf("Read %q: %v", name, err)
+ }
+
+ var buf bytes.Buffer
+ _, err = io.Copy(&buf, gz)
+ clErr := gz.Close()
+
+ if err != nil {
+ return nil, fmt.Errorf("Read %q: %v", name, err)
+ }
+ if clErr != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+type asset struct {
+ bytes []byte
+ info os.FileInfo
+}
+
+type bindataFileInfo struct {
+ name string
+ size int64
+ mode os.FileMode
+ modTime time.Time
+}
+
+func (fi bindataFileInfo) Name() string {
+ return fi.name
+}
+func (fi bindataFileInfo) Size() int64 {
+ return fi.size
+}
+func (fi bindataFileInfo) Mode() os.FileMode {
+ return fi.mode
+}
+func (fi bindataFileInfo) ModTime() time.Time {
+ return fi.modTime
+}
+func (fi bindataFileInfo) IsDir() bool {
+ return false
+}
+func (fi bindataFileInfo) Sys() interface{} {
+ return nil
+}
+
+var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\xe1\x72\xdb\x36\x12\xfe\x2d\x3f\xc5\x86\x77\xad\xa5\xb1\x49\xca\x71\x26\xed\xc8\xa4\x3a\x99\x36\x97\xf6\xe6\xa6\xed\x5c\xd3\xb9\xeb\xb4\x9d\x1b\x90\x5c\x8a\xb0\x41\x80\x05\x16\x92\xd5\x8c\xde\xfd\x06\x00\x45\x51\x8a\x9d\xa4\x97\xde\x1f\x5b\x00\x16\xdf\x7e\xd8\x5d\xec\x2e\x98\x3d\xf9\xea\xbb\x2f\x5f\xff\xf4\xfd\x4b\x68\xa8\x15\xcb\xb3\xcc\xfd\x03\xc1\xe4\x2a\x8f\x50\x46\xcb\xb3\x49\xd6\x20\xab\x96\x67\x93\x49\xd6\x22\x31\x28\x1b\xa6\x0d\x52\x1e\x59\xaa\xe3\xcf\xa3\xc3\x42\x43\xd4\xc5\xf8\x9b\xe5\xeb\x3c\xfa\x77\xfc\xe3\x8b\xf8\x4b\xd5\x76\x8c\x78\x21\x30\x82\x52\x49\x42\x49\x79\xf4\xcd\xcb\x1c\xab\x15\x8e\xf6\x49\xd6\x62\x1e\xad\x39\x6e\x3a\xa5\x69\x24\xba\xe1\x15\x35\x79\x85\x6b\x5e\x62\xec\x07\x97\xc0\x25\x27\xce\x44\x6c\x4a\x26\x30\xbf\x8a\x96\x67\x0e\x87\x38\x09\x5c\xbe\x79\x93\x7c\x8b\xb4\x51\xfa\x6e\xb7\x5b\xc0\x2b\x4e\x5f\xdb\x02\xfe\xc6\x6c\x89\x94\xa5\x41\xc4\x4b\x0b\x2e\xef\xa0\xd1\x58\xe7\x91\xe3\x6c\x16\x69\x5a\x56\xf2\xd6\x24\xa5\x50\xb6\xaa\x05\xd3\x98\x94\xaa\x4d\xd9\x2d\xbb\x4f\x05\x2f\x4c\x4a\x1b\x4e\x84\x3a\x2e\x94\x22\x43\x9a\x75\xe9\x75\x72\x9d\x7c\x96\x96\xc6\xa4\xc3\x5c\xd2\x72\x99\x94\xc6\x44\xa0\x51\xe4\x91\xa1\xad\x40\xd3\x20\x52\x04\xe9\xf2\x7f\xd3\x5b\x2b\x49\x31\xdb\xa0\x51\x2d\xa6\xcf\x92\xcf\x92\xb9\x57\x39\x9e\x7e\xb7\x56\xa7\xd6\x94\x9a\x77\x04\x46\x97\x1f\xac\xf7\xf6\x37\x8b\x7a\x9b\x5e\x27\x57\xc9\x55\x3f\xf0\x7a\x6e\x4d\xb4\xcc\xd2\x00\xb8\xfc\x28\xec\x58\x2a\xda\xa6\x4f\x93\x67\xc9\x55\xda\xb1\xf2\x8e\xad\xb0\xda\x6b\x72\x4b\xc9\x7e\xf2\x4f\xd3\xfb\x98\x0f\x6f\x4f\x5d\xf8\x67\x28\x6b\x55\x8b\x92\x92\x5b\x93\x3e\x4d\xae\x3e\x4f\xe6\xfb\x89\xb7\xf1\xbd\x02\xe7\x34\xa7\x6a\x92\xac\x51\x13\x2f\x99\x88\x4b\x94\x84\x1a\xde\xb8\xd9\x49\xcb\x65\xdc\x20\x5f\x35\xb4\x80\xab\xf9\xfc\x93\x9b\x87\x66\xd7\x4d\x98\xae\xb8\xe9\x04\xdb\x2e\xa0\x16\x78\x1f\xa6\x98\xe0\x2b\x19\x73\xc2\xd6\x2c\x20\x20\xfb\x85\x9d\xd7\xd9\x69\xb5\xd2\x68\x4c\xaf\xac\x53\x86\x13\x57\x72\xe1\x22\x8a\x11\x5f\xe3\x43\xb2\xa6\x63\xf2\xad\x0d\xac\x30\x4a\x58\xc2\x13\x22\x85\x50\xe5\x5d\x98\xf3\xd7\x78\x7c\x88\x52\x09\xa5\x17\xb0\x69\x78\xbf\x0d\xbc\x22\xe8\x34\xf6\xf0\xd0\xb1\xaa\xe2\x72\xb5\x80\xe7\x5d\x7f\x1e\x68\x99\x5e\x71\xb9\x80\xf9\x61\x4b\x96\xee\xcd\x98\xa5\x21\x63\x9d\x4d\xb2\x42\x55\x5b\xef\xc3\x8a\xaf\xa1\x14\xcc\x98\x3c\x3a\x31\xb1\xcf\x44\x47\x02\x2e\x01\x31\x2e\xf7\x4b\x47\x6b\x5a\x6d\x22\xf0\x8a\xf2\x28\x90\x88\x0b\x45\xa4\xda\x05\x5c\x39\x7a\xfd\x96\x13\x3c\x11\x8b\x55\x7c\xf5\x74\xbf\x38\xc9\x9a\xab\x3d\x08\xe1\x3d\xc5\xde\x3f\x83\x67\xa2\x65\xc6\xf7\x7b\x6b\x06\x35\x8b\x0b\x46\x4d\x04\x4c\x73\x16\x37\xbc\xaa\x50\xe6\x11\x69\x8b\x2e\x8e\xf8\x12\xc6\x79\x6f\x9f\xf6\x5e\x58\x6a\x50\xba\x73\x12\x56\x7d\x12\x84\x53\xd8\x15\xa7\xc6\x16\x31\x13\xf4\x28\x78\x96\x36\x57\xfb\x23\xa5\x15\x5f\xf7\x16\x19\xfd\x3c\x31\xce\xe3\xe7\xff\x1c\xfa\x1f\xaa\xae\x0d\x52\x3c\x32\xc7\x48\x98\xcb\xce\x52\xbc\xd2\xca\x76\xc3\xfa\x24\xf3\xb3\xc0\xab\x3c\x5a\x71\x43\x11\xd0\xb6\xeb\x6d\x17\x0d\x47\x52\xba\x8d\x9d\xeb\xb4\x12\x11\x74\x82\x95\xd8\x28\x51\xa1\xce\xa3\xde\x26\xaf\xb8\x21\xf8\xf1\x9f\xff\x80\xde\xc1\x5c\xae\x60\xab\xac\x86\x97\xd4\xa0\x46\xdb\x02\xab\x2a\x17\xdc\x49\x92\x8c\x74\xfb\x48\x7f\x9b\x5d\x5c\x90\x3c\x48\x4d\xb2\xc2\x12\xa9\x41\xb0\x20\x09\x05\xc9\xb8\xc2\x9a\x59\x31\x30\x0e\x42\x11\x28\x59\x0a\x5e\xde\xe5\x91\xb1\x45\xcb\x69\x3a\x8b\x96\xaf\xf8\x1a\xa1\xc5\x40\xe6\x49\x96\x06\xd1\x03\x8d\xd4\xf1\x18\x2c\x76\x70\xc0\x07\xfa\xe5\x24\x68\x49\x75\x0b\xb8\x7e\x3a\x8a\xd8\x87\x5c\xf6\xfc\xc4\x65\xd7\x0f\x0a\x77\x4c\xa2\x00\xff\x37\x36\x2d\x13\xfb\xdf\xfb\xb3\x1f\xce\x70\xba\x29\x76\xf7\x73\xa0\x36\xdc\xf3\xf9\x0d\xa8\x35\xea\x5a\xa8\xcd\x02\x98\x25\x75\x03\x2d\xbb\x1f\x72\xdd\xf5\x7c\x3e\xe6\xed\xea\x3f\x2b\x04\xfa\xf0\xd0\xf8\x9b\x45\x43\x66\x08\x8b\xb0\xe4\xff\xba\xe8\xa8\x50\x1a\xac\x4e\xac\xe1\x34\xba\x70\xf7\x52\x23\x8b\x1f\x6c\xfc\x10\xf7\x5a\xa9\x21\x7d\x8c\x69\xf4\xd0\xa3\x4c\x17\x2d\x33\xd2\x07\xb9\x49\x46\xd5\x1f\xba\xfe\xda\x95\xf7\xc7\x6e\x7f\x88\x4f\x77\xf6\x0e\x51\x87\xda\xe2\x22\x05\xfc\x30\x4b\xa9\xfa\x08\xcd\x15\x23\x56\x30\x83\x1f\xa2\xde\x67\xf9\x83\x7a\x3f\xfc\x58\xfd\x0d\x32\x4d\x05\xb2\xc7\x13\xd4\x88\x40\x6d\x65\x35\x3a\xbf\xbf\x48\x1f\x4b\xc0\x4a\xbe\x46\x6d\x38\x6d\x3f\x94\x01\x56\x07\x0a\x61\x7c\x4c\x21\x4b\x49\xbf\x3b\xd6\xfe\x0f\x97\xfb\x7d\xe5\xe8\x7a\xf9\xb5\xda\x40\xa5\xd0\x00\x35\xdc\x80\x2b\x26\x5f\x64\x69\x73\x3d\x88\x74\xcb\xd7\x6e\xc1\x1b\x15\xea\x50\x4f\xb8\x01\x6d\xa5\xcf\xa3\x4a\x02\x35\x78\x5c\x8a\x64\xf8\x95\xc0\x6b\xe5\xca\xf9\x1a\x25\x41\xcb\x04\x2f\xb9\xb2\x06\x58\x49\x4a\x1b\xa8\xb5\x6a\x01\xef\x1b\x66\x0d\x39\x20\x97\x3e\xd8\x9a\x71\xe1\xef\x92\x77\x29\x28\x0d\xac\x2c\x6d\x6b\x5d\x3b\x22\x57\x80\x52\xd9\x55\xd3\x73\x21\x05\xad\xb2\x92\x40\x28\xb9\x1a\xf8\x98\x8e\xb5\xc0\x88\x58\x79\x67\x2e\x61\x9f\x15\x80\x69\x04\xe2\x58\xb9\x5d\x7d\x55\x60\x65\xe9\xb6\x9b\x04\x5e\xc8\xad\x92\x08\x0d\x5b\x7b\x22\x27\x02\xd0\xb2\xed\x1e\xa8\xe7\xb5\xe1\xd4\xf0\x70\xf0\x0e\x75\xeb\xfa\xcb\x0a\x04\x6f\x39\x81\xaa\x21\x33\xa4\x95\x5c\xb9\x67\xc9\x0b\xcf\x70\xb7\x0b\x94\xa7\x66\x06\xa9\x33\xd5\xf7\xa8\xb9\xaa\x76\x3b\xd7\xba\x78\xd1\x24\x4b\xbb\xb1\xc5\xd5\xb1\xc2\x4b\x30\xbc\xed\xc4\x16\x4a\x8d\x8c\x10\x18\x64\xec\xe4\x41\xe1\xca\x63\x12\xea\xba\x6f\x49\x23\x20\xa6\x57\xee\xb9\xf6\x1f\x56\x28\x4b\x8b\x42\x30\x79\xe7\xaa\xcd\x50\x12\xb3\x94\x2d\xfd\x51\x1e\x2e\x86\xd0\x31\xe3\xce\xc5\x25\x29\x7f\xd4\xfe\x7d\x66\x60\xea\x46\x35\x17\xe8\x9f\x70\x3e\x7a\xe4\xb9\xb3\x93\xeb\xb3\x67\x97\x50\xaa\x6e\x1b\x76\xfb\x7d\x8e\x9a\xf1\xf5\x77\x80\x62\x85\x5a\x23\x84\xe2\x5e\xa8\x7b\x60\xb2\x82\x9a\x6b\x04\xb6\x61\xdb\x27\xf0\x93\xb2\x50\x32\x09\xa4\x59\x79\x17\x74\x5b\xad\x5d\x18\x75\x28\x5d\xa9\x38\x38\xb6\x40\xa1\x36\x5e\x24\xa0\xd5\x1c\x85\xf7\xb2\x41\x84\x46\x6d\xa0\xb5\xa5\x3f\xa0\x73\x2f\xba\x85\x0d\xe3\x04\x56\x12\x17\xe1\xdc\x64\xb5\x84\x52\xb5\x68\x46\x5e\x78\xf0\xfa\x0d\xbf\xfa\x1f\x87\x37\x82\x5f\x4e\x53\x78\x25\x54\xc1\x04\xac\x5d\xc6\x28\x84\xbb\x54\x0a\x5c\x33\x72\x74\x06\x43\x8c\xac\x71\x91\xe2\xed\xe8\xaf\x94\xdb\xbf\x66\xda\x45\x2e\xb6\x1d\x41\xde\x77\xb8\x6e\xce\xa0\x5e\xbb\xbe\xbd\xd7\xf1\x15\xd6\x5c\x06\xcb\xd6\x56\x96\xae\x01\x07\x6a\x18\x41\x68\x29\x0c\x30\x6f\x71\xb0\x5a\x40\x6f\xee\x80\x30\xe0\x79\x39\xc8\x87\xed\xd3\x59\xdf\x71\x07\xb9\xc4\xa0\xac\xa6\x7f\xff\xe1\xbb\x6f\x13\x43\x9a\xcb\x15\xaf\xb7\xd3\x37\x56\x8b\x05\xfc\x75\x1a\xfd\xc5\x37\x62\xb3\x9f\xe7\xbf\x26\x6b\x26\x2c\xee\x66\xb3\xf0\x4c\xb8\x39\xe6\xc7\xa0\x45\x6a\x94\xf7\x85\xc6\x52\x49\x89\x25\x81\xed\x94\xec\xe9\x80\x50\xc6\xec\x39\x1d\x24\x1e\xa0\xc5\x6b\x98\xee\x0d\xf3\x09\x3c\x85\x3c\x87\xf9\x7e\xad\xe7\x0c\x39\x48\xdc\xc0\xbf\xb0\xf8\x41\x95\x77\x48\xd3\x68\x63\xdc\xb5\x88\xe0\x02\x84\x2a\x99\xc3\x4b\x1a\x65\x08\x2e\x20\x4a\x59\xc7\xa3\xc0\x7a\xb2\x03\x14\x06\xdf\x0f\xf6\x41\x58\xe1\xcd\x15\x98\x5e\x5c\x04\x8f\xed\x8d\xaa\x64\x8b\xc6\xb0\x15\x8e\x4f\xe8\x73\xe3\x70\x14\x67\x88\xd6\xac\x20\x07\x6f\xfc\x8e\x69\x83\x41\x24\x71\xf5\xb8\xd7\xe2\xcd\xe1\xc5\xf2\x1c\xa4\x15\x62\xd8\x3f\xd1\xe8\x82\xb9\x17\xdb\x9d\x1d\x89\x27\x21\x75\x3d\xc9\x73\x70\xc5\xc9\xf9\xa8\x3a\xec\x74\x8e\x0d\x65\x74\x96\xb8\xfa\x78\xd8\x31\x1b\xe0\xde\x42\xc3\xea\x7d\x70\x58\x9d\xe2\x61\xf5\x08\xa0\xef\x5a\xde\x85\x17\xba\x9c\x11\x9c\x9f\x78\x04\x4d\xda\xb6\x40\xfd\x2e\xb8\xd0\xb5\xf4\x70\xde\xd4\xdf\x48\x1a\xed\xbd\x84\xab\xe7\xb3\x47\xd0\x51\x6b\xf5\x28\xb8\x54\xb4\x9d\xbe\x11\x6c\xeb\xb2\x2e\x9c\x93\xea\xbe\xf4\x4d\xc6\xf9\x25\x38\x5d\x0b\x18\x10\x2e\xfd\xe3\x60\x01\xe7\x7e\x74\xbe\x7b\x44\x9b\xb1\x65\xe9\xf2\xf1\xc7\xe8\xeb\x31\x06\x8d\xfd\xf8\x51\x9d\x43\x7e\x3d\x52\x0a\x9f\x7e\x0a\x6f\xad\x1e\x87\xa0\x8b\xe1\xbe\x50\x40\x0e\x51\xd4\xc3\x4f\x6a\xa5\x61\xea\x16\x79\x3e\xbf\x01\x9e\x8d\x61\x12\x81\x72\x45\xcd\x0d\xf0\x8b\x8b\x03\xd2\x64\x0f\x73\x91\x43\xe4\xfa\xe8\x8c\xaa\xa5\xef\x67\x42\xd3\xf3\x4b\x54\xb0\xf2\xce\x3d\xc9\x64\xb5\x70\xd9\x6e\x7a\x7e\x28\x86\xa3\x3a\x78\x71\x44\xf9\x67\xfe\x6b\x62\x0d\x6a\x5f\xb9\x2e\x20\x4a\x3a\xb9\xfa\xc2\xf0\xdf\x31\x7f\xfe\xec\x7c\x76\x03\x07\xcc\xd8\xcd\x2e\xa0\x74\x2f\x92\x1b\x08\x5d\xbd\xef\xad\x60\x78\x8f\xf8\x51\xa1\x74\x85\x3a\xd6\xac\xe2\xd6\x2c\xe0\x59\x77\x7f\xf3\x8b\xeb\x04\x5d\x89\xf0\x1d\xa0\xe7\xdd\x69\x5c\x3e\xc4\x65\xdf\x64\x5c\x40\x94\xa5\x4e\x68\xbf\x65\x38\xe5\xf8\xcb\x09\x3c\xd0\xbb\xc2\xf0\x5d\xa3\x9f\x6f\x79\x55\x09\x74\x24\xbc\xc2\xf0\x01\xaa\xb2\xda\x27\xae\x69\x18\x4f\x4f\x79\x10\x6f\x71\x96\x58\xc9\xef\xa7\xb3\xb8\x97\xd9\x8f\x2f\xe1\xdc\xb8\xfc\x5c\x99\xf3\x59\xd2\xd8\x96\x49\xfe\x3b\x4e\x5d\x23\x3c\x0b\xbc\x1d\x63\xd7\xdd\x0e\xde\xde\x8d\x2e\xda\xf0\x32\x9b\x25\x0d\xb5\x62\x1a\x65\xe4\xbf\xce\x38\x72\x83\x8b\x3d\x4a\x98\x3e\x8e\xc8\xdd\x71\x0e\x2d\x85\x32\x78\x52\x23\xc0\x20\xbd\xe6\x2d\x2a\x4b\xd3\xa1\x8e\x5c\xba\xd7\xe2\x7c\x76\x03\xa1\x2e\x1d\x10\xc2\xdd\xfd\xe3\x08\xbb\xbe\xbc\xbd\x34\xae\x83\xe7\xa6\x01\x06\x1b\x2c\x8c\xaf\x10\xd0\xef\xf1\xb5\x38\xd4\xdc\x17\xdf\x7f\x33\xaa\xbb\x03\xea\xd4\x1f\x6f\xf4\x99\x31\x4b\xc3\xb7\xaa\x2c\x0d\xdf\xe1\xff\x1b\x00\x00\xff\xff\x97\x3f\x1d\xc4\x98\x17\x00\x00")
+
+func faucetHtmlBytes() ([]byte, error) {
+ return bindataRead(
+ _faucetHtml,
+ "faucet.html",
+ )
+}
+
+func faucetHtml() (*asset, error) {
+ bytes, err := faucetHtmlBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "faucet.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+// Asset loads and returns the asset for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func Asset(name string) ([]byte, error) {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[cannonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
+ }
+ return a.bytes, nil
+ }
+ return nil, fmt.Errorf("Asset %s not found", name)
+}
+
+// MustAsset is like Asset but panics when Asset would return an error.
+// It simplifies safe initialization of global variables.
+func MustAsset(name string) []byte {
+ a, err := Asset(name)
+ if err != nil {
+ panic("asset: Asset(" + name + "): " + err.Error())
+ }
+
+ return a
+}
+
+// AssetInfo loads and returns the asset info for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func AssetInfo(name string) (os.FileInfo, error) {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[cannonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
+ }
+ return a.info, nil
+ }
+ return nil, fmt.Errorf("AssetInfo %s not found", name)
+}
+
+// AssetNames returns the names of the assets.
+func AssetNames() []string {
+ names := make([]string, 0, len(_bindata))
+ for name := range _bindata {
+ names = append(names, name)
+ }
+ return names
+}
+
+// _bindata is a table, holding each asset generator, mapped to its name.
+var _bindata = map[string]func() (*asset, error){
+ "faucet.html": faucetHtml,
+}
+
+// AssetDir returns the file names below a certain
+// directory embedded in the file by go-bindata.
+// For example if you run go-bindata on data/... and data contains the
+// following hierarchy:
+// data/
+// foo.txt
+// img/
+// a.png
+// b.png
+// then AssetDir("data") would return []string{"foo.txt", "img"}
+// AssetDir("data/img") would return []string{"a.png", "b.png"}
+// AssetDir("foo.txt") and AssetDir("notexist") would return an error
+// AssetDir("") will return []string{"data"}.
+func AssetDir(name string) ([]string, error) {
+ node := _bintree
+ if len(name) != 0 {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ pathList := strings.Split(cannonicalName, "/")
+ for _, p := range pathList {
+ node = node.Children[p]
+ if node == nil {
+ return nil, fmt.Errorf("Asset %s not found", name)
+ }
+ }
+ }
+ if node.Func != nil {
+ return nil, fmt.Errorf("Asset %s not found", name)
+ }
+ rv := make([]string, 0, len(node.Children))
+ for childName := range node.Children {
+ rv = append(rv, childName)
+ }
+ return rv, nil
+}
+
+type bintree struct {
+ Func func() (*asset, error)
+ Children map[string]*bintree
+}
+var _bintree = &bintree{nil, map[string]*bintree{
+ "faucet.html": &bintree{faucetHtml, map[string]*bintree{}},
+}}
+
+// RestoreAsset restores an asset under the given directory
+func RestoreAsset(dir, name string) error {
+ data, err := Asset(name)
+ if err != nil {
+ return err
+ }
+ info, err := AssetInfo(name)
+ if err != nil {
+ return err
+ }
+ err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
+ if err != nil {
+ return err
+ }
+ err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// RestoreAssets restores an asset under the given directory recursively
+func RestoreAssets(dir, name string) error {
+ children, err := AssetDir(name)
+ // File
+ if err != nil {
+ return RestoreAsset(dir, name)
+ }
+ // Dir
+ for _, child := range children {
+ err = RestoreAssets(dir, filepath.Join(name, child))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func _filePath(dir, name string) string {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
+}
+
diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go
index f86be62ba..90f79a47e 100644
--- a/cmd/geth/accountcmd.go
+++ b/cmd/geth/accountcmd.go
@@ -179,8 +179,7 @@ nodes.
)
func accountList(ctx *cli.Context) error {
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
-
+ stack, _ := makeConfigNode(ctx)
var index int
for _, wallet := range stack.AccountManager().Wallets() {
for _, account := range wallet.Accounts() {
@@ -278,7 +277,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
// accountCreate creates a new account into the keystore defined by the CLI flags.
func accountCreate(ctx *cli.Context) error {
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
+ stack, _ := makeConfigNode(ctx)
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
@@ -296,7 +295,7 @@ func accountUpdate(ctx *cli.Context) error {
if len(ctx.Args()) == 0 {
utils.Fatalf("No accounts specified to update")
}
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
+ stack, _ := makeConfigNode(ctx)
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
@@ -317,7 +316,7 @@ func importWallet(ctx *cli.Context) error {
utils.Fatalf("Could not read wallet file: %v", err)
}
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
+ stack, _ := makeConfigNode(ctx)
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
@@ -338,7 +337,7 @@ func accountImport(ctx *cli.Context) error {
if err != nil {
utils.Fatalf("Failed to load the private key: %v", err)
}
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
+ stack, _ := makeConfigNode(ctx)
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 2d121a611..66516b409 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -244,7 +244,7 @@ func exportChain(ctx *cli.Context) error {
}
func removeDB(ctx *cli.Context) error {
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
+ stack, _ := makeConfigNode(ctx)
dbdir := stack.ResolvePath(utils.ChainDbName(ctx))
if !common.FileExist(dbdir) {
fmt.Println(dbdir, "does not exist")
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
new file mode 100644
index 000000000..8d47159b2
--- /dev/null
+++ b/cmd/geth/config.go
@@ -0,0 +1,186 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bufio"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "unicode"
+
+ cli "gopkg.in/urfave/cli.v1"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/contracts/release"
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/naoina/toml"
+)
+
+var (
+ dumpConfigCommand = cli.Command{
+ Action: dumpConfig,
+ Name: "dumpconfig",
+ Usage: "Show configuration values",
+ ArgsUsage: "",
+ Category: "MISCELLANEOUS COMMANDS",
+ Description: `The dumpconfig command shows configuration values.`,
+ }
+
+ configFileFlag = cli.StringFlag{
+ Name: "config",
+ Usage: "TOML configuration file",
+ }
+)
+
+// These settings ensure that TOML keys use the same names as Go struct fields.
+var tomlSettings = toml.Config{
+ NormFieldName: func(rt reflect.Type, key string) string {
+ return key
+ },
+ FieldToKey: func(rt reflect.Type, field string) string {
+ return field
+ },
+ MissingField: func(rt reflect.Type, field string) error {
+ link := ""
+ if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
+ link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
+ }
+ return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
+ },
+}
+
+type ethstatsConfig struct {
+ URL string `toml:",omitempty"`
+}
+
+type gethConfig struct {
+ Eth eth.Config
+ Node node.Config
+ Ethstats ethstatsConfig
+}
+
+func loadConfig(file string, cfg *gethConfig) error {
+ f, err := os.Open(file)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg)
+ // Add file name to errors that have a line number.
+ if _, ok := err.(*toml.LineError); ok {
+ err = errors.New(file + ", " + err.Error())
+ }
+ return err
+}
+
+func defaultNodeConfig() node.Config {
+ cfg := node.DefaultConfig
+ cfg.Name = clientIdentifier
+ cfg.Version = params.VersionWithCommit(gitCommit)
+ cfg.HTTPModules = append(cfg.HTTPModules, "eth")
+ cfg.WSModules = append(cfg.WSModules, "eth")
+ cfg.IPCPath = "geth.ipc"
+ return cfg
+}
+
+func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
+ // Load defaults.
+ cfg := gethConfig{
+ Eth: eth.DefaultConfig,
+ Node: defaultNodeConfig(),
+ }
+
+ // Load config file.
+ if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+
+ // Apply flags.
+ utils.SetNodeConfig(ctx, &cfg.Node)
+ stack, err := node.New(&cfg.Node)
+ if err != nil {
+ utils.Fatalf("Failed to create the protocol stack: %v", err)
+ }
+ utils.SetEthConfig(ctx, stack, &cfg.Eth)
+ if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
+ cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
+ }
+
+ return stack, cfg
+}
+
+func makeFullNode(ctx *cli.Context) *node.Node {
+ stack, cfg := makeConfigNode(ctx)
+
+ utils.RegisterEthService(stack, &cfg.Eth)
+
+ // Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
+ shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
+ shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
+ if shhEnabled || shhAutoEnabled {
+ utils.RegisterShhService(stack)
+ }
+
+ // Add the Ethereum Stats daemon if requested.
+ if cfg.Ethstats.URL != "" {
+ utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
+ }
+
+ // Add the release oracle service so it boots along with node.
+ if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
+ config := release.Config{
+ Oracle: relOracle,
+ Major: uint32(params.VersionMajor),
+ Minor: uint32(params.VersionMinor),
+ Patch: uint32(params.VersionPatch),
+ }
+ commit, _ := hex.DecodeString(gitCommit)
+ copy(config.Commit[:], commit)
+ return release.NewReleaseService(ctx, config)
+ }); err != nil {
+ utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
+ }
+ return stack
+}
+
+// dumpConfig is the dumpconfig command.
+func dumpConfig(ctx *cli.Context) error {
+ _, cfg := makeConfigNode(ctx)
+ comment := ""
+
+ if cfg.Eth.Genesis != nil {
+ cfg.Eth.Genesis = nil
+ comment += "# Note: this config doesn't contain the genesis block.\n\n"
+ }
+
+ out, err := tomlSettings.Marshal(&cfg)
+ if err != nil {
+ return err
+ }
+ io.WriteString(os.Stdout, comment)
+ os.Stdout.Write(out)
+ return nil
+}
diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go
index fee8024a9..e5472836c 100644
--- a/cmd/geth/consolecmd_test.go
+++ b/cmd/geth/consolecmd_test.go
@@ -22,14 +22,17 @@ import (
"os"
"path/filepath"
"runtime"
- "sort"
"strconv"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
+)
+
+const (
+ ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
+ httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
)
// Tests that a node embedded within a console can be started up properly and
@@ -49,11 +52,7 @@ func TestConsoleWelcome(t *testing.T) {
geth.setTemplateFunc("gover", runtime.Version)
geth.setTemplateFunc("gethver", func() string { return params.Version })
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
- geth.setTemplateFunc("apis", func() []string {
- apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi)
- sort.Strings(apis)
- return apis
- })
+ geth.setTemplateFunc("apis", func() string { return ipcAPIs })
// Verify the actual welcome message to the required template
geth.expect(`
@@ -63,7 +62,7 @@ instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
coinbase: {{.Etherbase}}
at block: 0 ({{niltime}})
datadir: {{.Datadir}}
- modules:{{range apis}} {{.}}:1.0{{end}}
+ modules: {{apis}}
> {{.InputLine "exit"}}
`)
@@ -89,7 +88,7 @@ func TestIPCAttachWelcome(t *testing.T) {
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
- testAttachWelcome(t, geth, "ipc:"+ipc)
+ testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
geth.interrupt()
geth.expectExit()
@@ -103,7 +102,7 @@ func TestHTTPAttachWelcome(t *testing.T) {
"--etherbase", coinbase, "--rpc", "--rpcport", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
- testAttachWelcome(t, geth, "http://localhost:"+port)
+ testAttachWelcome(t, geth, "http://localhost:"+port, httpAPIs)
geth.interrupt()
geth.expectExit()
@@ -118,13 +117,13 @@ func TestWSAttachWelcome(t *testing.T) {
"--etherbase", coinbase, "--ws", "--wsport", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
- testAttachWelcome(t, geth, "ws://localhost:"+port)
+ testAttachWelcome(t, geth, "ws://localhost:"+port, httpAPIs)
geth.interrupt()
geth.expectExit()
}
-func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) {
+func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
// Attach to a running geth note and terminate immediately
attach := runGeth(t, "attach", endpoint)
defer attach.expectExit()
@@ -139,16 +138,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) {
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
attach.setTemplateFunc("datadir", func() string { return geth.Datadir })
- attach.setTemplateFunc("apis", func() []string {
- var apis []string
- if strings.HasPrefix(endpoint, "ipc") {
- apis = append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi)
- } else {
- apis = append(strings.Split(rpc.DefaultHTTPApis, ","), rpc.MetadataApi)
- }
- sort.Strings(apis)
- return apis
- })
+ attach.setTemplateFunc("apis", func() string { return apis })
// Verify the actual welcome message to the required template
attach.expect(`
@@ -158,7 +148,7 @@ instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
coinbase: {{etherbase}}
at block: 0 ({{niltime}}){{if ipc}}
datadir: {{datadir}}{{end}}
- modules:{{range apis}} {{.}}:1.0{{end}}
+ modules: {{apis}}
> {{.InputLine "exit" }}
`)
diff --git a/cmd/geth/dao_test.go b/cmd/geth/dao_test.go
index f9ce80218..ec7802ada 100644
--- a/cmd/geth/dao_test.go
+++ b/cmd/geth/dao_test.go
@@ -84,27 +84,24 @@ var daoGenesisForkBlock = big.NewInt(314)
// set in the database after various initialization procedures and invocations.
func TestDAOForkBlockNewChain(t *testing.T) {
for i, arg := range []struct {
- testnet bool
genesis string
expectBlock *big.Int
expectVote bool
}{
// Test DAO Default Mainnet
- {false, "", params.MainNetDAOForkBlock, true},
- // test DAO Default Testnet
- {true, "", params.TestNetDAOForkBlock, true},
+ {"", params.MainNetDAOForkBlock, true},
// test DAO Init Old Privnet
- {false, daoOldGenesis, nil, false},
+ {daoOldGenesis, nil, false},
// test DAO Default No Fork Privnet
- {false, daoNoForkGenesis, daoGenesisForkBlock, false},
+ {daoNoForkGenesis, daoGenesisForkBlock, false},
// test DAO Default Pro Fork Privnet
- {false, daoProForkGenesis, daoGenesisForkBlock, true},
+ {daoProForkGenesis, daoGenesisForkBlock, true},
} {
- testDAOForkBlockNewChain(t, i, arg.testnet, arg.genesis, arg.expectBlock, arg.expectVote)
+ testDAOForkBlockNewChain(t, i, arg.genesis, arg.expectBlock, arg.expectVote)
}
}
-func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis string, expectBlock *big.Int, expectVote bool) {
+func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBlock *big.Int, expectVote bool) {
// Create a temporary data directory to use and inspect later
datadir := tmpdir(t)
defer os.RemoveAll(datadir)
@@ -119,17 +116,11 @@ func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis stri
} else {
// Force chain initialization
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
- if testnet {
- args = append(args, "--testnet")
- }
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
geth.cmd.Wait()
}
// Retrieve the DAO config flag from the database
path := filepath.Join(datadir, "geth", "chaindata")
- if testnet && genesis == "" {
- path = filepath.Join(datadir, "testnet", "geth", "chaindata")
- }
db, err := ethdb.NewLDBDatabase(path, 0, 0)
if err != nil {
t.Fatalf("test %d: failed to open test database: %v", test, err)
@@ -137,9 +128,6 @@ func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis stri
defer db.Close()
genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
- if testnet {
- genesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
- }
if genesis != "" {
genesisHash = daoGenesisHash
}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index e8aef2bb2..c2d719d95 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -18,7 +18,6 @@
package main
import (
- "encoding/hex"
"fmt"
"os"
"runtime"
@@ -29,17 +28,13 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/console"
- "github.com/ethereum/go-ethereum/contracts/release"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
"gopkg.in/urfave/cli.v1"
)
@@ -60,7 +55,7 @@ func init() {
// Initialize the CLI app and start Geth
app.Action = geth
app.HideVersion = true // we have a command to print the version
- app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
+ app.Copyright = "Copyright 2013-2017 The go-ethereum Authors"
app.Commands = []cli.Command{
// See chaincmd.go:
initCommand,
@@ -82,6 +77,8 @@ func init() {
versionCommand,
bugCommand,
licenseCommand,
+ // See config.go
+ dumpConfigCommand,
}
app.Flags = []cli.Flag{
@@ -99,6 +96,7 @@ func init() {
utils.EthashDatasetsOnDiskFlag,
utils.FastSyncFlag,
utils.LightModeFlag,
+ utils.SyncModeFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.LightKDFFlag,
@@ -129,16 +127,12 @@ func init() {
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.IPCDisabledFlag,
- utils.IPCApiFlag,
utils.IPCPathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
utils.WhisperEnabledFlag,
utils.DevModeFlag,
utils.TestNetFlag,
- utils.VMForceJitFlag,
- utils.VMJitCacheFlag,
- utils.VMEnableJitFlag,
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
@@ -146,10 +140,10 @@ func init() {
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
utils.NoCompactionFlag,
- utils.SolcPathFlag,
utils.GpoBlocksFlag,
utils.GpoPercentileFlag,
utils.ExtraDataFlag,
+ configFileFlag,
}
app.Flags = append(app.Flags, debug.Flags...)
@@ -189,52 +183,6 @@ func geth(ctx *cli.Context) error {
return nil
}
-func makeFullNode(ctx *cli.Context) *node.Node {
- // Create the default extradata and construct the base node
- var clientInfo = struct {
- Version uint
- Name string
- GoVersion string
- Os string
- }{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
- extra, err := rlp.EncodeToBytes(clientInfo)
- if err != nil {
- log.Warn("Failed to set canonical miner information", "err", err)
- }
- if uint64(len(extra)) > params.MaximumExtraDataSize {
- log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
- extra = nil
- }
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
- utils.RegisterEthService(ctx, stack, extra)
-
- // Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
- shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
- shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
- if shhEnabled || shhAutoEnabled {
- utils.RegisterShhService(stack)
- }
- // Add the Ethereum Stats daemon if requested
- if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" {
- utils.RegisterEthStatsService(stack, url)
- }
- // Add the release oracle service so it boots along with node.
- if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- config := release.Config{
- Oracle: relOracle,
- Major: uint32(params.VersionMajor),
- Minor: uint32(params.VersionMinor),
- Patch: uint32(params.VersionPatch),
- }
- commit, _ := hex.DecodeString(gitCommit)
- copy(config.Commit[:], commit)
- return release.NewReleaseService(ctx, config)
- }); err != nil {
- utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
- }
- return stack
-}
-
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 491a4eb98..457728154 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -30,7 +30,7 @@ import (
var AppHelpTemplate = `NAME:
{{.App.Name}} - {{.App.Usage}}
- Copyright 2013-2016 The go-ethereum Authors
+ Copyright 2013-2017 The go-ethereum Authors
USAGE:
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
@@ -64,14 +64,15 @@ var AppHelpFlagGroups = []flagGroup{
{
Name: "ETHEREUM",
Flags: []cli.Flag{
+ configFileFlag,
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.NetworkIdFlag,
utils.TestNetFlag,
utils.DevModeFlag,
+ utils.SyncModeFlag,
+ utils.EthStatsURLFlag,
utils.IdentityFlag,
- utils.FastSyncFlag,
- utils.LightModeFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.LightKDFFlag,
@@ -115,7 +116,6 @@ var AppHelpFlagGroups = []flagGroup{
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.IPCDisabledFlag,
- utils.IPCApiFlag,
utils.IPCPathFlag,
utils.RPCCORSDomainFlag,
utils.JSpathFlag,
@@ -133,6 +133,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.NATFlag,
utils.NoDiscoverFlag,
utils.DiscoveryV5Flag,
+ utils.NetrestrictFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
},
@@ -158,30 +159,28 @@ var AppHelpFlagGroups = []flagGroup{
{
Name: "VIRTUAL MACHINE",
Flags: []cli.Flag{
- utils.VMEnableJitFlag,
- utils.VMForceJitFlag,
- utils.VMJitCacheFlag,
utils.VMEnableDebugFlag,
},
},
{
Name: "LOGGING AND DEBUGGING",
Flags: append([]cli.Flag{
- utils.EthStatsURLFlag,
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
+ utils.NoCompactionFlag,
}, debug.Flags...),
},
{
- Name: "EXPERIMENTAL",
+ Name: "DEPRECATED",
Flags: []cli.Flag{
- utils.WhisperEnabledFlag,
+ utils.FastSyncFlag,
+ utils.LightModeFlag,
},
},
{
- Name: "MISCELLANEOUS",
+ Name: "EXPERIMENTAL",
Flags: []cli.Flag{
- utils.SolcPathFlag,
+ utils.WhisperEnabledFlag,
},
},
}
diff --git a/cmd/puppeth/module.go b/cmd/puppeth/module.go
new file mode 100644
index 000000000..b6a029a01
--- /dev/null
+++ b/cmd/puppeth/module.go
@@ -0,0 +1,152 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+var (
+ // ErrServiceUnknown is returned when a service container doesn't exist.
+ ErrServiceUnknown = errors.New("service unknown")
+
+ // ErrServiceOffline is returned when a service container exists, but it is not
+ // running.
+ ErrServiceOffline = errors.New("service offline")
+
+ // ErrServiceUnreachable is returned when a service container is running, but
+ // seems to not respond to communication attempts.
+ ErrServiceUnreachable = errors.New("service unreachable")
+
+ // ErrNotExposed is returned if a web-service doesn't have an exposed port, nor
+ // a reverse-proxy in front of it to forward requests.
+ ErrNotExposed = errors.New("service not exposed, nor proxied")
+)
+
+// containerInfos is a heavily reduced version of the huge inspection dataset
+// returned from docker inspect, parsed into a form easily usable by puppeth.
+type containerInfos struct {
+ running bool // Flag whether the container is running currently
+ envvars map[string]string // Collection of environmental variables set on the container
+ portmap map[string]int // Port mapping from internal port/proto combos to host binds
+ volumes map[string]string // Volume mount points from container to host directories
+}
+
+// inspectContainer runs docker inspect against a running container
+func inspectContainer(client *sshClient, container string) (*containerInfos, error) {
+ // Check whether there's a container running for the service
+ out, err := client.Run(fmt.Sprintf("docker inspect %s", container))
+ if err != nil {
+ return nil, ErrServiceUnknown
+ }
+ // If yes, extract various configuration options
+ type inspection struct {
+ State struct {
+ Running bool
+ }
+ Mounts []struct {
+ Source string
+ Destination string
+ }
+ Config struct {
+ Env []string
+ }
+ HostConfig struct {
+ PortBindings map[string][]map[string]string
+ }
+ }
+ var inspects []inspection
+ if err = json.Unmarshal(out, &inspects); err != nil {
+ return nil, err
+ }
+ inspect := inspects[0]
+
+ // Infos retrieved, parse the above into something meaningful
+ infos := &containerInfos{
+ running: inspect.State.Running,
+ envvars: make(map[string]string),
+ portmap: make(map[string]int),
+ volumes: make(map[string]string),
+ }
+ for _, envvar := range inspect.Config.Env {
+ if parts := strings.Split(envvar, "="); len(parts) == 2 {
+ infos.envvars[parts[0]] = parts[1]
+ }
+ }
+ for portname, details := range inspect.HostConfig.PortBindings {
+ if len(details) > 0 {
+ port, _ := strconv.Atoi(details[0]["HostPort"])
+ infos.portmap[portname] = port
+ }
+ }
+ for _, mount := range inspect.Mounts {
+ infos.volumes[mount.Destination] = mount.Source
+ }
+ return infos, err
+}
+
+// tearDown connects to a remote machine via SSH and terminates docker containers
+// running with the specified name in the specified network.
+func tearDown(client *sshClient, network string, service string, purge bool) ([]byte, error) {
+ // Tear down the running (or paused) container
+ out, err := client.Run(fmt.Sprintf("docker rm -f %s_%s_1", network, service))
+ if err != nil {
+ return out, err
+ }
+ // If requested, purge the associated docker image too
+ if purge {
+ return client.Run(fmt.Sprintf("docker rmi %s/%s", network, service))
+ }
+ return nil, nil
+}
+
+// resolve retrieves the hostname a service is running on either by returning the
+// actual server name and port, or preferably an nginx virtual host if available.
+func resolve(client *sshClient, network string, service string, port int) (string, error) {
+ // Inspect the service to get various configurations from it
+ infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, service))
+ if err != nil {
+ return "", err
+ }
+ if !infos.running {
+ return "", ErrServiceOffline
+ }
+ // Container online, extract any environmental variables
+ if vhost := infos.envvars["VIRTUAL_HOST"]; vhost != "" {
+ return vhost, nil
+ }
+ return fmt.Sprintf("%s:%d", client.server, port), nil
+}
+
+// checkPort tries to connect to a remote host on a given
+func checkPort(host string, port int) error {
+ log.Trace("Verifying remote TCP connectivity", "server", host, "port", port)
+ conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Second)
+ if err != nil {
+ return err
+ }
+ conn.Close()
+ return nil
+}
diff --git a/cmd/puppeth/module_dashboard.go b/cmd/puppeth/module_dashboard.go
new file mode 100644
index 000000000..17f119111
--- /dev/null
+++ b/cmd/puppeth/module_dashboard.go
@@ -0,0 +1,537 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "math/rand"
+ "path/filepath"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// dashboardContent is the actual dashboard HTML content to serve up when users
+// load the dashboard website.
+var dashboardContent = `
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <!-- Meta, title, CSS, favicons, etc. -->
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <title>{{.NetworkTitle}}: Ethereum Testnet</title>
+
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/css/custom.min.css" rel="stylesheet">
+ <style>
+ .vertical-center {
+ min-height: 100%;
+ min-height: 95vh;
+ display: flex;
+ align-items: center;
+ }
+ .nav.side-menu li a {
+ font-size: 18px;
+ }
+ .nav-sm .nav.side-menu li a {
+ font-size: 10px;
+ }
+ pre{
+ white-space: pre-wrap;
+ }
+ </style>
+ </head>
+
+ <body class="nav-sm" style="overflow-x: hidden">
+ <div class="container body">
+ <div class="main_container">
+ <div class="col-md-3 left_col">
+ <div class="left_col scroll-view">
+ <div class="navbar nav_title" style="border: 0; margin-top: 8px;">
+ <a class="site_title"><i class="fa fa-globe" style="margin-left: 6px"></i> <span>{{.NetworkTitle}} Testnet</span></a>
+ </div>
+ <div class="clearfix"></div>
+ <br />
+ <div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
+ <div class="menu_section">
+ <ul class="nav side-menu">
+ {{if .EthstatsPage}}<li><a onclick="load('//{{.EthstatsPage}}')"><i class="fa fa-tachometer"></i> Network Stats</a></li>{{end}}
+ {{if .ExplorerPage}}<li><a onclick="load('//{{.ExplorerPage}}')"><i class="fa fa-database"></i> Block Explorer</a></li>{{end}}
+ {{if .WalletPage}}<li><a onclick="load('//{{.WalletPage}}')"><i class="fa fa-address-book-o"></i> Browser Wallet</a></li>{{end}}
+ {{if .FaucetPage}}<li><a onclick="load('//{{.FaucetPage}}')"><i class="fa fa-bath"></i> Crypto Faucet</a></li>{{end}}
+ <li id="connect"><a><i class="fa fa-plug"></i> Connect Yourself</a>
+ <ul id="connect_list" class="nav child_menu">
+ <li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-geth')">Go Ethereum: Geth</a></li>
+ <li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-mist')">Go Ethereum: Wallet & Mist</a></li>
+ <li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-mobile')">Go Ethereum: Android & iOS</a></li>
+ </ul>
+ </li>
+ <li><a onclick="load('#about')"><i class="fa fa-heartbeat"></i> About Puppeth</a></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="right_col" role="main" style="padding: 0">
+ <div id="connect-go-ethereum-geth" hidden style="padding: 16px;">
+ <div class="page-title">
+ <div class="title_left">
+ <h3>Connect Yourself &ndash; Go Ethereum: Geth</h3>
+ </div>
+ </div>
+ <div class="clearfix"></div>
+ <div class="row">
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-archive" aria-hidden="true"></i> Archive node <small>Retains all historical data</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>An archive node synchronizes the blockchain by downloading the full chain from the genesis block to the current head block, executing all the transactions contained within. As the node crunches through the transactions, all past historical state is stored on disk, and can be queried for each and every block.</p>
+ <p>Initial processing required to execute all transactions may require non-negligible time and disk capacity required to store all past state may be non-insignificant. High end machines with SSD storage, modern CPUs and 8GB+ RAM are recommended.</p>
+ <br/>
+ <p>To run an archive node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
+ <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre>
+ </p>
+ <br/>
+ <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-laptop" aria-hidden="true"></i> Full node <small>Retains recent data only</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>A full node synchronizes the blockchain by downloading the full chain from the genesis block to the current head block, but does not execute the transactions. Instead, it downloads all the transactions receipts along with the entire recent state. As the node downloads the recent state directly, historical data can only be queried from that block onward.</p>
+ <p>Initial processing required to synchronize is more bandwidth intensive, but is light on the CPU and has significantly reduced disk requirements. Mid range machines with HDD storage, decent CPUs and 4GB+ RAM should be enough.</p>
+ <br/>
+ <p>To run a full node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
+ <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre>
+ </p>
+ <br/>
+ <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="clearfix"></div>
+ <div class="row">
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-mobile" aria-hidden="true"></i> Light node <small>Retrieves data on demand</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>A light node synchronizes the blockchain by downloading and verifying only the chain of headers from the genesis block to the current head, without executing any transactions or retrieving any associated state. As no state is available locally, any interaction with the blockchain relies on on-demand data retrievals from remote nodes.</p>
+ <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Low end machines with arbitrary storage, weak CPUs and 512MB+ RAM should cope well.</p>
+ <br/>
+ <p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
+ <pre>geth --datadir=$HOME/.{{.Network}} --light init {{.GethGenesis}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre>
+ </p>
+ <br/>
+ <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-microchip" aria-hidden="true"></i> Embedded node <small>Conserves memory vs. speed</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>An embedded node is a variation of the light node with configuration parameters tuned towards low memory footprint. As such, it may sacrifice processing and disk IO performance to conserve memory. It should be considered an <strong>experimental</strong> direction for now without hard guarantees or bounds on the resources used.</p>
+ <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Embedded machines with arbitrary storage, low power CPUs and 128MB+ RAM may work.</p>
+ <br/>
+ <p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
+ <pre>geth --datadir=$HOME/.{{.Network}} --light init {{.GethGenesis}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=32 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre>
+ </p>
+ <br/>
+ <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="connect-go-ethereum-mist" hidden style="padding: 16px;">
+ <div class="page-title">
+ <div class="title_left">
+ <h3>Connect Yourself &ndash; Go Ethereum: Wallet &amp; Mist</h3>
+ </div>
+ </div>
+ <div class="clearfix"></div>
+ <div class="row">
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-credit-card" aria-hidden="true"></i> Desktop wallet <small>Interacts with accounts and contracts</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>The Ethereum Wallet is an <a href="https://electron.atom.io/" target="about:blank">Electron</a> based desktop application to manage your Ethereum accounts and funds. Beside the usual account life-cycle operations you would expect to perform, the wallet also provides a means to send transactions from your accounts and to interact with smart contracts deployed on the network.</p>
+ <p>Under the hood the wallet is backed by a go-ethereum full node, meaning that a mid range machine is assumed. Similarly, synchronization is based on <strong>fast-sync</strong>, which will download all blockchain data from the network and make it available to the wallet. Light nodes cannot currently fully back the wallet, but it's a target actively pursued.</p>
+ <br/>
+ <p>To connect with the Ethereum Wallet, you'll need to initialize your private network first via Geth as the wallet does not currently support calling Geth directly. To initialize your local chain, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and run:
+ <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
+ </p>
+ <p>With your local chain initialized, you can start the Ethereum Wallet:
+ <pre>ethereumwallet --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre>
+ <p>
+ <br/>
+ <p>You can download the Ethereum Wallet from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-picture-o" aria-hidden="true"></i> Mist browser <small>Interacts with third party DApps</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>The Mist browser is an <a href="https://electron.atom.io/" target="about:blank">Electron</a> based desktop application to load and interact with Ethereum enabled third party web DApps. Beside all the functionality provided by the Ethereum Wallet, Mist is an extended web-browser where loaded pages have access to the Ethereum network via a web3.js provider, and may also interact with users' own accounts (given proper authorization and confirmation of course).</p>
+ <p>Under the hood the browser is backed by a go-ethereum full node, meaning that a mid range machine is assumed. Similarly, synchronization is based on <strong>fast-sync</strong>, which will download all blockchain data from the network and make it available to the wallet. Light nodes cannot currently fully back the wallet, but it's a target actively pursued.</p>
+ <br/>
+ <p>To connect with the Mist browser, you'll need to initialize your private network first via Geth as Mist does not currently support calling Geth directly. To initialize your local chain, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and run:
+ <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
+ </p>
+ <p>With your local chain initialized, you can start Mist:
+ <pre>mist --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre>
+ <p>
+ <br/>
+ <p>You can download the Mist browser from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="connect-go-ethereum-mobile" hidden style="padding: 16px;">
+ <div class="page-title">
+ <div class="title_left">
+ <h3>Connect Yourself &ndash; Go Ethereum: Android &amp; iOS</h3>
+ </div>
+ </div>
+ <div class="clearfix"></div>
+ <div class="row">
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-android" aria-hidden="true"></i> Android devices <small>Accesses Ethereum via Java</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>Starting with the 1.5 release of go-ethereum, we've transitioned away from shipping only full blown Ethereum clients and started focusing on releasing the code as reusable packages initially for Go projects, then later for Java based Android projects too. Mobile support is still evolving, hence is bound to change often and hard, but the Ethereum network can nonetheless be accessed from Android too.</p>
+ <p>Under the hood the Android library is backed by a go-ethereum light node, meaning that given a not-too-old Android device, you should be able to join the network without significant issues. Certain functionality is not yet available and rough edges are bound to appear here and there, please report issues if you find any.</p>
+ <br/>
+ <p>The stable Android archives are distributed via Maven Central, and the develop snapshots via the Sonatype repositories. Before proceeding, please ensure you have a recent version configured in your Android project. You can find details in <a href="https://github.com/ethereum/go-ethereum/wiki/Mobile:-Introduction#android-archive" target="about:blank">Mobile: Introduction &ndash; Android archive</a>.
+ <p>Before connecting to the Ethereum network, download the <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> genesis json file and either store it in your Android project as a resource file you can access, or save it as a string in a variable. You're going to need to to initialize your client.</p>
+ <p>Inside your Java code you can now import the geth archive and connect to Ethereum:
+ <pre>import org.ethereum.geth.*;</pre>
+<pre>
+Enodes bootnodes = new Enodes();{{range .BootnodesLight}}
+bootnodes.append(new Enode("{{.}}"));{{end}}
+
+NodeConfig config = new NodeConfig();
+config.setBootstrapNodes(bootnodes);
+config.setEthereumNetworkID({{.NetworkID}});
+config.setEthereumGenesis(genesis);{{if .Ethstats}}
+config.setEthereumNetStats("{{.Ethstats}}");{{end}}
+
+Node node = new Node(getFilesDir() + "/.{{.Network}}", config);
+node.start();
+</pre>
+ <p>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <div class="x_panel">
+ <div class="x_title">
+ <h2><i class="fa fa-apple" aria-hidden="true"></i> iOS devices <small>Accesses Ethereum via ObjC/Swift</small></h2>
+ <div class="clearfix"></div>
+ </div>
+ <div class="x_content">
+ <p>Starting with the 1.5 release of go-ethereum, we've transitioned away from shipping only full blown Ethereum clients and started focusing on releasing the code as reusable packages initially for Go projects, then later for ObjC/Swift based iOS projects too. Mobile support is still evolving, hence is bound to change often and hard, but the Ethereum network can nonetheless be accessed from iOS too.</p>
+ <p>Under the hood the iOS library is backed by a go-ethereum light node, meaning that given a not-too-old Apple device, you should be able to join the network without significant issues. Certain functionality is not yet available and rough edges are bound to appear here and there, please report issues if you find any.</p>
+ <br/>
+ <p>Both stable and develop builds of the iOS framework are available via CocoaPods. Before proceeding, please ensure you have a recent version configured in your iOS project. You can find details in <a href="https://github.com/ethereum/go-ethereum/wiki/Mobile:-Introduction#ios-framework" target="about:blank">Mobile: Introduction &ndash; iOS framework</a>.
+ <p>Before connecting to the Ethereum network, download the <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> genesis json file and either store it in your iOS project as a resource file you can access, or save it as a string in a variable. You're going to need to to initialize your client.</p>
+ <p>Inside your Swift code you can now import the geth framework and connect to Ethereum (ObjC should be analogous):
+ <pre>import Geth</pre>
+<pre>
+var error: NSError?
+
+let bootnodes = GethNewEnodesEmpty(){{range .BootnodesLight}}
+bootnodes?.append(GethNewEnode("{{.}}", &error)){{end}}
+
+let config = GethNewNodeConfig()
+config?.setBootstrapNodes(bootnodes)
+config?.setEthereumNetworkID({{.NetworkID}})
+config?.setEthereumGenesis(genesis){{if .Ethstats}}
+config?.setEthereumNetStats("{{.Ethstats}}"){{end}}
+
+let datadir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
+let node = GethNewNode(datadir + "/.{{.Network}}", config, &error);
+try! node?.start();
+</pre>
+ <p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="about" hidden>
+ <div class="row vertical-center">
+ <div style="margin: 0 auto;">
+ <div class="x_panel">
+ <div class="x_title">
+ <h3>Puppeth &ndash; Your Ethereum private network manager</h3>
+ <div class="clearfix"></div>
+ </div>
+ <div style="display: inline-block; vertical-align: bottom; width: 623px; margin-top: 16px;">
+ <p>Puppeth is a tool to aid you in creating a new Ethereum network down to the genesis block, bootnodes, signers, ethstats server, crypto faucet, wallet browsers, block explorer, dashboard and more; without the hassle that it would normally entail to manually configure all these services one by one.</p>
+ <p>Puppeth uses ssh to dial in to remote servers, and builds its network components out of docker containers using docker-compose. The user is guided through the process via a command line wizard that does the heavy lifting and topology configuration automatically behind the scenes.</p>
+ <br/>
+ <p>Puppeth is distributed as part of the <a href="https://geth.ethereum.org/downloads/" target="about:blank">Geth &amp; Tools</a> bundles, but can also be installed separately via:<pre>go get github.com/ethereum/go-ethereum/cmd/puppeth</pre></p>
+ <br/>
+ <p><em>Copyright 2017. The go-ethereum Authors.</em></p>
+ </div>
+ <div style="display: inline-block; vertical-align: bottom; width: 217px;">
+ <img src="puppeth.png" style="height: 256px; margin: 16px 16px 16px 16px"></img>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="frame-wrapper" hidden style="position: absolute; height: 100%;">
+ <iframe id="frame" style="position: absolute; width: 1920px; height: 100%; border: none;" onload="if ($(this).attr('src') != '') { resize(); $('#frame-wrapper').fadeIn(300); }"></iframe>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script>
+ <script>
+ var load = function(url) {
+ $("#connect-go-ethereum-geth").fadeOut(300)
+ $("#connect-go-ethereum-mist").fadeOut(300)
+ $("#connect-go-ethereum-mobile").fadeOut(300)
+ $("#about").fadeOut(300)
+ $("#frame-wrapper").fadeOut(300);
+
+ setTimeout(function() {
+ if (url.substring(0, 1) == "#") {
+ $('.body').css({overflowY: 'auto'});
+ $("#frame").attr("src", "");
+ $(url).fadeIn(300);
+ } else {
+ $('.body').css({overflowY: 'hidden'});
+ $("#frame").attr("src", url);
+ }
+ }, 300);
+ }
+ var resize = function() {
+ var sidebar = $($(".navbar")[0]).width();
+ var content = 1920;
+ var limit = document.body.clientWidth - sidebar;
+ var scale = limit / content;
+
+ console.log(document.body.clientHeight);
+
+ $("#frame-wrapper").width(content / scale);
+ $("#frame-wrapper").height(document.body.clientHeight / scale);
+ $("#frame-wrapper").css({
+ transform: 'scale(' + (scale) + ')',
+ transformOrigin: "0 0"
+ });
+ };
+ $(window).resize(resize);
+
+ var item = $(".side-menu").children()[0];
+ $(item).children()[0].click();
+ $(item).addClass("active");
+ </script>
+ </body>
+</html>
+`
+
+// dashboardMascot is the png dump of the mascot to display on the dashboard about page.
+var dashboardMascot = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01s\x00\x00\x02\x00\b\x06\x00\x00\x00p\xe4\x8c`\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\v\x13\x00\x00\v\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\atIME\a\xe1\x03\x1d\x0e0&\xf3\xca\t\x11\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\a\x00\x00 \x00IDATx\xda\xec\xbdw|\x15U\xfe\xff\xff<3s[\x12BB\x12H!\x90\u0411&H\a\x01i\"V\xb0!b\xc1^>\xbb\xee\xae\xdd\xd5u\xed}-k/X\u058e\xa8\xa0\u049bH\x87\bH\xef\x04BI\x81\xf4[\xa6\x9c\xf3\xfbcnB`\xdd\xef\xcf\xdd\xd5\xddU\xe7\xf9x\\\u023d\u027dw\xee\u0339\xafy\xcd\xfb\xbc\xcf\xfb\r\x1e\x1e\xff\";wn\x17\xb7\xdf~\xabQw_)\xa5+\xa5\x1a)\xa5\u0494R\xc9J\xa9D\xa5T\xf0\xb8\xa7\xe9\x1f|\xf0\x9eQYyXx{\xd0\xc3\xe3\xc7\xc3\xfbBy\xfcK\xbc\xf3\xce[\xe2\xd2K/Wq\x11o>y\xf2G'\xaf[\xb7\xae\xff\u05ad\xdb2\xa5\x94\xa9\xd1h\xb4:---\u06b9s\xe7HJJ\xf2\xb2f\xcd27\x9e}\xf6\x98\"!\xc4\u07ba\xd7x\xf6\xd9g\xb4\xcb.\xbbD\xa6\xa4\xa4\xb1d\xc97\f\x18p\xb2\xb7c=<<1\xf7\xf8O\xf0\x97\xbf<EII\x89x\xf4\xd1\xc7\x14\xc0\x8c\xe9_\x0e\xffv\u035a\xa7\u05ec]\u05f9`\xf5jJK\xcb0M\x13M\xd3\b\x85B4i\x92J\xabV\xf9\xe4\xe5\u54d9\x99Y\x90\x9a\x9a2\xa5_\xbf\xfeS\xfb\xf5\ubfe9\xee5o\xba\xe9V\x9e}\xf6\t\x94R\b\xe1\rI\x0f\x0f\x0f\x8f\x9f\x9c\xf7\xde{\xb7\x81;\u007fs\xe4\xc4+.\xafi\u04e6\x8d\x02\xfe\xe1M\xd75\x95\x9c\x9c\xacZ\xb7n\xadF\x8d\x1a\xa5\u018d\xbbp\xd7=\xf7\xfc\xf1\xf5\xaf\x17.\x18q\xfc\xeb\u007f\xfa\xe9'\xdeN\xf6\xf0\xf0\xf0\xf8\xa9\xc9i\x9e\v\xc0\x97_N\x1bq\u0265\x97\x1c\xceh\u06b4^\xb4\x85\xa6)\xc3\xe7S>\xbf_\x89\xef\x15u]\x86B!\x99\x93\x93\xa3\xfa\xf6\xed\xab.\xb8\xe0\x82\x8a\a\x1e\xb8\xef\x83}\xfbv\xf7k\xf8\x1e\u007f\xf8\xc3\x1f\xc4\u0739\xb3\xbc\x9d\xed\xe1\xe1\xe1\xf1S\xb0r\xe5r\r@)\xd5\xee\xf6\xdbo\u06d2\u07fa\xb5\x02$\xa04]W\x86\xe1S\t\u024dUjV\xaeJ\xcdn\xa9\x12S\u04d5\x10\xe2\x18A\x17B(\xbf\xdfo'5jde\xe74W\xfd\xfa\xf5WW^yE\xe5k\xaf\xbf\xfa\xa0R*\xb1\xee\xbdz\xf7\xee#\xdex\xe35o\xa7{x\xfc@\fo\x17x\xfc\x10\x94R\x9cz\xeaH\x00\xfe\xf6\xb7\xb7\xaf\u0634yK\xfb\xdd;w\u0680\xa1\xe9:\x86\xcfO\x93\xdc\xd6t\x18r\x06\x19m\xbb\xa0\xe9\x06f\xd5\x11\xcavmd\xff\xa65\x1c\u06be\x91pU\x05J)L\xd3\xd4\x1d)\xb1\x1d\u01e9\xae\xae\xe1\u0421\x83\u027bw\xef\xfe\xe3\x96\xcd[\u03999s\xfam\xa3F\x8d\x9e\xber\xe5\n\xb5r\xe5\n>\xfa\xe8\x03q\xe1\x85\x17)\xef\bxxxx\xfc\b\u0318\xf1U\x9d+O\xbe\u66ab\xe7\xb6\xcc\xcbW\x80\x85\x10J7|\xaay\x97^\ua497\xa7\xab{\xd7Ku\xcfw\x8e\xbao\x83\xa3\x9e\xd8\"\xd53[\x1d\xf5\xe0\xd2\xfd\xea\xca\x17>Q\xfd/\xbcZ\xa5\xe5\xe6\xff]X&!1Q\xa6\xa5\xa5\xa9\xf6\xed\u06ebs\xce9[=\xfa\xe8#\x1f\u06b6\x99Y\xf7\u0793&\xbd\xe1\x99\x0e\x0f\x0f\u03d9{\xfc\x18l\u0630A\x03\xe4\xf4\xe9\xd3z\x95\x1d>\xd2\xfdPq1\x80.4\x8d`R2\x9dF\x9eG\xab\xc1\xa7\x11\x8dJ|\xd2B\xf3\t4\f4M#%3\x9b\x93\xce<\x97\x13O=\x97\xc2u+Y\xf1\xc9$VM{\x8fhm\r\xb6\x94H\xc7\x11JJu\xe8\xd0!QYYIY\xd9\xe1\v\v\v\v{\xbf\xfa\xea\xcb\u007f\xbc\xfa\xeak?\x12B\xd8c\u019c\xa3=\xf0\xc0\xfd\xaas\u7b9eK\xf7\xf0\xf0\xc4\xdc\xe3\xdf\b\xb3\u803dq\u00e6v\xa6i6\x89E\xc2\x12\xd0p\x1c2\xf2\xdb\u04a6\xcf`t\xc0pb\x04|\x02\x03\xb0-\vi\x83\xa6\t\x10\x02C\xf7\u0476ooZu\xefM\x87\x81#\x98?\xe9iv\x16,AJI$\x12\x11J)4Mc\u02d6-\x1c<x0\xff\u0421C\xefWTT\x9c\xa7\x94\xfa\x83\x10\xa2\xf0\xb3\xcf>\xe7\xc9'\x9f\x10II\x89\xea\xba\xebn\xf0\x0e\x8a\x87G\x03to\x17x\xfc\x10\xe6\u0319\xeb(\xa5\xf4\x85\v\x17\\\xb5x\xf1\u0493JJJ\x00\x84\xa6\xe9\xe4\x9f4\x90^\xe7\\\x8a\xdf\x1f\xc4'\x1c4\x01 @\x80B\xe1(\x85t\x14\xb6\xed`\xc5\x1c\x84\xd0\xc9\xed|\x02\xed\xfb\x8d\xc4\x17\brp\xebz,3\x86m\xdbH)\xd14\r\u02f2\u063f\u007f?\xd5\xd5\xd5\x1d\xb7n\xdd:\xfc\xfd\xf7\u07dd\xf3\xf4\xd3\xcf\x1c\x99={\x0eYYYb\xfc\xf8q\u031e=\xc7;0\x1e\x1eq4o\x17x\xfc\x13\xe4X\x96\u0569\xa2\xa2<\xae\xd6`\x04\x83\xa4d\u5498\x92\x8a\x81D\x17u+\xd1\x14(\x85T \x158\xb87KB,\x16\xa5\xb6\"L\x93\x9c\xe6\x9cu\xcb\xc3\\\xf9\u0707\xb4\xee\xd1\x1f\x00\xd34\xa9\xad\xad\xc54M\xa4t\xd4\u06b5\xeb\x983gn\xe7G\x1ey\xec\u04d2\x92\x03\xb9\x00\xaf\xbd\xf6\xba*--\xf5V\x17yxxb\xee\xf1\xafP[Se\x16\x15\xed\x8fX\x8e\x13Wk\b$$\xd18\xb39\x9a\x06\x02\x85&@\x8b\vz\xc3\u015c\ueb27B\x01\xb6\u0490B'\\S\x8be\xd9t\x191\x8a\u02df\xf9\x80\xbec/\x03\xc0\xb6mjjj\x88FcB\b\xa1v\xed\xda\u0152%K\xbb\xfc\xe1\x0f\xb7~\xban\u0777-\x00\x1e{\xec\t5c\u0197\x9e\xa0{xxb\xee\xf1\xcfR\xb4\xbfH\x84\xc3a\u0371\xed\xfa\xc7|\x81 \xa1\u4538\x11W8J \x95\xa83\ueba0\xbb&\x1d\xea\\\xbar\x1d\xba\x12:\u04b1\xa9\xad\xb6i\u05a6\x05\xe3\x1fy\x83\xb3n~\x88P\xa3d\xa4\x94\xd4\u0506\tG\"BJ\xa9\n\v\vY\xb7n]\xcfG\x1ey\xec\x8b\x193\xbe\xec\fp\xdaig\xa8\r\x1b\xbe\xf3B\x85\x1e\x1e\x9e\x98{\xfc3\xa4\xa4\xa4\xa0i\x9aRR\x1e}P\b\x94Tq\xe7-\u0730J\u0703\u02fa$\u0138So\x98\x86\"\x15\xd8R\xa0\x14(\xe9P]a\xe2O\xd09\xf5\x86\xbb\xb8\xe0\xde\x17H\xcdj\x8e\x92\x0e\xe1p\x98H4*\x14\xa8\x83\a\x0f\xb1a\u00c6\xae\xef\xbf\xff\xc1\x97\v\x16\xcc;\x1f\xa0s\xe7\xae\xce\u0319\u04fdq\xec\u1279\xb7\v<~(\u035ae\xeb)\xa9\xa9\xbe:\xd3\r`\x9b&\xe1\x8a#q]\x17\xf5\x95\xdb\xd4q\xff\x1f\r\xb7\xa8\xfa\u01dc\xb8\x93\a\xd7\xd5\xd7VE\xb1\xa5\xa2\xf7y\x13\x18\xf7\xd0\xeb4ks\x02(E4\x12!\x12\x8d\n\u02d1\xeaPq\t\u02d7\xafh\xf9\xe1\a\x1f~<s\xe6\xf4\xdb\x00F\x8d\x1a-\xdfy\xe7m\xf1\xd9gS\xbc\x83\xe4\u1279\x87\xc7\x0f\xa0*))\xb1\xd8\xe7\xf3\xd7\xeb\xb4\x15\x8dPY\xb2\x1f%\xeb*\x1e\xaa\xb88\xbb\xee[\xd5\u074e\x13\xf6\xba\xfb\x96t\x1d\xbc\x16\u007f\x8e\x15\x8b\x11\x8b\xdat\x1av*\xe3\x1ez\x9d\xdc\u03bd\x000\xa3Qj\xc3a\x11\xb5lJ\x0f\x973c\xe6,\xde\u007f\xff\x83\u01fe\xf8b\xeam\x00\x97^z\x99Z\xbf~\x83\x98<\xf9#\xef(yxb\xee\xe1\xf1\x8f0\f\x9f.\x84\xa8\xb2L\xab --\r\xdc:+8\xb6EU\xc9Aj+\xcaA\xd3P\xc7LI\n\xb7p\xcbqB~\u053d\v$\x02[\xb9~]\x13\x02M\b\xa4m\x13\xad1i\u0777\x1f\x17>\xfc:\xad{\x0f\x06\xc0\x8eE\x89\x86\u00d8\xb6Cyu\r\xf3\xe7/\xe0\x93O\xa6<\xb6h\xd1\u009b\x00\xfe\xf4\xa7{\u055a5k\u0164I\xaf{\a\xcc\xc3\x13s\x0f\x8f\xef\xe3\xfe\a\xfe\xac\x03\xb4i\u04fa\xbcY\xb3f\xf5\x9a,\x1d\x87\xaa\u0483T\x1c\xda\x17\x9f\xect\xeb%\xba\x99,\xea8\xf9v\u007f\xaf\xe2.\xbc\xee/l)p\x94h\x10W\x17\xeeB\xa2\xea(\xb9\u077ar\xc1C\xaf\xd3a\xd0i\xae\xa0\x9b1\xccH\x18\u01d1T\xd4\u052a\xb9\xf3\x17\xf0\u059bo=\xb3`\xfe\u071b\x00\x1e~\xf8\x11\x15\x8b\u017cq\xed\u1279\x87\xc7?\xc0\x01h\u07fe\xfd\x16M\xd3\x0e\x01B)\xa5P\x8a\xea\xb2b\x8e\xec\u0749\xe1sE\xbc\xae\xc1\x84\x10 \x8e\x9f\xfa\x14\xaa~R\xb4>$\x03\xd8Ra\xab\x86a\x18\xe1\xc6\u02ebbd\xb5o\xc3y\xf7\xbfD\x87\x93G\xb9\x82n\x99\u0122a\x1c\xa9DyU\x8d\x9c5g\x1e\x93&\xbd\xf9\xccg\x9f~\xf2;\x80\ubbffQ~\xfa\xe9'\xde\xd8\xf6\xf0\xc4\xdc\xc3\xe3x\xfa\xf4\xee\xad\x00\x86\x0e\x1d\xbe==#cKBR\x12\xae\xc1\x96\xd4\x1c.\xe6\xd0\xf6\x8d\xf1\xa5\xfb\xc7\x0f\xa9\xe3\x03,\xc2\x15\xf4\x06\x8f\v\x14J\x81\x1d\x8f\xb3\x83B\x13\n\x81\xe6\nz\xb5Ef\xbb\x96\x9c\xff\xd0kt\x19y\xae{f1M\xccH\x18\xe98Zyu\xad\x9c\xbb\xe0k>\xfc\u88e7\xdf{\xf7\x9d\xdb\x01\u018e=O\xae^\xbd\xd2K[\xf4\xf0\xc4\xdc\u00e3!C\x87\x0e\x97\x80\x10B\x1c\xc8i\xde|k\x8b\xdc\\\x00[I\x89\x15\rS\xbag;U%\xc5\xe8\x86\xcfm\xff\u01b1\xee\\\x1c\u04e1P\x1c\xa3\xf3*\x1e\x9d\xc15\xfa\rD\x1f4\xcdM]\xac=b\x91\u05a29c\xef}\x81\ue9cfs\x05\xdd2\xb1\xcc(J)\xad\xaa6\xa2\xbeY\xb2\x8c\xaf\xa6O\u007ft\xea\xd4Oo\a\xe8\u0673\xb73c\xc6W\xde\xc2\"\x0fO\xcc=<\xea\xe5W\b\x9ey\xe6\x19\r\xa0u~\xfe\xdaF\x8d\x1b\x03\xf8\xa5m#\x1d\x87\xe2]\x9b9\xb4s\x13\xbe@\xbd<\xd7GR\xc41.\xbdAz\v\r&G\xd5\u047fQ\xf5r\xee\xfe\xa4\t\x01\xca!V\x15%-\xa7\x19c\xee~\x8e\xeeg\\T\xef\u042dh\x18)\xa5\xa8\xac\x8d\xb0h\xf1R\xbe\xf8\xe2\xabG\xe7\u039d5\x01\xe0\xb4\xd3NW\x0f<p\x9fX\xbdz\xa5w\x10=<1\xf7\xf0\x00\xc8\xcfo)\x01.\xba\xf0\xbc\x95\xe9\xe9\x19\xbb}\xfe\x80+\xbfJq\xa4h\x0fE\x1b\v\xb0M\xd0u\xbd\x81\xcbv\xe5\xf9\xe8\x9a\xd0:\xbb\x1e\x97\xeacD\xfd\xa8\x84\xcb\xe3\x025Z<\x16\x1f\xae\x8a\x92\x9c\x95\xc1\xd9w=\u0349\xa7]\x00\xb8\x93\xa2V4\x8cr$\xe5\u0575\xcc_\xf05\x1f~\xf8\xf1\x13\u02d6~\xd3\x15\xe0\x9e{\xeeU_\u007f\xbdH\x1c<\xb8\xdf;\x88\x1e\x9e\x98{x\x9cr\xca)\n \xb5I\u01b7\x1d\u06b7\u06d9\x95\x95\x05\xa0\xa4ccFj)\\\xb3\x8c#\xfbv\x11\b\xf9Q\xaa.\xa5\xa5\ue98e\xae5\xaa_\x19ZW\x95+.\xe0\xf1\xa5\xfeu\x81\x96\xfa\xfctE\xbcD\x80[N7Z\x15%5\xbb\x19g\xde\xf54\x9d\x86\x9fsT\xd0\xcd(JA\u0251\n\x96._\x99\xf9\xde\xfb\x1f\xbe\xab\x94\xca\x03\xb8\xf9\xe6[\u051bo\xbe\xe5\x1dD\x0fO\xcc=<\x92\x93S\xea\u007f\xee|B\x87\u03dbff\x02\b\xe56\x98`\xef\x86\x02\xf6\xac]\x1e\x17]\xcd\xf5\xe4B\x1d\xe3\xb0\x05G\x8bn\x1d\x1bEw\x05\xdd]\x15\x1a\x1f\x9c\xc2\x1d\xa0\r\x9f\x03\nM\x83hu\x94\xf4\xdcl\u03be\xebiZ\xf5r\xf3\u042dh\x04;\x16E*\xa5\xf6\xec\xdb\xcf\xeao\xd7ty\xe4\xe1\x87\xea\x15\u0732L/~\xee\xf1\x8b\u015b\xed\xf7\xf8\x97\x98:u\u06bey\xf3\xe7\x8f(\xda\u007f 3\x16\x8d\x80t\xb0bQ\x1c\u01e1y\xe7\xde$\xa7\xa5#m\xbbA\xce9GeY\x88\xfa\xf4E\x94:\xb6\xbcb\xbd+\x17\xf5\xa1\x97\xba\x12\x00uN]w\xb3\x16\xb1mh\u04bc\t\xcd\u069e\xc4\xfe\xf5\xab\xa9*9\x80\xe3\xd8h\x9a&\x10B\x95\x1d>,L3\x9a\xf7\u007f7\u07980{\xf6\xec\xb9\v\x17~\r\xc0k\xaf\xbd\xca\x17_|\xe1\x1dD\x0f\u03d9{\xfcz\xb9\xe1\x86\xeb]\xc1\x15\xe2\u0420\x81\x03\x16d\xba\xee\xdc\xcd\x15\xb7,\xf6|\xbb\x94\xdd\x05\x8bQJa\xf8|GE\xbb>\x98\u00b1\xf1\xf3\xfag\x1f]`TW\xac\xcbQ\x02[\x1d\x9f\xc4\xe8.0B\b\x94c\x13\xaeth\u0465\vg\xdc\xf9\x14i-\u06c0RX\xb1\bJ\xda\xc2r\xa4Z\xb7~\x13\x05\x05\x05\xb7\u035e=}\f\xc0\x94)\x93\xf5\xab\xaf\xbe\xc6;\x90\x1e\x9e\x98{\xfc\xba\xb9\xf6\u06ab\xeb\u007f>}\xf4i\x9ft<\xa1\x83\n%$\xba\x8a\xac$\xe1\x8a26\xcd\xff\x9c\u0292\x03\xf8\x03:\xba\x00\xedh\xbb\x8a\x06\x01\x95\x06%\x15\x1b8\xf2\xba`\x8a#AJ\x15\xaf\xaa\x18\xbf\xc5\v\xbe\xd4M\x90\n\x01\xca6\x89\xd5Z\xb4\xedw2\xa7\xfd\xfea\x12S\u04d0\x8e\x83m\xc6\x10(Q\x13\x8eX\x9b6oe\xc6\xf4YW\x01\x9c{\xee\xf9\xce\u0295\u02fdq\xef\u1279\u01ef\x9bn\xddz0y\xf2\xc7\x00\xe4\xe7\xb7^\u04a3[\xb79\xd99\xd9\xf5Z,m\x8b\u00b5+(\\\xbb\x1c!\xc00\f4\xa1\x8e6\xach \xe0\xa2\xc1?nhE\"m\a\u01f4pL\x13\u01f6\xdd>\xa2\xf1a\xea\x98Ql3\x86m\xd9HG\xba\xb1u!P\x8e\x85\x15u8q\xf4\xf9\f\x9ax\v\bp,\v\xdb4\xd1\f\xdd\xd8Y\xb8\x97\xad\xdbw\x8c\xfe\xf8\xe3\x0fo\x04\xe8\u077b\xaf\xfa\xee\xbb5\xde\xc1\xf4\xf0\xc4\xdc\xe3\xd7\xcd\xf9\xe7_P\xffs\xcb\x16-\x1e\xec\u043e\x1dqw\x0e@e\xf1~\xd6N\xff\x98\u02b2rt\xbf\x8e@\xb8\x0e\xbd~y\u007f\u0709\xd7?\xa6PR\"-\xcb\x15k3\x86c\x9b\x14\x16,a\xf6Sw1\xf7\xaf\xf7rp\xdb\x06\x84\xeesE:\x1a\x8d\xf7\fu\x90\n\x84\xa6\xe3X&\n\xe87\xfez\xba\x8d\x1eW/\xfe\x8ei\nG*\xb9u\xfbN\xbe-\xf8\xf67J\xa9\x96\x80\x9a9s\xb67\x19\xea\xf1\x8b\u009b\x00\xf5\xf8\x97\x989s\x86x\xf7\xdd\xf7\xf8\xec\xb3\xcf\x0f.[\xb6\xb4WQQQ\x9b\xca\xca\xca\xfa\xdf\xd7\x1c)\xa1i\xab\x0e4?\xa1\x13\x8eS\x97\x9a(\xe2\xf9\u3abe\xad\x9c\x06(i#-\vi\xdbH\xdbB)\x89c[\xac\xf9\xfcmV|\xf0\x02\xfb\xd6-\xa7t\xf762\xdbu%9#\x13;\x16E9\x0e2\xde$C\x17\x02M\u05f0-\x8bPr\x12\xa9-:\xb0k\xd5\"j\x8f\x94 \x1d\x1b\xc3\xf0\x8bp4\x8a\xa6\x91n\x9b\x91\xa2i\u04fe\\6g\xce\\f\u039c\u03bb\xef\xbe\xe7\x1dL\x0f\u03d9{\xfcz\x195\xea4w\xe1\xbd\x10V\x97\u039d\xa6\xb6i\u075a@0T\xff\xfb\xda#\xa5\xac\xfd\xea#*\x8b\xcb\xf0\x05\xf4z'\xae\x8b\xa3\v\x88\x04\xa0\xa4\x04\xdbF\xd9\x16\u04b6\x90\x8e\x83\xb4,t\xdd 5;\x8fPr*\x00\xbbW.\xe4\x9bIOR]V\x8c\xee\xf3!-\x13\u01ccaG\xa3\u0626+\xee\x9a\xd00\xc3&M;t\xa6\xcfe7\xa3\xf9\xfc()1c\x11\xa4\x92l\u0676\x83\xd5\x05k\xae\t\xd7V\xe7\x02L\x9c8\xd1s\xe7\x1e\x9e3\xf7\xf0\xa8\xe3\x8b/\xbe(.(X5\xbch\xff\xfef\xb5\xe1\bR:\x00T\x1c\xdaKJ\xb3\x1cZ\x9e\xd8\vi+\x94\x92\xf5\x8b\x81\xa4\x02\xa9\x14\u02b6Q\xb6\x8dt,\x1c\xc7A92\xbezT\x91\x9e\xd7\x16\xcd0(\u0779\x19\xc74)?\xb0\a#\x98@\xf3N'!m\x1b%%B\xc9x-\x18\x88w\x95F\t\x1f)\xf9\x9d)\u07fb\x9d\xd2\xed\xebQn\x03j,[\xe2\xd8vZ\xf9\x91\xb2\xb2\xf9\xf3\x17|SSS\x8bR\x92\xfb\xee\xbb\xcf;\x88\x1e\x9e3\xf7\xf8\xf5\xb2c\xc7\u05b89\xd7\xf6\xf5\xec\xd9\xf3\xf5.\x9d:\x11\b\x06\xd0\r\x03\x003\\\u02ea\xcf\xdf\xe1\u040e\x1d\x18A\xb7\x00\x97T\x10\xb3%Q\xcb\xc1q$B)7\\\"\x15\x9a\xaa\x13g\x89c\x99\x18\xfe }\xc6]\xcf\xc0\x897\u04e8i6V4\xc2\xe6yS)Z\xbf\x1a\xdd\x1f@96R\xd6M\x9a\x9a(+\x86\x94\x92X$\x8a\x11\xf4\xd1k\xfcoIHM\a\xeaV\x88\xc6\xd8[T\xc4\xe6-[\xafUJ\xba+\x9e\x84\xf7\x15\xf0\xf0\x9c\xb9\u01ef\x9c\xe7\x9e{\x9e/\xbf\x9cf\xbc\xff\xfe\a\xf2\xb3\xcf>\xdf\xf5\xdd\xfa\xef\x86\x1e<x(\xbb\xaa\xaa\xcaQRjJ)*\x0f\x15\xe1\x0f%\u043a\xcf0\x14\x02\u06f6\xb1\xe2\xe9\x85~\rt%\x91\xb6\x8d\xe3\xd8 \x9dz\xc1\x17\n\xa4c\xa3\x19\x06\xd9't\xc7\x17J\xa0d\xc7F\xaa\x0e\x15\xa1\x80\xecN=1\x82!pl4\r\x10\xae\x9b\a\x81\xe9\x80\x14\x06\x8d\x9b\xe6Pyp/\a7\xaer\xdd~<\x97\xc6\xd0\xf5\u0186&J\xa7N\x9d\xb6\x04`\xf1\xe2EL\x9a\xf4\xa6w@=<g\xee\xf1\xeb\xe5\xf4\xd3\u03f4;th\xaf\v!JO<\xb1\xdbcy-[\u0621PH7|>Y\xe7zWN\x99\u0136of\x12Lr\x1d\xbbO\x17\x04}\x1aZ\xbc\u035c\xc2\x15b'\xde4T(U\xbfX\u050aF\x90\xb6M\x97Q\xe7\xd3\xed\x8c\xf1\x18\xc1\x10;\x97\xcfg\xc7\u04b9h\x9a\x8e\x00\xa4\x94\u060eD:\xd2\x15w\xe5\x80\x19!\x10\xd4\xe8|\xda8B)\xae;\x97\xb6I,\x16\xa5\xb8\xb8\x84\u056b\v\xaePJ5\x05\x188p\x90w =<1\xf7\xf8al\u07bc\xe1\u07dal[\xb3\xa6\xe0\u007f\xb2\xea\x9f\x10\x82\u0673g\xba\xd5\x14/\xbaxr\xabV\xf9S\xf3Z\xb6@\xd34G\xb8\xa5\x0e\xa99\\\u02827\x9e\xe4\xf0\xde\x03$$'\xa0\xa1\u0705D\x02\x948Zd\xcb\xed\x17\x1a_\xbc_W\x17W)\xcch\x18!4:\x8f<\x8f\xfc\x9e\x83\x88V\x1da\xc3\xec)\x1c.\u070e\x11\f\xe1H\x85\x94\xf1\xd5E\xd2A96\xb6\xe3\x10\x8bJ2\xdau\xa5\xc3\u0433\u074dU\n\u01f69|\xa4\x9cM[\xb6\xb4\xff\xec\xd3O\xeas,\xa7O\xff\xd2\x1b\xa4\x1e\x9e\x98{\x1c/\xdc\x1b\xffN\xb8;v\xec\xac\xfe\x9d\xd7\xec\xde\xfd$\x95\x95\x95\xf3w\x8f\xef\u06f7\xe7\xbf\xfaY\x8b\x8a\xf6\u04a2E\xbe\x9a>\xfdK\r\u0b33\u039a\x94\x97\x97\x17m\x94\x94\xe4\xf3\x19>U\x97W\xbec\xf9|\xbe\x9e\xf4$2f\x13LH<\xdaRN\xd3\x11\xba\x01B\xe0H\x85-\xdd0\x8b\x8aOl\xd6aE#4JoF\xd7\xd1\xe3Hm\u078aC\u06fec\u04c2/\x88\x86k1|~\x90\x12\u02d1\x98\x96\x83m\xdb\b\xe9\xe0\xc4\xc2\x04\x1b5\xa6\u07503\xf1\x05\x13\\w\xee\xd8D\xa2QJJ\xcbX\xb0p\u163a\xd7\x1f=\xfa\fo\xe0z\xfc\xac\xf1R\xb3\xfe\r\x94R\u031f?W[\xbbv\x9d\xb6k\xd7N\xfd\x85\x17^\x8a\x1d\xf7\xfb\x04)\xed\xc0\u0739s\xd2g\u03de\xa3L\xd3N\xea\u07ff\uf165\xa5\xa5I\u06f7\uf215\x96\x96\x12\x0eG\xb0m7\x93C\xd7u\f\xc3 11\x91\xa6M\x9b\x92\x97\x97\xe7\xcf\xc8H\x0fo\u077au\xea\x91#\x87\xcbN9\xe5\x14\u0577o\u07f2\x9c\x9c\x16\xa6\x10\"|\xdc\xe6\x18\x8f=\xf6\xa8\xc8\xc9\xc9v.\xbe\xf8\x12)\xc4\u007f\xef\xd0>\xf8\xe0\xfd\x0f|\xf5\xd5\xf4\xbb7n\xdeJ$\x1cV\x96e\n\xe2\x19'g\xdd\xf9\x17\x06^z\x13\b\x81m\u0190\nl\xd3$V[K4\x1aA\xd96B\x88x\xfdrw\x88\xaa\xb8\xab\xd6t\x1dG\x18,\xfb\xf8u\xd6M~\x89\x84\x944\x86\xff\xdf}\xb4\xee=\x18+\x16\xc1Q\x02\x89@7\f\x94\xe1G\xe9>\xfc\t\x89\u012a\x8e0\xe3\xc1\xeb\xd9<\xf73\x00t\u007f@\xa56N\x16}\xfb\xf4\xae\xbe\xf7O\xf7\\\u052bw\u07ef\x00\xbe\xfbn\r]\xbbv\xf7\x06\xb6\x87'\xe6\xbf&\xbe\xfa\xea\v\xed\x0f\u007f\xb8\x85\xad[\xb7\xca\x06\xe2ml\u0630\xb6\xcd\xe4\u025f\xa4WUU\xb7\xf6\xfb\x03\xc3\xf7\xed+\xca\b\x04|\x03\x0e\x1e<\x94\xb4\u007f\xff\x01\xc2\xe10\xb1X\x94X\xcc\xc4i\xb0\xf0E)U_\x94J\xd34\fC\xc7\xe7\xf3\xe1\xf7\a\b\x85\x82dee\u046cY\xb3\x9ah4\xba8''\xa7\xaci\u04cc-J\xa9UC\x86\f\xd9;h\u0410\"!DM\xddv\xa4\xa5\xa5j/\xbe\xf8\"\x17\\0\xee\xbf\"\xeaJ\xa9\x8c\x1b\u007f\xfb\xdbY\u04e7\xcf\xe8^\\\\\x8c\x15\x8bb[\x16\x00\xa1F)\x8c\xfd\xd3_\xe9u\xee\x04\xa2\x11\a\xe9\xd8\u0636\x85Y[K,\x12\xc1\xb6,\x84R\b];Z\x90K\xc5\xc3/Rb\x1a\t\x1c\u06b7\x97o'=\xc8\xfe\xb5\x8bi?\xf8L\x86]\u007f7\xc1\xe4\x14b\xd1\x18a\a\xfc>\x03\u007f\xc0\x87\xa3\xfb\xd14\x8dPJ\n\x05\x1f\xbd\xca\xf4\a\xae\a%\xd1t\x03\u007f {\x9d\xd4C\x1b}\u06a8G\xee\xbc\xeb\x8fwy#\xda\xc3\x13\xf3_\t\xf3\xe6\xcdf\u0630\x91L\x9a\xf4\x12\x13'^W/\xbcJ\xa9\xd0\xec\xd93\xfb\u035a5+\xaf\xa4\xa4\xb4\xb7eY\xa7TTT\xb4\u06f3\xa7\x90\x92\x92\x12,\xcbB)E0\x18\xa4q\xe3\xc6$''\x93\x9c\u0708\x84\x84D\x12\x12B\x04\x02\x01t]G\xd7uw\"\u03f6\x89FcD\"a\xc2\xe1\b\xd5\xd5\xd5TUUQQQA4\x1a\x05\xc0\xe73h\u04a4\t\xf9\xf9y4i\x92V\u0628Q\xa3e\u035b7_\u06f6m\x9b\xe5\x13&\\\xbaR\b\x119\xea\x92\x1f\xc4q\x14\xfd\xfa\xf5d\xe4\xc8\xd3~\xd2}t\xf7\xddw\xf1\xe0\x83\x0f\x030k\xd1\xe2S\x9e{\xfa\x99\x99\u02d7.\xf6\xd7VWc\u01a2\xc8x\xbewjV.\x17<2\x89\x0e\x83\x87\x13\xad\xb5\xb1\xad\x18v$\x82\x15\tc\x99\x16\x8eTG\xfbV4\xa8\xb4(\x95\xa2*\xea@0\x89\x03K\xbed\u065b\x8f#\x1d\x87\xc1W\xdfA\xe7\x91c\x89DbT\xc7,\x82>\x83P\u0407\xd2\xfdh\x86N\xa0q\x13\x0emY\xcb\u053b.\xa3x\xdbw\bM\xc3\x1f\bZ\xad\xf3\xf3|C\x86\f\xfa\xfa\x85\x17^\x1a%\x84\x88\x1e<\xb8Ode\xe5*o\xb4{xb\xfe\v\xa4\xa4\xe4 M\x9bf\xfd\xdd\xe3{\xf7\xee\xea2y\xf2\xa7\x97-_\xbe\xbc]MM\xcd\xe0\x8a\x8a\x8a\xe4\xad[\xb7Q[[\x8b\xae\x1bdee\u04be};\xf2\xf2\xf2\xc8\xc9\xc9&++\x8b\x8c\x8c\f22\xd2INN&!!\x81P(\x84\xdf\xef\xbaGW\xcc\x15\x8ec\x13\x8b\u0148D\"D\xa3Q\xaa\xaa\xaa)++\xa3\xa4\xa4\x84\xe2\xe2b\x8a\x8a\xf6SX\xb8\x97\xed\u06f7\xb3\u007f\xff~b\xb1\x18\xc1`\x90\xb6m\u06d2\x9c\x9c\\\x15\f\x06\xbe\xee\xd9\xf3\xa4\xcdc\u01ce\xfd\xb8{\xf7\x93\n\x8e\xdf\xee\u077b\xb7\x93\x9f\xdf\xf6'\xd9W\x05\x05\xab\x985\u007f\xa1\xb8\xeb\xd6[\x15\xc0\v\x93\xdez\xf1\x93\x0f?\xbc~\xf5\u0295\u0122\x11e\u0162\xa2.\x0e\u07b2[\x1f\xce}\xe0Ur\xbbv%R\x15\u00caF\xb0\"\x11wE\xa7\xe3\xb8\xcd+\x94jP[\xd1Mo\x89\xda\x12\xcd\xf0#\xec\x18\x8b_\u007f\x94Ms>#\xf7\u013e\x8c\xb8\xe9A\x82\xe99T\xd7F0\f\x9d\u0120\x81\xe1\xf3#u\x1f\xba?\x00\x02f>\xfc\x1b\xd6|\xf6\xa6+\xe6\xfe\x80\u04f8qc}\xc8\xe0\x93\xf7>\xf5\xd4\xe3\x17\xe5\xe6\xe6/\xfd\xfc\xf3O\xc59\xe7\x8c\xf5\xc4\xdc\xe3g\x89\xe1\xed\x82\xefg\u0294\u027c\xf3\xce;\xa2i\xd3,u\xdc\xe3\xe7}\xfe\xf9\u0511\x97]v\xc5\u041a\x9a\xda\xd6;w\ue92a\xaa\x8aF\x8d\x1a\xa9.]:\x8b\x9e=O\xa2{\xf7\x1e\xe4\xe7\xbb\"\x9e\x91\x91Abb\xf2\xf1\x81\b@\x1d3\xc9W\xf7\xbf\x887np\xd3\xfa\xfe\xbe\xf2w8\xec\x8a\xfb\xc1\x83\a),\xdc\u02da5k((\xf8\x965k\u05a8\x8a\x8a\xaa\u4924\x843\xf7\xef\xdf\u007f\xe6\u0085\x8b.\xbc\xfc\xf2\xcbV\x8c\x181b\xf2\xc5\x17O\xf8\xa4\xee\xf9uB>s\xe6tF\x8d\x1a\xfd\xa3\uecd3N\xeau\xd4J\x037L\xbc\xec\xee\u28bd9\xd5\x15G\xce\u06bcy\xb3@\xf91c&\xa0(\\\xb7\x82\x99O\xdf\xcd\xd8\xfb^\xa2I\xf3\x1cl\xdbF7,\x94\xae\xa1\x94\x83-\x05R\xe0\x86\\\xe2\xcbF\x95\x02\xbf\xae\xa1\t\a\u007fr\n\x1dO9\x93\x03\x1b\v8\xb4e-[\xbe\x9eA\x971Wa\xe8\x1aR),[\xa1\xeb\x0eB\xd3Q\x8eE(5\x85\x8cV\x1d\xdd}-%RJ-\x12\x8b\x12\xae\xad\xcd]\xf4\xf57\ud065;v\xec\x10\r\xb7\xdf\xc3\xc3\x13\xf3\x9f1_}\xf5\x05\x8b\x16-\x12\xe7\x9e{\xfe\xd1\xc6\xf1J\xa5\xbd\xf9\xe6\xeb\x17O\x9d\xfa\xc5\x19\x0f>\xf8\xf0\xc9ee\xa5\xc1\xe2\xe2\x12\xfc~\xbf\u0763G\x0fN=u\x84\u07bbw/\x91\x9f\u07caf\u035a\x92\x94\u0538^\xb4m\xdb$\x1a\xad=&3\u37c9c\x1f\x15{\xb7\xff\xa5\xdf\xef\xa7E\x8b\x96\xb4h\x91O\x9f>\xfd9\xf3\xcc3(--e\xf7\xee=b\xe5\xcaUj\xee\u0739\xce\xea\u056b\u067auk\xcb\xed\u06f7\xb7\\\xbd\xba`\xf4\x84\t\x13\xee9\xf5\xd4\x113&L\xb8\xf4q!\xc4\x11\x80Q\xa3Fs\ubb77\x88!C\x06\xab\xd3O?\xf3G\u0747u\xf1\u007f!\xc4\x11\xa5\u0504CE\xfb>\x8aE\"\xa7\xed\u06bd\xcbM54-@\xb1i\xc1\x17$7\xcba\xf4\x1dO\xe3OL$b\x9a\b\xc3@H\x89\xa6\x1c\xa4\x12G\xd3\x14\x1b4\xa9PRa\x9bQ\xb2;\x9dD\xab\u07a7\xb0\xf6\u02ff\xb1c\xd9\x1c\x9au?\x99\xf4\xbc\xf6\u0122\x11L\xdb\x01M#\xa89 u\x90\x90\x9c\x99K09\x95hU9JI\x11\x8b\xc6,\xdbq|\xc5%\xa5\x1d\x00\xaa\xaa\xaa\xbd+U\x0fO\xcc\u007f\tTU\x95\x8b\xe4\xe4\u0506mox\xf6\xd9g\xae\xba\xf2\xca+\xee[\xb6ly\xf6\u07bd\x85\x84\xc3\x11\xb2\xb3\xb3\xcdq\xe3.\xd0/\xb8\xe0|\xa3{\xf7\x1edd\xa4\xe3\xf3\x05\x01\xb0\xed\x18\xe1p5J)4M\x8b\v\xb7@\xd3\xfe5\x9d8^\xf8m\xdb\u01b2,\xa4\x94\b!\xf0\xf9|\xb4h\x91O\x8b\x16\xf9\f\x18\xd0_\\~\xf9\xa5\xc6\xfa\xf5\x1b\x98:u\xaa3g\xce\\\xb9}\xfb\x8e\xa4\xfd\xfb\xf7w]\xb3fM\xd7\xf9\xf3\x17\\\xfe\xfa\ubbfd|\xe5\x95W\xdd/\x84\x90O<\xf1\xa4z\xe2\x89'9r\xa4T4i\x92\xf1\xa39R!\x04J)\xee{\xf0\x01\x9f\x10\xa2z\u03de\x1d\xf7\xff\xe5/\xcf\f\xa8\xae\xaeJ.--SJ)a\xdb6J)\x96\u007f\xf82\xfe\xe44\x06\xdf\xf0gT\xa0\x11\xb1\xa8\x89\xc2F)\a\x15oN\xd1\xf0\xe2\xa4.\xd5Q:\x0e\xbeP\x02\xed\x06\x8f\xa6p\xcd\x12\xcavof\xd7\xf2\xb9\xa4\u4dad\x0f\xc98\x8e\xc4r\x14>]\"\x1dE\xa3\xf4L\x12\x1a7\x89\x8b\xb9['\xecHy9\u06f7os\xdc\x10\xd4n\xefK\xe0\xe1\x89\xf9\u03dd\x993\xbf\u0493\x93S\x9d\xb8\xb3\f=\xf3\xcc3#\x97.]r\xe3k\xaf\xbd>\xa2\xa8\xa8\x88H$B\u01ce\x1d\x195j\xa4\x1a3f\x8c\xbf[\xb7\xae\x04\x02\t\xf1g;XV\xf4hIV\xfd\xa7\xab\x92P\x17\x86q\x1b&\xbb\xab\x1fc\xb10B\xb8\xae=##\x93\xa1C3\x192d\xb0\xbe{\xf7n\xfd\xf3\u03e72m\xda\x17j\xed\xdau\xe2\xc0\x81\x03\xcd\n\n\n\xee\x9d5kV\xffg\x9e\xf9\xcb\xf37\xdd\xf4\xfb9B\x88H\x93&\x19j\xe6\xcc\xe9\xfa\xa8Q\xa3\x9d\x1fs;\xcb\u028a\xed?\xdf\xf3'\xf2\xf2\xda,_\xb4h\xc1\x05EE\a\xfe\xb6d\u0252\x8cp8\xecD\xa3\x11\u0772\x1dP\x8aE\xaf>\x04\xbaA\x9f\x89w \x83\xc9D\xa2&8\x02\xa4[tK\b04\x81\xae\xc5\xc3N\xc2]d\xe4\xd8\x0e\xe9m:\u04e2\xd7\x10\x8e|6\x89}\xab\x16\xd0\xf2\xa4!4m\xd3\t\u06cc`\xa1\x10\x8e\u0110\x12\u01f2\t5iJ0\u0794Z\u0157\xf7G\xa31\x8e\x1c)\a\xa0\xbc\xbc\x9c\x993\xa72j\xd4\xd9\xde\x17\xc2\xc3\x13\xf3\x9f\x1b;vlc\xc0\x80\x93\u0168Q\xa7;\x00K\x96|3\xec\xe6\x9b\u007f\u007f\xc7\xe2\xc5K\x86o\u0672\x95H$\xc2\t'td\xec\u0631j\u0738\vD\xbbv\x1d\xc5\xd1\x10J\f\xc7q\xfe'>\x87R\x8aX,V\u007f21\f\x1f\xad[\xb7\xe3\xe6\x9bo\xe5\xe2\x8b\u01cb\xa9S\xa71y\xf2dV\xacX\xc9\xee\xdd{F\x14\x16\xee\x1dQX\xb8w\xee\u0085\xf3\x1e\x1d2d\u063cQ\xa3F;-Z\u42af\xbf^\xa8\xf2\xf3[\xff(\u06d4\x9e\xdeL-[\xb6X\xf4\xeb7P\r\x1at\u02ac7\xdex\xedV\u04cc\xbd\xb5r\xe5J\xddql\th\x96e\x03\xb0\xe8\xa5\xfb@)\xfa_u\x17\xaaq\x1a\x91#%8\xb6\u0114\uef02O\x13\x04\r\x1d]S\xf5\u92a6i\xa2\x05\x13\xc9:i\b;\x96\u03a6\xa2p\x1b%\x9bV\u04fcCg\x94\xa90\xa5\xc4q$\xd2qP\xd2\u0097\x90T\xbfx(\xfe\x12H\xa5\x88F#\x0e@ff3O\xc8=~\xb6\xfc*c\x84EE{\x99:u\x1a7\xde\xf8\u007f\r\xc50\xf9\xa6\x9b~{\xe5\xb6m\xdb\xfe\xbca\u00c6\u48a2\xfd\xe4\xe66g\xfc\xf8\xf1L\x980\x9e\u039d\xbb\u015d\xb0uL~\xf8\xff\xf4\xc1\x15\"\x9e\xf6\xe8\a\xe0\xc0\x81}|\xfc\xf1d\xde~\xfb\x1d\u05ad\xfb\x8e\xec\xec,\xbat\xe9R\u0561C\x87??\xfd\xf43o\b!\xaa\xea\x9e\xfb\xf6\u06d38\xe5\x94Sh\xd1\"\xff\xdf\u078e\xab\xae\xba\x92\xd7_\u007f\x03\x80G\x1ey\u8669S\xa7\u0774e\xcb\x16,\xcbR\xa6i\t+\x9e\x83\x0e\xd0\u007f\xe2\xad\xf4\x9dx;\x18A\xc2e\xc5\u0636\x8d\xe9Hl\x05\xba&\xf0k\xe0\xd3\xdc@\x8a\xa3\x14\xd2\x1f\xa2\xb6\xb2\x825o?\xce\ue15f\x93?`\x14C\xae\xb9\x93\xa4\xd4tjL\x1bt?\xc1\x80ABR\x12\xb6\x19e\xca\xed\x17\xb3s\xe9\x1c4]\xc7\b\x84\x9c\u071cl}\xf4\xa8\x11\x9f>\xf7\xdc\xf3\x13\x84\x10\x91\xc2\xc2]\xa2e\xcbV\xde$\xa8\xc7\u03ce_\xe5r\xfeo\xbeY|\x8c\x90/^\xbc\xa8\xef\x84\t\x17\u007f5s\xe6\u033f\u031a5;\xf9\u0421b\u018c9\x87\xd7^{\x95G\x1f}\x84\u039d\xbba\xdb1b\xb1p}\xbc\xfa\xe7\x80R\n\u06f6\xe3\xdb\x1d%;;\x97\xdf\xfd\xee\x0f\xbc\xf1\xc6k\xdcp\xc3\xf5\xc4b&s\xe6\xccM\x9e1c\xc6_.\xbf\xfc\xb2\xaf\u05ac)\xe8[\xf7\xdc\xcb.\xbb\x82\xa5K\x97\xfd(\xdb1n\xdc\xd16sw\xde\xf9\xc7\xdf\xf5\xee\xddkR\u02d6y\x18\x86!\x02\x01\xbf\xf2\xfb\xfd\xf5s\x03K\xdf|\x82yO\xddL\xf8\xf0A\x92\u049a\x12\xf0\x1b\x18\x9a\xc0\xd0\x04R*\xa2\x0e\x84-E\xad%\x89\xd8\xeed\xaa/)\x85f\x9d\xfbb\x84\x12(\u0771\x9e\x8a};\t\x04\x02\b\xe9\xa0\xe2\xee\u0736\x1d@\xa05\x98\x83\xd04!\"\x91(MR\x9bt\x06\xda\x03\u0636\ud578\xf0\xf0\xc4\xfc\xe7\xc0\xa2E\v\xc5E\x17\x8d\xaf\xbf\xff\xf8\xe3\x8f]\u007f\xd7]w\u007f2k\u05ac\x81\u06f6m'??O>\xf2\xc8\u00fc\xfc\xf2\x8b\x9cz\xeai8\x8eM$R\xf3?\x13N\xf9W\x91R\x12\x8d\xd6b\x9a\x11z\xf4\xe8\xc5\x13O<\u018b/\xbe@\x9f>}\xe4\xb6m\u06d81c\xc6\xc0[n\xb9e\xf2s\xcf=\xf3\x1b\xa5T\xc0\x15\xe1\xf1\u03181\xfd\xdf~\xef\xe1\xc3Oe\u0294\xc9\xf5\xf7\x9f}\xf6\xafw\x9e}\xf6Y\x85\xb9\xb9-\x10B\b\xbf\u07e7|>_\xfd<\xc0\xba\xa9o3\xeb\xa1\xeb)\u07bc\x8a`\xe3&\x84BA\fA\xbc@W}).\xb7\xe2\xa2\x02#\x10\"\xa5U'\x92\x9a\xe5R{\xb8\x98\x8a};\xb0L\xab\xfe\xaf\xed\xf8D\xaa\xb2-l\xdb>\xbaO\x14*\x10\nRU]\xb5\x1d\xd8\x1d\x0fQy\xae\xdc\xe3g\u026f*f>o\xdelm\u0420!2\xeeZ\x1b\xdd~\xfb\xad/\u007f\xf4\xd1\xc7\xe3\v\n\n\x00\x9cQ\xa3N\x15w\xdcq\x9b6x\xf0\x10\xc0\xcd\xe9\xd64\xad^d~\xf615!\x90R\x12\x0eW\x13\f\x069\xff\xfc\v\xe8\u05ad\x8b\xf6\xdcs\xcf\u02f7\xdf~G\u035b\xb7\xa0yuu\xcds\a\x0e\x1c\x1c\xa0\x94\xbaB\b\x11>\xed\xb4\xd1,]\xbaX\xeb\xdf\u007f\xe0\xbfu9r\xee\xb9\xe7S^^Fjj:B\x88\x92}\xfb\xf6^\\YY\xf5\xf1\xb4i\u04f2\x8b\x8b\x0f\t\xbf\u07e7\x84\x10\u00b2m\xa4\xe3\xb0{\xd6vD[\x00\x00 \x00IDAT\xf9\\j\x8a\x8b\xe8?\xf16\xf2\xfa\x8e !1\x89\xda\xdaZ\x1c\xe9\x80\x10\bM\u00f6lJ6\xad\xc0\xb1LR\xf3;\x92\u07be\a\x15\x85\xdb(\u07f7\x93hM9\x9a?\x19G)lGaK\x90\x910\x8e\x19\xab\xdf\x17\x80\n\x85B\x1c8pp\xa3\x10\xa223;K\xcb\xcd\u0353\x9e,xx\xce\xfc\u007f\x98\xd7_\u007fM\x1b6l\xa4\x04X\xb1bY\x9f\xf1\xe3/\x9a\xf5\u99df\x8d/(( 11Q\xddv\xdb-\xda\xdf\xfe\xf6\xb66x\xf0P\x00\xa2\xd1\b\xba\xae\xf3\xdf,X\xf5S\t\xba\xae\ub626\x89\u3634k\u05d1\xe7\x9e{Z{\xfa\u99f4\xbc\xbc\x96j\xe5\xcaUL\x992\xe5\xc2\xf3\xce;\xf7\xf3\x82\x82\xd5]\x00\xfa\xf7\x1f(?\xf8\xe0\xbd\u007f{\xac\xa4\xa6\xa63c\xc6W\x1a@nn\x8b%c\u01desf\x8f\x1e=\xf64i\x92\x86\x10B\x05\x02~\xfc>_}6P\xe9\xee-\xcc|\xf4&\x96\xbf\xf98\xe1\xb2\xfd$%7\xc6\xef\xf3#\xa4D\xd7t\x90\x0e\aV\xcfc\xcd[\x0f\xb3o\xe9t\x1ae\xb6@\xf7\xfb).\xdcAuy9\x9a\u03c7\x94\n)\x1d,\x05U\xe5e\xc4j\xea\x9aN\v\xea\u02a6\u06cek\xd7'\x8c\x1f\x1f\xb8\xf3\xce;|g\x9ey\x86~\xe3\x8d\xd7\ubbfe\xfa\xb2>g\xce,}\u04e6\rZ\xc3u\x02\x1e\x1e\x9e3\xff/\xf1\xc8#\x0f\x8b\xab\xae\xbaZ\x02|\xfc\xf1G\xe7>\xf1\u0113o-X\xb00\xe9\xf0\xe1\xc34o\x9e\xa3\xee\xb8\xe3vq\xe3\x8d7\x00:\xb6\x1dsK\xa8\x8a_\xf6\u0730\x10n\xd7\x1f\xc7q\xf0\xfbC\\u\xd55\"77\x97\xbb\xef\xbeG\xae^]\xa0UTT\x8e\b\x85\x9e^2}\xfa\x97\x13G\x8f>c\xcaE\x17],\x9f}\xf6\x19q\xd3M\xbf\xfb\xb7T\xed\xb4\xd3N\x97\xef\xbf\xff\x9e\x18?\xfeb5x\xf0)\u07fe\xfa\xea+cm\u06da\xf2\xed\xb7k\xf2\xcb\u02cfH\xc3\u0405\x10BX\x96\x1b\x12\x89\x85\xabY\xf5\xc1\xf3\x14o]G\xb7\xb1\u05d0\u0575?\xbe\xe4T\xccH-\t\t\x89d\xb4\xeeD\xe1\u0499\xec\x98\xfd1M\xf2;\xe0\v%RUz\x90\x8a\xcaJ\x82\xb9\x06J\xc4\xdcu\xb4B\x10\xa9\xac \x16\xae\x89\u007f~\x90\xd2\xc1\xef\xf7\xd3(\xa9Q-\xc0\x93O>\x15\xf9G\xdb=p\xe0@\xfdO\u007f\xba\x1b\u01d1j\xf8\xf0a\xea\xe4\x93\a(\xc3\b\xb2g\xcfN\xf2\xf2Z{J\xe2\xf1\xdf\xffN\xff\x92?\xdc\xef\u007f\u007f\x13\x8e\xe3\x88\xe7\x9e{^\x01<\xff\xfc_\xc7N\x9b6\xed\u0765K\x97\x85jjjh\u06f6\xadz\xfc\xf1G\xc59\xe7\x8c\x05\xc04#\xfcZ\x1dX \x10\x044\u05ae\xfd\x96;\xee\xb8S\u035a5[\xa4\xa44f\u0420A\x911c\xc6L\x988\xf1\x8aO\x01n\xbd\xf5\x16\xe1\xf7\xfb\xd5C\x0f=\xfcO\xbf\u01de=;\xf9\xeb__\u2a67\x9e\xe4\uaaef\xe6\xb5\xd7^\x03\xe0\xbd\xf7\xde\xed>u\xea\xd4OV\xacX\u046a\xa4\xa4\x04!4%\xa5\x8c/,r\x87\xa9t,\x92\x9ad\xd2\xfe\x941\xb4\x1dq.\xa9y\x1d\x11\x02\xecX\x94\xed\xf3\xa6\xb0i\xc6\xfbD*\x8f\xa0l\x1b4A\xff\xdf<F\x8b>\u00d0\xb1(J@Bj\x06{\x17\u007f\xc1\xdc\xc7~Cmy\x19\x86\u03cf\x12\x9a\u04f6Mk}\xe4\xf0\xa1\u04c7\x0f\x1b\xf6F\xd1\xfe\xfdM5M\v\xef\u077bw\xe5\xc9'\x0f\x8c\x0e\x1f>\xd40\x8c\xa0\x01\xec\xfd\x9e\x92\xc3\xc7T\xba\\\xb6l\t\xfd\xfa\r\xf0\x14\xc5\xc3\x13\xf3\x9f\x82\x1bn\xb8^\xbc\xf8\xe2K\n\xe0\xc5\x17\xffz\xfaG\x1fM\x9e\xbcr\xe5\xaaP$\x12\xa1[\xb7\xae<\xf9\xe4\x13\f\x1f>\x12)M\xea\xf2\x9d\u007f\u0543A\b\xfc\xfe\x10\x85\x85\xbb\xb8\xf3\xce?\xf2\xe1\x87\x1f\x92\x90\x90\xc0\xc0\x81\x03#\xe3\u018d;\u007f\xe2\xc4+\xbe\x02\xb8\xed\xb6[\xc5\xe3\x8f?\xf1\x0f\xcfz\xdf~\xbb\x9a\x1e=z\xfeS\xef]SS\xde\xea\xce;\xef\x992s\xe6\xac\x13\x8b\x8a\x8a\\\xb1\x94\x12\xa9\x14\b\x1d!t\x94t\x90\xb6EZ\xab\x8et\x18u\x11m\x06\x9fIJ\xb3\xe68\xb10;W-b\xeb\xbcO)\u07b8\x12'\x16\xa5\xf7\xb5\xf7\x91?\xe8\f\x84\xb4\xd1\x04$\xa5g\xb1\xe1\xb3W\x99\xf7\xd4\x1fp,\v\xe16\x0e\xc5\xd05\u0661C\a-'';~\xa5\"),,\\\u05e8QR$33\u04d7\x96\x96\xee\x03\xb9\xc3\xe7\xf3\xefh\u07fe}E\xf3\xe6\xcd\x17]r\u0265\x87\x85\x10[\xbe\xefs\xfc\xf1\x8fw1x\xf0 F\x8e\x1c\u5a4b\x87'\xe6?\x06o\xbe9IL\x9cxE\x9d\x90\x8f\xfe\xf0\u00cf\xdf[\xb5jUJ$\x12\xa5W\xaf\x9e<\xfb\xec\xd3\xf4\xeb7\x10\u02ca\xe28\xce/>\xac\xf2Cq\xcb\xf5&RZZ\xcc\x1f\xffx7\x93&M\xc2\xef\xf73`\xc0\x80\x8aK/\xbd\xf4\xe2K/\xbdl:\xc0G\x1f} .\xbc\xf0\xa2c\x04\xfd\xb3\u03e60f\u0339?\xe4=\xda\xee\u07bd\xa3\xe3\xea\xd5\x05r\xf7\xee\u0772\xb2\xb2Jj\x9aV2l\xd8\xd0\xd3\u05ee]w\xff\xfb\xef\xbf\u03d6-[\xb0m\xc7\xcd\"\x12\x1aB\xe8\b\xcd\x00\x01v4\x8c\x11\b\u0462\xe7`N\x18y.-N:\x05\xbdq\x06\xbbW-`\xc5\xcb\u007f\xa6r\xdf\x0ez]s/\xad\x86\x8dE\x03\f\rR\xd23Y\xfc\u04bd,\x99\xf4\x88\x1bc\xf9\xfe+0\xe5\x9e\xd3D})\x06M\xd3HJJ\"++\x93\x94\x94\x14,\xcb\u079b\x99\x99Y\x99\x9f\x9fw 77w\xe6%\x97L\xf8\xb2Y\xb3\xac\x1d\xff\xaf\xfd\xe9\x8d-\x0fO\xcc\xffEV\xae\\&z\xf7\xee\xa7\\\xd1\xf9p\xf4\xcb/\xbf\xf2\xde\xe2\u014bS,\u02e2w\xef\u07bc\xf0\xc2s\xf4\xec\xd9\a\u04cc\xd4\xd78\xf18\x8a\x94\x92P(\x89\x8a\x8a\xc3\xdcq\u01dd\xbc\xfa\xeak\xe8\xba\xce\u0211\xa7V\xfc\xfe\xf7\xbf\xbbx\u0108\x91\xd3\x01V\xaf^!z\xf6\xec\xf3\x0f\x1d\xfa\xaaU+\xfa}\xf6\xd9\xe7\xadJKK\xba$$$\x0e\u07bd{O\xb8\xac\xacL$$$\xe4\x80\u02a8\xaa\xaaR\xb1XL\u0555\xfe5\f\xa3\xa6}\xfb\xf6\xd9EEE\xbe-[\xb6P[S\x8b\xed\xb8\x95\x0fu\u0747\xa6\x1b\bMC\xd3t\x1c\xdb\u010eFH\xca\xc8&\xb7\xfb@:\x9c~\t\xca\b\xb0\xf4\x85\xbb(\u07fd\x99>\xd7\xddG\x9ba\xe7!P\x18~\x1f2Z\xcb\xfc\xc7\u007f\xcf\xf6E\xd3\x10\x9a\x86j\xb0V\xa0\xaeDB\x03\x01\x96\xc7=\xae\x1c\u01d1\x80\x1e\n\x055\x9f\xcfOVV&\t\t\x894j\x94\xb4\xbbC\x87\x0e\xbb\xf3\xf2\xf2>\x1f>|\xd8w\xbd{\xf7\xfd\xfa\xfb\xf6\xc5\xff\xfd\xdf\r\\{\xed5t\xe9r\xa27\xc0<<1\xff!\xbc\xf3\xce[\xe2\xd2K/\aP\xdf~[0\xfc\xfe\xfb\xef\x9f2}\xfa\x8cd\xd34\x9d\xee\u077b\ubbfc\xf2\"\xbdz\xf5\xf5\x84\xfc\a\nzyy\x197\xdf|+o\xbe\xf9\x96\x13\b\x04\xf41c\xc6T\xdd{\xef=c:v\xec4?\x18\fj\xd1h\xb4auI\u07c2\x05\xf3:~\xf8\xe1G\x83\x0e\x1e<x\xd9\xee\xdd{:\x16\x17\x17k\x9a&B\xc1`\x90h4\x86e\xd9\xc4bQ\x1c\xc7F\b\xed8!u\x17\x01%&&*P\"\x1c\x8e\xb8\xcd=\x00\xc3\xf0c\x18>4]\a\xb4x\x98\x04\xacH\x18P\xa4\xb4hKbz6\xe5{\xb7a\x9b\x11\x86\xde\xfa,\xad\x87\x9c\x8dm\x9a\x04\x92\x1aQ\xba\xa5\x80\x19\u007f\xbe\x92\x92m\xeb\xfeN\u033f/\xdcTGC\x87\x1e/ \xe6(\xa5\xb0,\xcb\xd14\xcd\u07e8Q\x12\xa9\xa9\xa9\x04\x83A'9\xb9QU\xabV\xad\xcb:v\uce22}\xfb\xb6S\x87\r\x1b\xb6*##\xb3\xf0\xf8\xd7_\xb6l\x89\xe8\xd7o\x80\x97\x1e\xe3\xe1\x89\xf9\x0f\xb9\xa4-**l\xf5\xdb\xdf\xfe~\xfa\xfc\xf9\xf3\xdbWTT\u0431c\a^\u007f\xfdU\xfa\xf7?\x19\xa5\xdc\x06\x10\x9e\x90\xff\xff\f\x8ex\f\xfd\u02112\xae\xbd\xf6:>\xf9d\niiM\x18:t\xe8\xc6\x17^x\xee\xf4\xa6M\xb3\v\xe3\xfb=\xf5\x81\a\xee;s\u04e6\u037f\u0674is\xcf\x1d;v \x84 \x1c\x0e7\x9cP\xae\xaf\u007fX\x97\x1e\xf9}\xf9\xfbu\xc7\xd00\x8c\xfa\x15\xac\xb6m\xbb\xee\xdc\xf0\xa3\xeb\x06\x9a\xa6\xe3\u0583\xaf;\xf1\xd8H\xcbD\xd3\f4\x9f\x8f\xa4\xa6\xd9\xf4\xbb\xf2.\xd2\xdbt&RSE \x94\xc8\xc1\x8d\xabX\xfa\xda\x03T\x97\x1e\xa8\xaf\xeaX\xf7\x15h8\f\xea\xab4\xba\xa7\xa7\xef\x1d#\rJ\xfc\"\xa5T\x80\xd0u\xb7\xcd_rr2\x81@\x80\xb4\xb4&t\xea\xd4\xc9NII\xf9\xa8m\u06f6_\xe6\xe7\xb7Z}\xd6Yg\x954,\x99\u0423G\x0f\xe3\xba\xeb\xaea\xe4\xc8\x11N^^kO\xdc=<1\xff\x9e/[\xfa\x84\t\xe3\u07de7o\xfe\xe8C\x87\x8a\xc9\xc9\xc9\xe1\xf9\xe7\x9f\xc3\xcdZq\x88F\xa3\x9e\x90\xff@4M\xc3\xe7\v\xb2c\xc7V\xae\xb9\xe6:\x16,XHNN6#G\x8e\xfcj\u04a47\xcf\x02|\xbf\xfb\xddM\x9f~\xf3\u0362\xd1\x1b7n\"\x163\x8f9\x14\xc4c\xd0J\xb9\xa1\xea\xba\xfd\xfe}9\xfc\r\ufeee\xdd}\t\xdbvk\xe1h\xba\x0fM3\x10\x9a~\x8c\x007\x8c\x81\vM#\u0538\t\x89M\x9ab[&V,\x82t\x1cb5\x95\x84+\u0290\x8e\xddP\xad\u007f2|>\x1f\xc1`\x90`0H\xeb\u05ad\xc9\xc9\xc9.MKK[\x92\x96\x96V\x90\x92\xd2x\xfem\xb7\xddQ$\x84\xd8[\xf7\xf7o\xbd\xf5\x866r\xe4\xa92;\xbb9\xf3\xe7\xcfe\xe8\xd0\xe1\xde\xe0\xf3\xf8\xf5\x89\xf9\x8c\x19_\xb1p\xe1\xd7\xe2\xb1\xc7\x1eW\x00\xf7\xdcs\xf7\vS\xa6|z\u00e6M\x9bHHHP\x8f?\xfe\x98pk\xb1(\xa2\u0470'\xe4\xff\xe4\u054ea\x18\x18F\x80\xe5\u02d7r\xf5\xd5\u05e8\r\x1b6\x8aN\x9dN\xe0\x8a+&>\x9e\x94\x94\xd4\xf4\xd9g\x9f\xbb|\xf3\xe6-\xff0\xad\xb3n\u007f7lX}\xb4\xd6\xfb?h\u05a1\x14R)\xb7T\xadRnHL\xd3\u30ae\xd7\u05c9o(\xca\xf1P7B)\xecX\fG\xd9?H\xb2u@\xd3\x05\xb6\xa3~\x12\x897\f\x03\xbf\xdfG(\x14\"33\x93\x9c\x9c\x1c\u0574i\xd3\xe5\x19\x19\xe9S\u018e\x1d\xb3\xec\u44c7,=\xfe93g~\u0168Q\xa7{\x03\xd0\xe3\xd7%\xe67\xdex\x03/\xbc\xf0\"\x00\xaf\xbe\xfa\u0288\x17_|q\u06bau\xdf\x05\x95R\xfc\xe1\x0f\xbf\xe7\xd1G\x1f\xc6\xe7\xf3y\x8e\xfc\xdf\x10tM\xd3\xf0\xfbC|\xf8\xe1\xfb\xfc\xf6\xb77QZZF\x9f>\xbdcyy-\x03\v\x16,\xa4\xa4\xa4L\x89x\xe7\x88\xe3E\xfd\x18\xc7]\xe7\xa4\x1b\x8a|\x03\x1b\x0f\n%]!\xff\u01c36^b\xa1>\xde\xee\xde\xfc\t\x894JoF()\x19_(\x01_ \x88\xe1\xf3\xe1\xf3\x19\x84\xfc\x06\x86\xc0\x9dh\xd5\x04\x89\x8d\x92\xc9h\u0782\xe4\x8cl\x84\xe1\aM#\x1251-\x13G\xbaW\x03V\xa4\x96HM5\xb5U\x15T\x94\x1e\xa2d\xefN\x8e\xec\xdbE\xac\xb6\xa6~\xfb\xea?O\x83\xab\x83\xff\xd7IM\b\xd04\x9dF\x8d\x1a\u047auk\xd2\xd3\u04cb\xf3\xf3\xf3\xbe>\xf7\u0731\x8b\x86\r\x1b\xf1\x8a\x10\xa2>G\xf6\x92K&0b\xc4p\xe2s@\x1e\x1e\xbfl1\u007f\xe2\x89\u01f8\xf5\xd6\xdb\xeb\xbeDY\xa7\x9f~\xfa\xeco\xbe\xf9\xa6suu5g\x9cq:o\xbc\xf1\x1aM\x9bf\x11\x0eW\xff\xa4M#~\xe9H\xa9\xf0\xfb}\b!\xb8\xff\xfe\ax\xf8\xe1G\xd1u\x9d\xe4\xe4d\x94\x92\xaa\xb2\xb2\xaa\xbe{\xd0O\xb1\xf0J\xd7\r\x8c@\x00_ \xc1m\xd4l\u06d8\xd1\bB\bB\u0269d\xb4\xea@\xeb\x01\xa7\u04b4M\x17t\xbf\x1f_ \x80?\x10\xc4\xef\xf7\x91\x14\xf4\x91\x92\xe0\xc3\xf0\xb9\xd5\x195\x01\x9a/@(9\x15\x15\x00G\x82\xe5\x80%\x8f\x9eT\x1c\a\xa4\x05\x96\x19\xc32\xa3\x985U\u0516\x1f\xa6\xa4p'\xfb\xb7\xac\xa1\xe8\xbb\x15\x14\xef\xdcL\xb4\xaa\x1c+v\xb41\x89\xe6\xaau\xbc\u035d\x02)\xff\xce\xed\xc7E]*\x05~\xbfO\xcb\xcf\xcf'//\x8f\x13N8a\u0168Q#\xdf\x1e1b\xd4K\rO\x80\u007f\xfd\xeb\xb3\xe27\xbf\xb9\u024b\xa9{\xfc\xb2\xc5|\xc1\x82\xb9\xe2\x94S\x86+\x80K/\xbd\xe4\x91y\xf3\xe6\u0771\u007f\xff\x01\u06b5k\xab\xde~\xfbM\u0477\xef\x00\"\x91\x9a_L\xb1\xac\xff\xae\xa0KB\xa1D\xca\xcaJ\xb9\xfa\xeak\xf8\xfc\xf3\xa9\xca\xef\xf7\x8b\xc4\xc4$\x1c\xc7&\x1c\x0e#\xe3\xcd$\xfe\x91\xa0\a\x02A\xfc\xc1 >\u007f\x00M\xd7\x11\x9a\xee\xe6\x91\xeb\x1a\x9an\xe0\v\x04\t&5&)-\x83\x94f9$7\xcb!9\xad\x19IM\xd2\t5N\xc5\x1f\b\xa1\xa4\xc24c\u0516\x97Qqp?\xc2\xf0\x91\x9a\u05d1\x94\x96\xed\xd0\x02!l\xc7F\x17`h\x1a\xba\xa6\xe1\xd3\x05~C\x03MG\x02\xb6\x84\x98\xa3\x88\xc6Lbu\xd50\x95@\x89\xba/\x85\xdb\xcdH\b\xf7\n\xc0gh\x04|\x06\x01\xbf\x0f\xa5 RSM\xb8\xe20\x87\xf7\xed\xe2\xc0\x96\xef\u063f~\x05%\xdb\xd6Q[v\x88XMe}\xa6\x8c\xd0\u2379\x1b\x84\x8b\x8e\x17uM\x13\xd2q\xa4\r\xf8[\xb5jEnns:w\xee\xfc\xdd\xc9'\x0f|l\u0738\xf1\xef\x1f\xbf\xff\x16/^$\x06\x0e\x1c\xe4\t\xbb\xc7/G\xcc\xdf\u007f\xff]\u018f\x9fP\u007f\xff\x85\x17\xfez\xfeK/\xbd\xfc\u0386\r\x1b\x83III\xea\xe9\xa7\xff\"\xae\xba\xea\xea\xff\xa9n@\xbf\x14\x02\x81\x04V\xadZ\xc1\xd5W_\u00fau\xdf\u0468Q#\xfc~?\xd55n\xa9`\xa5\x14\xb2\xc1>Oi\x9aI\u04fc\xb6\xb4\xec\u0503\xdc\xf6]H\xcbjNb\xe3\x14\x94\xe1\a\xcd@\t\x1d\xdd\xef\xc7\x1f\bb\xf8\xfdh\x86\x0f\xdd\xe7G\xf3\xf9\x11\x86\x0f\xc3\aq\xcdwO*\x8e\xeb\x9c\x1d\x1b\xccH\x14\u04f2\x89Y\n\xd32\xe3B.\xd04\x1d]\xd7PB\xc3A\xb8\xff+0\xa5\xdbrNA}\x8c\\*7n.\x04\b\x14*^[W\x13\x02\x05\xe8\xc2-\xb7+P\u8e86\xe6\xf7\xe3\xf3\xfb\xdd\x05L\xa6\x83\x19\rS]\xba\x9f\xd2-k8\xb0v\te\xdb\xd7S}h\x1f\xe1#\x87\xb0\xa2\x11@C7\f\x84\u0410\xd2q;\x1f\xa9\xefM\x8dTB\b\x91\x99\x99I\u02d6-\xe8\u0673\xe7\xa6\xe6\xcds\x1e:\u55213{\xf7\xeeS)\x84\xa8\u06e9\xdaSO=a\xb4i\xd3\xda:\xfb\xec\xb1\xea\xd5W_\xe6\x9ak\xae\xf3\x06\xa6'\xe6?_\xbe\xf9f\xa18\xf9\xe4!J)\x95w\xe1\x85\x17\u03181cF\x87\xea\xea\x1a\xfb\xf2\xcb/7^\u007f\xfd\x15t\xddO4Z\xeb\xc5\xc9\u007fd\xdcf\x12\x06\x0f?\xfc\x10\xf7\xdcs\x0fR*\x02\x81\x00\x8e\x94\xd8\xf1\xaeA\t\x8dS\xc8l\u0749\xdcN\xdd\xe9|\xf2\bZu\xefO\xa8qj}\u03b6\x03H\xe2m>\x05\xf8}\xf5\xd1\t7\xf4,\xe3\xa1\x0e\x89[\xf6\xf68\xc7_\xefr\x85\xc0\xb4$\xb5\x11\x13+\xbe\x92\xd7\xd0\x056\x1a\xb6\x128\b\x1c\xa9\xb0\xe2o(\x94B9\xa0I\xe5\x164w\x14\x9a\x12h(\xa4\x00\xa1@\xb8\xadG]a\x8f\x8b<B 5PB 5\x81\xd0\u0709\\\xddg\xa0\xf9\xfch\xba\x86\x9b.i\x11=R\xcc\xe1\xed\xeb8\xb8~)\xa5;6PV\xb8\x8d\xaa\xe2\xfd\x98\xe1Zw\xf27\x9ev\xa9\xbe\xbfc\x95\x02\x84\xcf\xe7#%%\x85\x9c\x9c\x1c\u06b6m\xbd\xa7U\xabV\u04db4I\x9bu\xdbm\xb7\u007f#\x84(\xaf\xfb\xe3'\x9f|\xdc8\xf3\u0333\x9c\xf6\xed;xn\xdd\x13\xf3\x9f'\xf1\\_\x01\xa8\x9bn\xfa\xcd]\u04e7\xcf|h\xfb\xf6\xed\xb4i\xd3FM\x99\xf2\xb1\xe8\u06b5\xfb\xaf\xbap\xd6O\x89a\x18\u8e9fg\x9f}\x9a\x9bo\xbe\xb5\xfe\xca'\xb1q\n9m:\u04b4mgZv\xebO\xeb>CH\xcf\xcd\xc3\x10`9\xee*O\xe5H\xa4\x94X\xf1IN\r\xf0\xf94|\xba\x1e\x9f?\xfc\xfb\x8c\x12\xe9\x06\xa0\xe3\xf3\xa6\x02\xd5`\xf0*\x14Q[Q\x13s\x889\nG\xba\xae\xdav\xc0\xb6\x95+\xe0\x8eB\xd9\n\xddQ\xe8\x12\x84T\x88:{^\x97O\x1e\x0f\xaf(\x11?\x9b\xc4\xdfO\xe1\u693b\xb1pp\u217f\x10\xf1\t\xd0\xf8k\b@\xf3\x19\x88\x80\x0f\xcdo`\xf8u|\x86\u00ac,\xa5l\xc7:\x0em_\xc7\xc1\xed\xeb8\xb0m\x1de\xfbw\xe2\x9814\xddp_K\xc9\xf8\xa4\xef\u07fbu\x9f\xcf \x18\f\x91\x92\x92B\xeb\u05adHKK\x9bw\xc2\t\x1d\x17\x0f\x1atr\xc1\x88\x11\xa3\xbe\xa8\xfb;]\x0f\xb0e\xcbwL\x9d:\x8d[n\xb9\xd5\x1b\xa4\xbf\xc6\xef\xe5\xcfu\xc3\x1fy\xe4a\x03\xb0W\xae\\\xde\xe6\x96[n\x9d\xb8c\xc7\x0e\x00\xe7\xd2K/\u047bv\xed\x0e8\x9e\x90\xffT\x0e@\b\xa4t\x1dx\xe3\xe4d\x8e\x94\xbbF1-\xbb\x05g\xdd\xfc0-\xfb\f!f\x81m;\x84\xc3Q4%\xd1\\I\u018a\xbb\xdeX<n\x1d4tt\x04\x8e\xaa\x13T\xf7\u007f\xd9@\xb4\x1b\xaa\xbbj`?\xa4\x02\u06c2\u06b0$\x12\x95X\x96D:\x80\xe3\n\xb8\xee(\x84\xac\x13\xdc\xf8\xeb\u01c5\x17\x15\u007f\\\xa8\xb8h\xc7?[\x831#d\xfc\xf7u\x8f;\"\xfe{\xf79\xc4O\f\xc4\xdfCDM\xa8\x8e!5\rS\xd7P~\r#\x90L\x8b\x8e\xc3h\xd5e\x04fu\x19\x87vm\xa0hK\x01\xbb\xd6~\xcd\xee\xf5\x8b\x89\xd6V\xb9i\x9a\x9a\x86\xae\xdcYQ\x85\xac\xdf \u02f2\xb1\xacj\xc2\xe10\xc5\u0147\b\x85B\xc3v\xed\xda5l\u02d6-\\r\u0244\x0f\x87\x0e\x1d:\xf3\xf2\xcb'~.\x84\xa8l\u06f6=\x00w\xdey;'\x9ex\"\x17^x\x917X\u007fE\xfc,\xd3;\xde}\xf7\x1dn\xbc\xf17\xd2u.\xbe\xdbW\xacXqfee\x15\xbd{\xf7\xd2\xee\xbf\xff\u03e4\xa66\xc1\x8cw\x94\xf1\xf8i\xae\x8a4M#';\x8b\xe6y\xf9\x1c\xae\x0e\xb3\xbfp7\x8em\u046a\xcfP\xd2\xdbt\xa2\xa2\xb2\n\u02f6q\x1c\x85&\x14\xfex\b%\xe2(\"\x0e\xd4\xda\x02[\t|\xbe\xbae\xf2q\x01W\x02\x89\xeb\x80\x1d\xe5:c\x89\xfb\x98\x02\xa4\r\x8e\xa9\x90\x11\x87X\x8dCm\x85Em\xb5\x83S\xeb\xa0E$ZL!L\x85\xb0\x14\x9a\x03\"\x1eR\x11\xc4\xe3\xe0u\xd1\x19D}k\x96\xfa\xe5\xfa\x9a\xe6.VB\xc4\x17-\xe9\b\x8d\xf8\xfd\xa3W\x03u7M)W\xc4U\x83\xeb\x85\xf8\x95\x80\xe6H\x94\xed\xe0\xc4lb\xe1\x18\xb1p\f\xb4\x00\xa9Ym\xc8\xed\u0417\x96\x1d\xfb\x93\xd9\xe2\x044M\xa3\xa6\xbc\x183V\xeb\xeeW]C\x13\xfa\u07dd\xc5T|i\xaa\xe3H\x8a\x8b\x8b\u0556-[EUUe\u78a2\xa2s\x16/\xfef\xd0\xf5\xd7_w\xe4\x8b/\xbe\xd8\x02\xb0x\xf1\x12\x06\f\xe8/f\u035a\xed\rVO\xcc\xff\xb7)--\x13{\xf7\xee\xe5\xeb\xaf\x17v\xfd\uaaef\x9e/(\xf86\xa8\xeb\x1a\xf7\xdcs7\u00c7\x8f@J\u06db\xf4\xfc\x89\x91R\x92\x96\x96\u0189={!\xd2r\xf9f\xde\\\xaa\x0f\x97\x10\r\xd7\u04a2\xc7\x10\x02\xc9M0-\x13S\xbaU\v\x13u\x81)\xa1\xca\x12T\xdb`)H\xf0\xe9\x04tw\xa5\xa7\xac\x8bn\b7\xce]'\xecn\xefN\x894\x152,\x91\xd5\x0eN\x8d\x83Y\xe3P[e\x13\x89H\x94\xa5\xa8W\xfb\xb8\xf3\x16\xf1e\xf9\xaa>D#\xdc\xfct\u074dq\xfb\xfc\x01\x8cP\x10\u007fB\b\u007f(\x84\x91\x10\xc4\x17\f\xa0\a\xfd\xe8\x01?F\xc0\x8f\x11\xf2c\x04\xdd\x14\xc7@(H !\x84?\x18\xc0\xef\v\xa0\x1b\x06\x1aZ\xbd\x90\u05c9:\u01c9=\x8eB\xd8\xeee\x84c;D\xa3\x11L\xd3$\x98\x94F\xb3\xfc\xae\xb4\xe8\u060f\xacV\xdd\x10B#Ru\x183Z\x8b\x94\x0e\x9a\x86\x9b\xe9s\xdcITJ\x89\xae\xebB\xd7u*\xca\xcb\xed=\x85{\xb4\xe2\xe2\xe2\xdc={\n/\xbc\xe8\xa2\v;,^\xbcd\xf9}\xf7\xddW=k\xd6l\x1e~\xf8Am\u07bc\xf9\xde\xe5\xa9\x17f\xf9\xdfd\xc1\x82\xb9\u0525\"\xbe\xfd\xf6[\xa3\u05af\u07d0\x020`\xc0\x00F\x8d\x1a\x15\xbf4\xb5\xbc#\xfb\x1f\xc0\x8eE\x89\xe0#\xbf[_\xfa\x8c>\x9f\xd9o=\u02eeU_\xb3\xe7\xdb\xc5t8\xf5B\x1ca\xe0H\x87\x04\x1d\x92\xfd\x10\x8b\x82\xa3\x14\x8e\x04\xbf\x00\xbf\x13W]\x9fpSFp\xc5W:\xb8jo*\x94%Q\x96D8\xa0\x1c\x85\xb2%\xa6\xa3\x88\xd8no\xcf\xfa\x90\b\u2a17mPwE\xf7\xf9\xd1\xfd\x01t\xbf\x1ft\x81\xe3XH'\x86\xe9DP\x96\x85f[H3\x8cY[\x89\x15\x8b\xe2\xd8\x16\x8e\x1dC\xd96B\xd30|\x01|~?~\u007f\x10\u007f\xb0\x11>\u007f\"\x9a\x1e\x00\u0347\x1e\xf2\xe1\x0f%\xa2\v\x03iK\xac\x98\x89\x8a\xc5pl\u06dd\xb9=\xbaA`\xc7C?\x9a@\tI$Z\t\x9a\xc0\x1fJ\xa7\xcdIg\x91\xd3~ E[\x97\xb3i\xc9\x14\xf6o[IeY\x11\x8e\xed\x8ec\xa1\u05657R_\xafF\xd34t]7\x84\x10r\xff\xfe\x03\xce\xe1\u00c7}ee%\xe3\x0e\x1c8\xd8w\u07bc9w\r\x1b6\u20fb\xee\xba[\xe6\xe6\u62bd{\xf7*/\t\xe0W\x10\xfe\xfc\xb9n\xf8\xfb\xef\xbf\xdb\xea\xe5\x97_\x99\xbft\xe9\u0496Bh\xbc\xf2\u028bL\x9cx\x95\x97\xbd\xf2\x9ft\x02\x02\xaab6[U23\xe6\xaf\xe0\xc5\xdfM\xa0\xacp\a\x9dF\x8c\xe5\xf4\xbb\x9f')=\v'\\C\xcb$A\x93\x00\xec\xa8V\x14V+b&$*H\xd4\xdcE6\x9a_C\v\xb8\x82ET\xa2\xa2\x12,\xe9f\x95HE]B\x9e\x9b#\xae\x88\u064a\x98T\u023a\xd4\x17\x11\x9f\xbcT\x12)\x14\x9a\u03c7\x16L@\xf3\xf9\xb0c\xb5\xc4\"\x15\xc4j+\bW\x16SY\xbc\x9b\x9a\xb2}\xd4\x14\xef%r\xf8\x00\u044aR\xccp\r\xb6\x19E\u019bI+\xe5\x06\xf6\x15\xc4K\b\xb8\x8e^\xf7\x05\t&&\x93\xd0$\x83\xa4\xf4l\x92\u04f3Ii\x9aG\xe3\xb4<\x12\x933\t\x06S\b\x05\x1ac\xe8A\x1c\xd3\u008a\x85ql;\xbeJ\u050d\xe9\u0539w\x15\u03d0Q\x024C\xc7\b& \x84\x86\x15\xae\xa4\xb8p=\xdbW~I\u0476\u5517\xee!RS\x8eR2^`L\x1c\xb3}\x9a&\u0404@JG*%\xb5&M\xd28\xf1\xc4nj\xc0\x80\xfew\xdc{\xef}\x8f\xd7\x1d\xab\xfb\xef\xbf\xcf\xf8\xdd\xef~\xeb$'\xa7zN\xdds\xe6\xff[l\u0738\xf1\xe2\xc2\xc2\u0096\xb6\xed0d\xc8\x00\x86\r\x1b\x06\x80\xe3H\f\xc3[\xe9\xf9\x9f@\x01~C\u00e8\x8e\u04aa\xf3\x89t\x1dz\x16\xf3\xdf\xfc\v{V/\xa2t\xfb\x062\xb2\xb2\xc0/\b\u06cajKP\x1e\x83\x98\x03\xd2V\xf1\x15\x92n\xf8A\x85\x1d\xa4\x16?\x01[\n\xe1\xb81\v\xd5\xc0mK\xa9\xea#)\x86\xe1\xc6eL\xa9\u0710\x8cP(CG\x0f5\xc2\b\xf9\x89\x85\xab\xa8,\xd9N\xd5\xc1]\x1c\u07b9\x8e#{6pd\xcf\x16j\x8b\xf7aEkp,\xeb\u01e9\xb3%p\xcb\x05\x04\x13I\xcdjE\xd3\x16'\xd04\xa7\x13\xe9\xd9\xedHMoIJZ\v\x92R\u04f1b1\xa2\xd1p\u072d\x8b\xfaP\x90\x92\xee\x0fR9\u011cj\x84a`\xf8\x12h\u07a6\x1f\x99\u037bQQ\xbc\x8b\xa2\x1d+8\xb8\xb3\x80\xb2\x03[(/\xd9E\xa4\xb62^hL;f;t\xdd\xd0\x04\x8a\xf2\xf2\n\x16/^\"\xaa\xaa\xaa\x1e\x9b8\xf1\xf2\x93\a\r\x1a\xf4\xc8\xe5\x97O\\&\x84\xb0\xff\xf4\xa7{\xf9\xec\xb3O\xb4s\xce9W\n!\u0639s\x1b\xad[\xb7\xf3\x06\xb2'\xe6\xffy^y\xe5eq\xed\xb5\xd7)\xa5\x94\x187\xee\xc2\u02cf\x1cq\xb3(\xce8\xe3\fZ\xb4\xc8'\x1a\xadE\u05fd\x95\x9e\xffI1\xf7\xe9\x1a\x8d5\x8b\u018d\x1a\xd1i\xd0\b\n\xbe\xfa\x90\u0292\x03l_<\x936\xbd\ac\x18AJ\xc3\x11bRa\xd5\t\xb8\xed\u01b9\x95r\x85]\xd8\xf1\xec\x10#^|K\xa8\xfaKF7Y$\x9eQ\"@\xd7\xdc<r\x9fO\xe0\xd3\x05\x8e\u07cf\x13\fPc\u0654\xec\xddJ\xe9\x8ao)\u077c\x9a\x92M+9\xb2c#v\xb8\xfa{\xb7=\xadI\x13\xb2\xb22IKK\xa3Qr#\x1a'7&\x18\n\xe1\xf3\xf9\xea\xcb\x16(\xa9p\xa4C4fR]\x1b\xa6\xa2\xb2\x8a\xc3G*(;|\x98\xe2\xe2b\xa2U\xe5\u0626\x85mV\x10\xa9\xfa\x96\x03[\xbf\x05\xc0\x1fJ\xa2YnGrZ\x9fDv~\x0f\x9a\xb7\xe9I\x93\u0336h\xc2G,\x1a\u01b6\xad\xa3\xb5g\xe2\x93\x05\x02\x81t\x1c,;\x8c\xe9H\x90\x92Fi\xb9tn\x9aG\xfb\x93N\xe7\xc8\xc1\xed\x1c\u06b3\x86\xa2\xed\xcb\u067f\xb3\x80\x9a\xca\xd2x\xa3\x0e\x83\xba*1B\b|\x01\x1d)\x1d\u05ae[O\xd1\xfe\x03g\x14\x15\x15\x8d(((\xf8`\u035a\u056fw\xef\xdes\u02581\xe7I7\x12\xe5u@\xf2\xc2,\xff\x03\xfc\xedo\xef\f}\xfc\xf1\xc7\xe7\xad_\xbf\x81\x13N8\x81\xbf\xfd\xedmz\xf4\xe8\xe9\xd5_\xf9/\xa0\v\xa84\x15\xab*|\xec*\x8f\xf0\xf6\xdd7\xb0r\xda\xfbdw<\x91\t\xcf}FJ\xf3<\xaa\xaak\xb0l\x85\x13U\xc4b\x12\"\x12\u007fD\x11D\xe1fZ\u01d7\xcf\xeb\u008d\xdd\xc4S\x05\xad\xb8s\xf5\xe9\x1a\xba!\x10~\x81\xf0k\xe8!\x1fzb\x00-\x045\x95\x11\xb6,\x9b\u01f6%s9\xb8~\x05e[\xd7 \x8f\xcbd\xd2CId\u7de5]\xfb\xf6tn\u05ca\x0e\xadZ\u0432y\x0e\xe9\x19\u9924\xa4\x90\x94\x94DRR\x12\x81@\x00]\xd7\xe3c(\x9e\xc5\x1e\x8fQG\xa21\xaa\xaak(\xaf\xac\xe2Hy9\x87J\xca\xd8{\xe0 ;\v\x8b\u0631k7[7nd\xff\xaem\xa8\u0631}\x9f5\xcd \xb7m/Z\xb6\xefK~\xc7A\xe4\xb6\xeb\x87?\xd4\x18\xd3r\x1buH\xc7F\xc5\xeb\xa6+w\xd9\x04\x8ec#\x1d\v\xa5\x1c\x84r\x1b\xa8\xe8\x86\x1f]\u04e8)?\xc0\xa1\xddk\u0675a\x1e{6/&\\S\xee:uMw\xb3n\xe2a\x17Pn\x88G@vV3:w\xeet\xa4g\u03de\x9f^{\xedUogdd-\xae\u06fe6\xed\u06b2`\xde\x1crs\xf3\xbc\xc1\xec9\xf3\xff\f\xaf\xbd\xf6*W_}\r\x00\xabW\xaf\xba\xb0\xac\xac\f\x80\x01\x03\xfa\u04ed[Wl;\xea\t\xf9\u007f\x01\xa9 \xc9'h\uace8JkL\xbb^\x83(\x981\x99\u00c5;)\\\xb3\x94F\xd9y(K\xa0*m\x88J\xfc\xb6DJ\x81\x94\n\xd3Q(\x01\xbav\xd4UH\x01\xb6\x06\xb6\x00\xe1\x13\x84\x82:FPC\xf84\x8c\x84\x00F\x82\x8e\xee\x87\xca\xe2#l\xfeb\x1a[\xbf\x9e\u039e\xd5\xdfP]v\xa8\x81E\xd1I\xca\xc8&\xbf[O\xba\xf4\xeeO\x97N'\u042du.\xeds\x9b\x92\xdb,\x1d\xdf1Y\"\xeer\xd3\xfa2\x04R\x1e\x93\t\xe5f\xc1\b\x12\x13B$7J\xa4yv\xf61\x1e(\xec8\x1c,;\xc2\u03bd\a\u063ck/\xeb7nf\u00f7\xab\u067af\x15\x15\a\x8b\x90\u04a6p\xeb2\n\xb7.c\xdd\xe2\x8f\xc8m\u04d3\xb6\xddF\xd0\xf6\xc4\xd3HHi\x8e\xed\xd8\xd8f\xbc\xf3R]\x9c^\xb9\xce\x1c)\x91\xca\x01%\xb1cQ\x04\xe0\x0f\xa5\u042a\xebH\xb2[\x9dD\x8bv\xfdY\xbf\xec#\x0e\x16\xaeG\xc5\xdb\xeb)%\xe2\xb1t\r\xc3\xe7\a\xa5\u053e\xfd\x87TIiY\x93]\xbb\xf7\\\xb5i\u04e6\v\xfe\xfa\xdc3\xcf\xff\xdfon\xfa\xb3\x10\xc2\u06b1m;W_}\x9d\xf8z\xd1B5x\xd0\x10o@{\xce\xfc?K\u07fe}Vm\u0630\xb1\xa7\xa6i<\xff\xfcs\\r\xc9e\xde\xc4\xe7\u007f3\xd4\"\xa0$\xaaXU\x1d\xa2\xe0\xbbM\xbcy\xebe\xec\xdb\xf8-=\u03ba\x94\xb3\xeex\x05\xabV\x11\xad\b\xe38n\n\x9f\u012d\xaf\"l\xe5\xe6j\xc7\xf3\xb8\x95\x00\xe9\xd3\x10\x8du|\x89:\x81\x90\x8e\xcf'\xd0\f\x83@\xa2\x0fM@eq\x19\x1bf\u007f\xcaw3>\xa2h\xc3jb5\xf1\xc6=\x9aARF\x16\xf9]{\xd1u\xf0H:u;\x89\xbc\xdc\x1c\xba\xb4\u0320u\x8aA\b\x00\x1b\xcb4\xb1l\xc7\x15\xbc\xffG=\xf5c\xbb\x11\x1d\xfd\xacuY%\xa0\xd0\x04\xfc\u007f\xec\x9dw|UU\xba\xfe\xbfk\x97\u04d2\x13\xd2h\xa1\x86^D@)\n\x88\xbd\x01\x96\xb1\x97k\x1d\xb1a\x1f\xcbXf\xece\xf4\u07b9\xe3X\xc6^\u007f\x8c]\xb0PT\x8a\x02\x82\bH'\xa1CBB \xbd\x9c\xba\xcb\xfa\xfd\xb1\xf7>I(\x8ewF$\xced}>\x87\x84\xe4$\xd9\xe7\ucd5e\xf5\xae\xe7}\xde\xe7\xd5T\x15M\xd7A\xe8H\xa02nR\xbc\xbb\x8a\xcdE%,]\xb6\x8c\x85\xb3f\xb2j\xf1Bj\xcb\u02fc\u07c0/\x10\"\xaf\xfbP\x0e\x1d}\x01}\x0f\x9f@Zf\a,\xd3&\x11\x8f8]\x93l\x13\xcb4\x90\xb6\x89\x94n\u027f[\xf4$\xa5\x8dD\xa2\xeb\x014M\xa5z\xd7fV-\xf8;\x9bV~I\xa4\xa1\nE\xf3\xa3*\x9a\xa3\x9dW\x85\xfb\xde\n\xa4\xb4\xedx<&\xc3\xe9ij~~w\x86\r;l\xe6]w\xde\xfe`~~\xef\xef\x00F\x1d9J\xfc\xfe\xee;\xe5\u99df\xd9:\xa9[#\xf3\x037\x16-Z\xc0\x91G\x8e\x01\xe0\x95W^>\xff\x89'\x9e8\xa4\xa1\xa1\x811cFs\xec\xb1\u01f0\xbf\xf6^\xad\u35cb\u03b3\xfd\x82\x8e\t\x83.}\x06\xd2}\xc8H\x8a\xd7\xfe@\xc9\xea%Tm\\OV\x87~(\x8a\xc3A;\x05\x94N\xc1\x8e\xd4@Z\xb8\x05C\x8e\u02e0/\xa8\xe0\xcf\xd0\xd13T\x046\x9a\xee#\x10T\xa9/\xaff\xf5\x17\x1f\xb2\xf4\xe37(Y\xb7\f3\xe1P)\xbeP\x98.\x87\x1c\xc6\xe0\xa3Of\xd4\xf1\xa7\u043bO\x1f\xdae\xa6\xd19\x03rT\b\xd8\x06\x981L[\xe2)\x19\u007f\xea\tn\xafNH\xce\x17\x9b}\u0352`%\r\x14a\xa0\nAn@#\xb7k;\x86vm\xc7o\xc6\f\xa5\xf2\xb7\x17\xb1rM\x01\xd3?\xff\x9c93g\xb2~\xdd\x1a\x12\xb1\x06\xb6\x15~K\u0676\x15\xac^\xf8\x1e\x83\x8e\xba\x88^\x87\x9eLZF{\x8cd\x8cD,\xd1\xe8\xb4h7z\xd1x\x1d\xed\x04\n\x86\x11\u01f6u\xb2;\xf6c\u0504\xdb\xc8\xcd\xeb\xcd\xea\x85\xefS\xb9k\v\xb6\xb4P\x15\x1d\xdbvr\x11\x8a\xa3\x84W\xfc\xfe \xd1XB\xae][(jjjOI&\x8c\xe3f\u0318v\u07a9\xa7\x8e\xffd\u1885\xb2\xbc\xbcB\x00\xf2\xb9\xe7\x9ec\u04a4I\xad\x13\xfb\xd7Fy\xfe\x1a.\xf2\xd5W_K}\u07b6m\xdb\x1b\n\n\n\x8e\xa8\xafo\xe0\xf4\xd3O\xe3\x82\v.r\xb4\xc3?\u04a0\xb7u\x1c\u0623\x9d\r\xf8\\\xaa\xa4\x16\x9d\xf2\xcaZ\xd6/\x9aM\xb4\xae\x86\xbc\u0783\xe9\xd4{\b\xa6ab\xdb\x0e I!\x90\x9a\xc0\xf6)\u062a+\xd3S\x05\xfe\x80B\u042f\xa2\xa9\xa0\x06T\x02\xe1 \u04b0\xd9\xf8\xedl\xbe\xf8\xcb},x\xebi\xaaK\xb6a[\x16\xbe\xb4\fz\x1fy\x1c\xa7^\xfd;.\xb9\xe3\x01N\x9fp\"c\xfav\xa4\u007f\xb6\x8f.\xbaA\x86L\xa2\xd9\u03bc0\xed\x03\xdd$\x8e\x94\xfb\xa2e\xd9H\xdb\x00\xdb@U \xcd\x1f\xa0g\xe7<N9\xee\x18\u019f6\x81N]z\xd2\xd0`PQ^N<VOME\x11[V\u03e1|G\x01\xc1\xb4\f2s\xba\xa0\xf9\x02\x98F\x02\xe9\xedt4\xda\x10H\xaf\xbb\x92\x14H\xdb\xc62\x92h\xfe\x10\xed\xbb\r\"\xbbC>\xd2J\x12\xa9)#\x11m@(\x9a#\u007f\x942\xd5<[Q\x15!\x84\"kjk\xc5\u039dej\xf9\xee\xddg\xdes\xcf\xef\xd7|\xfc\xf1\x94\xf5\x9f~\xfa)\xd3gL\x17\x17_t1\bv\x99\xcf\x00\x00 \x00IDATO\xff\xf5if\u0398\xd9:\xc1[\xc1\xfc\xe7\x1dEE[\xf9\xdf\xff}\x1a)e\x9b\xbf\xfd\xed\xf9{\u05ed+\xe8\x18\x0e\x87\xe55\xd7\\-\x06\r:\x14\xcb2Z}X\x0e2\xa0\v\xc0\xa7\b\"\xb6N\xbd\b\xb2f\xfeWT\xef,\"\xb3}Wz\x1fq2\x96e9\xf2BM C\nvH%\x99\xa6`\xe9\n\xc2r\x8a\x88\xfc\x9a\x82\x8aDS5\xfc\x19!\xcawlf\xde\xcb\u007f\u2aff\xdeO\xc9\xdaeH\xdbB\xf1\a\xe9?\xf6\x14&\\{'\xe7\xdf|\x0f\xa3\x8e>\x82\x1e\xd9A\xfa\a-rE\x02\xd54\x1c\xe3*<;\x80\x83C=y\xc0nY\x06H\vE\x856\xe1\f\x8e\x18q\x18\xc7\x1cs\n9\xb9\xf9\xd4\xd4\xd4SV\xba\x1d\xcb2\xa8,\xdb\u0226U_\x11m\xa8\xa0MN\x17\xda\xe4vv]\x15\x8dT\u0751l${R\x04\xa9Db\xdb&RB\x9b\xb6]\xc9\xeby8\xe9m\xdaa&\"\xd4W\xef\xc42\x9dF\xd7N\x929E\xbb\bEQe}}\x83\xa8\xa8\xa8\xd0*+\xab\u03bc\xf9\xc6\x1b\xd7L\x9f1c\xfd\xe4\u0253\x99\xfa\xc9\x14q\xf9e\x97\xb7N\xecV0\xff\xf9\u01e0A\x87\xf0\xc9'\x9fq\xec\xb1c\xc7~\xf8\xe1\u01d7\x97\x94\x94\xa6\xf5\xea\xd5S\xdcr\xcb\xcd\xe4\xe6\xb6u\x16L\xeb8\xa8C\x02>\x15,\x14\xea\xf40k\x16\u007fK\xe9\x865\x04\xc2\x19\xf4\x1du\x12\xc1p\x1a\xaa_\"\xd2U\x94t\x15\xcb'0\x85@*\x02\xc5\x02\xdd\x06](\x04\x02!\x04\x925\xdfLa\xfa\xd3w\xb1j\xc6\xfb$c\x11\x00\xf2\x0f\x1b\xc3\xe97\xde\xc3\xf97\xdd\xcdQ\u01ce$7\x18 \xcd6\xc9S\x93d*f\x13?\x97\x83\x03\xe2\xfb}o\xa4t73\x03\x81Mvv:\u00c6\re\xe0!c\xc9\xca\xe9Bee\x05U\x95;1\x93qJ6/\xa5l\xfbJ\xd2\u04b3\xc8\xed\xd0\x1bE\xd3\xddJ\u0426\x15\xa5\xa4<\v<'I\xc7\xc2\xc2@\xf3\xa5\u046e\xeb :\xf7\x18B\x9b\x8cl\xea\xabJ\xa9\xafs\xc4\x02\x8a\xa2:\u0564Bq\xc4C\xaaB,\x1e\xb7*++}UUUgN\x9cxU\xe1\xec\u0673\v\xde}\xf7=\x1ex\xe8\x01q\xe7\x9dw1y\xf2\xe4\xd6\xc9\xdd\n\xe6?\xdf8\xf7\xdcs\xc5\xc7\x1fO!?\xbf\xc7y+W\xae<\xa3\xa2\xa2\x82\x91#Gr\xcd5\x13QU\r\xd3l\x05\xf3\x960\x14@U\x15\xaa-\x1f\x9b\xb6l\xa5\xe0\xdb\u0668\xbaN\x8f#\x8e!+\xbf;>\xbf\x81\xeeW\xb0qx\\\x81\xdb\xf4AJt\xa9\x92\x9e\u0786H\xed.\xe6\xbd\xfb\x14\xb3_\u007f\x88\x8a\xed\x1b\x01\bf\xb5\xe5\xc4\xcbo\xe4\xd2{\x1e\xe3\xb8S\x8f\xa3mZ\x10%\x91D\x97&\x1d|6\xed\xfd\xb6\xe3\xef\xf2\v!\xb8\xa2(h\x9a\x86\xae\xebh\x9a\xf7\xd0\xd045\xd5\xd1j\u007f'E\xdbv\"iU1\xe8\xd0>\x87C\a\x1fI\xbf\xfe#\xf1\xf9\xd2).\xdaB<\x1e\xa1\xbe\xba\x94\xe2\r\x8b0\x8d\x18\xb9\x1d\xfb\x11J\xcf\xc62\x93N\xe5g\xd3>\xd6{6\xb5\xb6\x9d\x1e\xa7\x96\x99$\x18\u03a5k\xcfat\xea\u069fD\xa4\x9a\xdd;7!m\x1bU\xd5R\xbdS\x05\x02EU\x95D<aU\xd7T\xfbjjjO\xbf\ubbbb\xea\xa6M\x9b\xf6\xfd\xd7s\xbff\xf2\xe4\u027c\xf1\xe6\x9bb\xea\u0529\xad\x93\xbb\x15\xcc\u007f\x9e\x91\x97\x97'\xbe\xff~\t=z\xe4\xffv\u04e6\xcdCjjj\x12g\x9du\xa6v\xf2\u0267\"\xa5\xd5j\xaa\u0542\xf8\x16]\x11D\xd1(\xad\x8e\xf0\xc3\xdc\xe9\xc4\xebk\xc8\x1f6\x9a\xbc\x81\x87b%\x93XR\x90\xb0\x04\xba\xea\xd02B@(=DZ DY\xe12\xbex\xf1\x0e\x96\xcex\r#\x11\x03\xa0\xef\x91\xc7\xf3\xdb?>\xc5o.\xbf\x8a\ue773Q\xa2\t|\xb6I\xa6.\xe9\x14\x90\xe4\xfa$\xaaH\u016d\at\xf8|>4\u034f\xaaj\b\xd7\xd9PJ\xdb}H\xb7\xb7\xa7\xe6\xf4+\xd5t4\xcd)\xe0\xd9\xef\xe2SM\xfc~\x83\xee\u077a2d\xe8\x18\xf2{\f\xa2\xbc|\x17\xa5\xa5[I\xc6\x1b\u0631y)uU\xc5d\xb7\xcf'\xabm7\xc7j\xc0\xb6HeB\x9bR/\x82Ta\x95\x94\x12#\x99\xc02mr:\xf6\xa4k\xfe`\xccX\x1de%\xeb1-\xd31\x1b\x13\x8d-\xedTUU\x12\u0244U]]\xed\xab\xaa\xae:\xf5\x81\a\x1e\xe8>\xe5\xe3\x8fg<\xf8\xe0\x83\xd6\u0529Sy\xe6\xd9g\u014c\xe9\xd3[\xe7w\v\x1f-^\u0372y\xf3\x06\u0473g\x1f)\xa5\f\x9dt\u0489\ud28b\x8bIK\v\xa9\x83\x06\rr#\x9d\xd6\xc4g\u02e1\x13@\x936m\x03\u042dG/\xd2s\xdaS\xbei-\x95E\x9b\x10@\xd4\x14\xc4-\a||\x8a@\x15\x92@z\x1a>\xbfB\xe1\x97\xd3\xf9\xea\xd9\xfb\xd9Q\xb8\u0519\x98iY\x9c}\xd9U\xfcv\xd2\rt\xcb\xefJ<\x9eD$\"\xa4\x05\x14\xc2\x1a\x844\x89.R\x01\xe9\x01\xa5H\xfc~?B8K\xa5\xba\xba\x82M\x9b6\xb3z\xf5j\xb6n\xddFEE\x05\x91H\x04]\xd7i\u06f6-\xf9\xf9\xdd\x190`\x00}\xfb\xf6\xa1]\xbbv\xf8|\x8e(2\xb1G1\x91t\x9b\\\xf8t\x81\xa64\x90\xdf\xddG\xe7\xff:\x8d\x11\xc3\a\xf1\xea\xab\xcf\xf1\xe6\x9b/\x90\x887\xb0\xee\xfbO\xa8\xad(b\xec\xe9w\x90?\xf0X\xe2\xf1(\x89D\xa4\x11\xc4]\x95K3^I\b\x14T,\u06e4\xa6\xa6\x92P\xb8#\xa3'\u0702/\x98\u0392\xf9\uf4ccG\xf1\aB\xa8\x8ak?\x8cDU55\x96HXK\x97\xfe\xa0J)/\xaf\xaa\xaa\n\x02\x17\x00\xdcx\xc3\r<\xf3\xec\xb3\xdcx\xc3\r\xad\x93\xbc\x15\xcc\xff\xf91g\xce\\\x01\xc8m\xdb6\xf7\x8dD\"=M\u04e4c\xc7\x0eb\xe0\xc0\x81\xad`\xde\xd2\xc0\x1c'!\x97\ud0ce\xedrh\u06f5\xa7\x03\xe6;\xb6QW\x17#j\xab$-\x13]\x11\x98\x96M(\x14\x00i\xb0x\xf2\xeb\xccy\xe91jv\x16\x03\u042d\xff\x10\xae\xbf\xedn~{\xe9y\xe4\xf8 a$0\x83NoO\x95\xc6f\x12\xf2\x00'8\x9d\x06\xd6\xe9\x80Ea\xe1Zf\xcc\xf8\x82\xa9S\xa7RX\xb8\x9e\xfa\xfa:\x12\x89d\xb3\xf9'\x84\xc0\xef\xf7\x93\x96\x16\"??\x9f\x13O<\x91q\xe3Ne\u0108a\xf8\xfd!\xe2\xf1H\xeayMA]QUTa\x11\xf0'\x181\xbc;C\x06?\u0148\xe1\x83x\xfc\xf1\xc7\u067c\xb9\x90\x92-\u02d9\xf6\xd6\xef8\xf6\xcc\xdfs\u0211\xe7\xa2(\x82x<\x82\xeb\xf4\xd2\xecd\x84\xe7\xad.\x1c\xe2K\u0692h\xb4\x81`(\x97#N\xb9\x01\u0757\u0192y\xef`\x1a\x06\xc2\xe7kTZJ\xd0\x14U5MS~\xf7\xdd\x12\x11i\x88\x9c\xff\xf0#\x8f\x88\xfb\xee\xbd\xf7b!\x84\u0675k\x17\x15\xb0\xa6L\x99\xc2o~\xf3\x9b\xd6\xc9\xdeJ\xb3\xfc\xdfG\u06f6m\xc5\xf2\xe5+d\u06f6m{~\xf7\xdd\xe2\v+++\xdbt\xef\u078dI\x93&\x89\xb4\xb4tL3\xd9z\x17[\x16\u04c2OU\xa9L\n\x96\xafX\u0156\x1f\x16\x92\x9e\u06d1\xbca\xc7!\x82\x19\x98\x96\x89%%\x81@\x00U\x1a\xcc\u007f\xe3\u007f\xf8\xea\xb9\ai\xa8*\a`\xf8\t\x13x\xe2\xcf\u007f\xe1\xca3O \xa4\x82iD\x11\xb6Ds)\x19\xb7\xc7\xc4\x01\x05q\xaf\xf9F \x90\u01ae]\xa5\xbc\xf8\xe2K\xdcw\xdf\x1fx\xf7\xdd\xf7\u063au+\x91H\x04\u06f6\xf1\xf9|4iE\xdaX\xfa\x1f\x8bQZ\xba\x93\x05\v\x160e\xcaTv\xec(\xa1G\x8fnt\xec\xd8\x19!\xc0r{\x95\xee\x19\xa9;\x15\xa8&>\x9f\xcea\x87\r\xe6\xb0\u00c6\xb3~\xc3V\x8a\x8b\xb7\x92\x885P\xbc\xf1;\xd2\xc3Yt\xee1\x14E\u0571,3\xf5\xa6\xefS\x13/\x1b?1M\x03\u0557F\xe7\xfcCQ\x05\xec,ZC\xd2H8\xfe.\x1eG\x83@(\x8a\x90H\xb9{W\xb9\xa8\xae\xae\x1eX^Q\xdeo\xf6\xacYS\xfb\xf5\xebg\u03593G\x1d7n\\\xabl\xac52\xff\xe7F8\x1cV\x00[Q\u0120\xf4\xf4\xb4.\x00\x9d:u\x12\xa1P\xa8\xf5\xee\xb5\xd0\xe8\\`\x93\x1d\xd2\xe9\u0439\v\xa0PWUNmu%9\xb9\x9d\x90R\x12Ho\x83\x99\xa8g\xce\x1bO0\xef\xf5\xff\xc1t\xbdT\x8e=\xe7\x12\x9ex\xe21F\xf4\xec\f\x18$\x13\xc6>[\xc7\x1dX\xaaH\xba\xc9L?\x8b\x17/\xe2\x91G\x1ee\xe6\xcc/R\x1e\u22a2 \xa5t\xf9s\r\xd3\xf5?Q\x145U5\xea\xd9\x02H)\xa9\xa9\xa9\xe1\x85\x17^d\u039c9<\xf6\u0623\x9c}\xf69\xa8\xaaJ<\x1e\xdf/\xa0'\x12Q4Me\u0318\x91<\xfb\xcc\v<\xf0\xe0\xc3|\xfe\xf9;D\x1bj\x98\xf5\xc1#\u0636\u0270\xe3~\x8b\xaa\xa84D\xebphtoW\x91\xcd{\xeb5\":\xf1D\x84\x80?\u0110\xa3/\u00d6\x92\xef\xe7\xbeE\"\x1e\xc5\xe7\x0f9\x85Tnd\xaf\xaa\x9a\xb0\xb1\xe4\xbau\x05B\xd5\xd4\xf3\xc2\x19\x19H)/\x11B$g\u03de\xad&M\xd3:\xf5\xe4\x93['{kd\xfe\u007f\x1b\xfd\xfb\xf7UV\xacXi\x0f\x1c8p\xdc\xe6\xcd[\x8e\u077d{\xb7y\xdcq\u01ea\xa7\x9d6\x01U\xd5Ze\x89-qR)\x02C\xd5X\xb1a;\x8b\xbf\xfa\x1cEQ\xc8?\xf2D\xb2\xba\xf6D\xd5|\b\xcb`\xd1\xcb\x0f\xf3\xed\xebO9v\xb4B\u5b097\xf1\xa7?=\xcea\xdd\xdac\x9bq\x92\xc6/\x9f\xd4\xf6\xf8qU\xf51s\xe64n\xbc\xf1&\x16,\xf86\x05\xe2\x9e\u007f\v\x80i\x9a$\x93\xc9\x14h{\tPUUS\x80\xdfT\xd1RYY\xc5\xe7\x9fO#++\x93\xe1\u00c7\xa3\xaa\xaa\xbb\x11\x88\xfdP<\x12)M:uj\xcf\xe0\xc1G\x11\x8dI\n\nV\x10\x8f\xd5S\xbcy\x19i\xe9\x99t\xeb3\x02M\u04f0\\\x9d9{)hd\xaai\x87\x87\xeb\x96e\xa0\xeaA:v\x1d\x88\"MJ\xb7\xaf\xc60\x92NR\xd7;\xfdH\x10\x8a\"\x84\xa2\xc8]e\xbbD}]\xdd@\xa1\xd0y\ua529\x9f\xbc\xf5\xd6[R(\xaax\xe0\x81\ax\xef\xbd\xf7Z'{+\x98\xff\xf4\u0467O\x1fu\xed\xdau\xb6\xdf\xef\x1fQZZzb}}\xbd\x9c0a\xbcr\xdcq\xc7\"\x84lU\xb2\xb4@\x9aE\x11 u\x8dU[\xcaX0}\nf,B\xaf#O\xa0\xeb\xd0C0c&\u07ff\xf1\x14\u07fd\xf6\x84\xa3\x9fV}\\t\xe3\x1d<\xfa\xd0\xfd\xf4o\x1f\xc6H\u01b0\xec\x83s\x92\xd7u\x1dE\u04595\xebKn\xbc\xf1f\n\n\n\xc9\xcc\xcc\xc4\xef\xf7\x13\x8f\u01db\x81s\xd3(\xbd\x11\x80\xed\x94U\xc0\x9e\xf3RQ\x14\x92\xc9$\xb3f\u0362]\xbb\xb6\f\x1b6\u048d\xec\x8d\xfd\x02\xba\x94`\xdbI\xb2s2\xe9\xdb\xef\b\xa4\xf4\xb3b\xc5w$bu\x14m\\JFf[\xba\xf7\x19\x01B`Zf3,oz]\"\xa5g\u013d6\x03\xcd\x17\xa2C\xb7C\xc0JPV\xbc\x163\x99L\x9d.\x9a<U\b\xa1\xc8\xd2\xd2RQ[W7\xf4\xa1\x87\x1fJ~\xf4\xe1G\vV\xad\\\xc9\xdau\xeb\xc5\xfb\x1f\xbc\xc7\x1bo\xbc\xd1:\xe9[\xc8h\xd1\xe6\u07f1X\x84\xf7\xdf\xff\xc0\x06H&\x93i\x91H\x04EQ\xc8\xca\xcaF\b\xb5\xb5\xea\xb3\x05\x93-> \x10\f\xa1\xea:F\xb4\x1e;\x11!\xe0\x83\x95\xef?\xcf\u04b7\xfe\x1b\xcbH\x82\xaas\u044dw\xf0\xf0\x03\u007f\xa0WN\x90x,\xc2A\xc2q7\xaa\xf6\xb1aC!w\xdf}\x0f\x1b6l$\x1cN\xa7G\x8f|\xc2\xe1\xf4\xbd\x12\xed\xd9\xd9\u0664\xa7\xa7\xa7~\xb6\x11(-L\xd3\xdc\u06e4\xcb\xe5\xe1\x93\xc9$\xbf\xff\xfd=\u03181\rP\xd0u\xfd\x1f\x9c\x16\x04\xc8\b]\xbb\x84\xb8\xe6\x9a[\xf8\xedUw\xa3\a\u04895T\xf2\xe5\xfb\x0f\xb3z\xf1T\xc2\xe9Y\xa4\x87\xd2\xd1u\xdd1\xd9J\xa9\xf8\xd9\xef&\x11\x8fGA\vq\xf8qW0\xec\xe8\vP5\x95d\"\x86e\xd9)\xf0O9\t\b!\x97|\xbf\x94\u0253\xdfy\xe4\xed\u0253\xcf\x03X\xb3z\x85\xfc\u04df\x9e\x14\x85\x1b6\xb4N\xf7V0\xff\xc7c\u04e6M*`I);\x1fr\xc8\xc0\xe3\xea\xeb\xeb\xd0u]\xe4\xe4d\xe3T\xbd\xb5*YZ\x1e\x8c;\xc5;\x01 '\u034f\xe2v\xc41j*X\xf3\xf1T\xbe}\xe9\x11\xe2\xf55\b\xdd\u03f97\xdc\u0243\x0f=@\x8f\xcc F\"\xbeW\x03\xe3_r\xf8|~\xa2\xd1z\x1ey\xe4Q\x96.]\x86\xcf\xe7#++\x9b\xea\xeaj\x8a\x8a\x8a\xf7z~CC=\x86\x91\xdc+\n\xb6m{\xafy)\x84H\x15\x13\x01\xd4\xd4\xd4p\xd7]\xbf\xa7\xb8x\x1b\xaa\xea\xfb\t&q\x82\xa0?N~\xf7 \xd7]w+\xd7^s\x1f\x81`\x98\xba\xea\x9d\xcc\xfc\xfb\x1f\u0670\xf2KBi\x19\xa4\x05\xd3\xf0i:\x9a\xaa!\x14\a\xd0=P\xf7\xca\xf9\x9b\x02z\"\x1eE\rf1\xe2\xe4\xab9\xfc\xd8\v\xd0\xfd\x01,\xd3\xc0\xf6\x1a`\v\xc5K\xae\x8ah,\xce\u04a5\u02d4\x8f?\x9e\xfa\u03b4\xe9\xd3\u007f\x030c\xfa4\xa9\xbb\xcd`J\xcbv\xb6N\xfe\x83<Zt\x02t\xf7\xee\xdd\u07a7\xc1D\"\x99fY6~\xbf*22\xc2{-\xa2\xd6\u0442\x00\u0775km\x17RP\xa4\x03l\xeb\xe7M#V\xf7\xff\x88\xd68\xa5\xe5'\xfd\xd75\xdc~\xcf}\xf4\nkX\xc9(\xf6Atcv\x80V\xe1\u33e7\xf0\u99df\x01\x90\x99\x99I\"\x91$\x16\x8b2t\xe8\x10\xbav\xedJVV&\xa1P\x88\xb2\xb2],Y\xb2\x84\x1d;JP\x14e/\xf0\xde\x17g\xee6`\xc60\f,\xcbb\xf5\xea5<\xf7\xdc\xdfx\xe2\x89?\xe1\xf3\xf9H$\x12?\x1aIK\x01>=J\xe7N~\xae\xbcr\x12\x91H=o\xbe\xf1\x14\x95\xbb\xb60c\xf2}\x9c}\u074bt\xea>\x04a[\u0130\x91\xb6\x8d\xad\xe0\xf6\vu\xb4\u4379P\x91re\x8cE\x1b\b\xa5\xb7a\u0109\x13\xc1\x96\xacX0\x05\xcb4\x10\x9a\xee\xde\x11\x05\x90h\xba.kj\xeb\xc5\xf7\xdf\u007f\xafdee\xbe\xbbf\xed\xdas\x0f\x198\xf0\u04de={\xb5v-j\x05\xf3\u007f<\xaa\xaa\xaa\xbcO\x8d\x86\x86\x86$8\xf6\xa5>_\xa0\x15\xcc[\xf0\xf0\xa0-S\x93\xe0&\xa8\xb7.[\x90\x02\xbd\x11\xa7\x9e\xc5\xed\xf7=\xc0\x90v\x01l3\x86y\x90o\xa3\xae\xeb\xd4\xd6V\xf1\xfe\xfb\xefS[[K\x9b6\x19\b!\xd04\x95+\xaf\xbc\x82\x1bo\x9cD\x8f\x1e\xbd\x9bD\xdf\x06_~\xf9%\x8f=\xf6\x04\xdf~\xbb\x10UU\xb1,\xa7C\xa9\xa7\x86\xf1d\x8a\x9e\xc2\u0176m\xd2\xd2B\xa8\xaaB$\xe2\x14\x10\xbd\xfb\xee{\\~\xf9e\xf4\xeb7`\x9f\xfe\xe9\xcd\xdeS\x1b\x14\x05B\xc18\xf9\xddC\\w\xedmTW\x973\u58d7(\u0776\x8a\xd9\x1f<\u0339\u05fdB \xad\r\xa6i`\xa9\x16\xa6\xe54\xbd\x964\xa9P\xa5\x89\xf2\xc5\x05\xf5x\xa4\x81P8\x8b\x91']\x8d\xb4,V,\x9a\x8ai&Q\x15\xcd\xdd\xe8\x1c\xe3\x05]\xd7eyy\x85\xf8\xe6\x9by\xbe\x8cp\xf8\xb5\xfa\xfa\xfa\x13\xc3\xe1\xf0r!D\x13O\x81\xd6\xd1J\xb3\xecc4\x8dVL\xd3LE8\x9a\xd6\xda\xe7\xb3eG\xe6\xce\xc7t\x1d|\xba\x9a\xa2\x1f\x00\xfa\x1d6\x92\xbb\xfe\xf8\x00\xa3{d\xa1\x99qLK\x1e\xe4k\x95\x80\xca7\xdf\xccc\u0672\xe5\xf8|:\xa1P\b\u02f28\xed\xb4\xf1<\xfe\xf8\xa3\xf4\xe8\xd1\x1b\xdb6\xb0m\x03\xcbJ\xa2(\n\xa7\x9c2\x9e\xc7\x1e{\x94~\xfd\xfa\xb8\xba\xf1\xc6\xdf\xe9\xa9^\xf6\xe4\xd2\x13\x89d3\xcae\xfb\xf6\xed|\xf8\xe1\x87xA\xca?\xdc$m\x87\xfe\b\xf8c\xf4\xef\x9f\u036d\xb7\xde\xc3\xf0\x11'\x00\xb0n\xe9t\xbe\xfb\xeaE\x14EC\xf7\x05\xf1i*\xba\xab\xac\xf1\xeco\xc1\x05w)\x91\xee\xc6#\xa5\xd3X:\x1ai@\x0fd1\xf2\xe4\xab\x198|\x1cB\x82i$\x1b7\x18\x97?\xf7\xf9\xfc\x94\x96\ucd27M\x9f\x91\xf3\xd0#\x8f\xbe!\xa5\xec\x04\xc8W^{\xad\xb5\xcdW+\x98\xef\u007f$\x93F\x93\x89\u072aZ\xf9\xb5\x8d\xdc\xecl\xb7\u035a3\xdaw\xcc\xe3\xc1\a\xefg\xc2\x11\x83\xf0[IL\xcb>\xe8\xe1\x9c\a\xac\xdf}\xf7\x1d\xa5\xa5\xa5\x84\xc3a\x84P\xc8\xcb\xeb\u0239\xe7\x9eC \x90F<\x1e\xc10\f\f\xc3\xc04M\x12\x89\x04\xb6m0f\xccXN>\xf9\x14\xb7xho\xde|O\xaa%\x99Lb\x18f\xb3\xa4\xe7\x9c9s\x89\xc5\x1a~r\xc3\f\xe9\xf2\u067a\x16a\xe4\x88n<\xf0\xc0\xe3t\xeb\xd6\a\x80\xf9\u04dee\u00ca/\b\x04\u00e8\xaa\x8a\xae*\u8aa3\xbaAx}Mid\xd2]\x91\x8b\rH\xcb\x01\xf4@(\x97Q\xa7\\G\xff\xc3N\u0136\r\x92\u0244\xdb|Z\xa4\x1e\xbaO\x17%;J\xf8\xfc\xf3\xcf\x0f}\xf4\xf1\xc7\u07d6R\xe6^u\xe5\x95\xd6\v/\xbe\xa4\xae\u07f0\xbeu\u2dc2\xf9>8 M\xdbk\xd1Ii\xbbG\xda\xd6\xd1b'\x95\x17y\x16\x15QSS\xedF\x9e\nw\xdd\xf9;\u039bp*\x8a\x11\xc30\x8c\x83\x0e\xe4RJt]g\xf7\ue76c[\xb7\x0e\x80\x9c\x9c\\\x84\x10t\xed\u0695\xbe}\xfb\x02\xd6>\xf9`Oz8l\xd8\xe18\t\xf9\xc6!<WB\xd1L\xe3\x97\xea/\xda4j\u07fcy\v\u02d7\xaf\xe0\xff\xa2\x12\xf6|]\x14\x11g\u0729\xc3x\xe8\xe1\xc7\b\x85\xc2D\xeb+\xf9\xe6\xd3?SS\xb1\r\x9f\xdfQ\xdah\x8a\x82\xae\n\x14\xa5\xd1\xcf\u073b\x1e\xa5\xc9\x03\xe9h\xdb#\x91\x06\xd22:2z\xfc\r\xf49\xf4\x18,3\x81a$\x91M\x1cpl[\nU\xd3\u5dad\xdb\xf9\U0008bbce\xfd\xcb\xd3O\xdf\x02p\xed5W[\x93&\xdd$\xcav\x95\xb5.\x82V0o>\xd2\xd3\xd3R\x9f\xfb|\xbe\xd4\x02\xf4\x16Rk\u04a5e\x0e]\x0f\x10\x8fG\xf8\xdf\xff\xfdKJ\tr\xf1\xc5\x17s\xd5o\xafDJ\x93\xa4a\xee\xd5~\xed`\x81\xb9\x10\x1aEE\xc5\x14\x15\x15\xe3\xf7\xfb\xc8\xca\xca\xc40\x1c*%\x18\f\xfd\x03z\xc6I\x94\x06\x83\xc1\xbd\xc0\xdc)\x1e\x12\xfb\xa0J\xec\x14e\bN\x92\u007f\xed\u06b5\xff\x14\x95\xe5\x045&\x97^r6\xb7\xdcz+\x00[\n\xe6\xb3\xf4\xeb\xb7P\x14\x15Us\xfa\xa6j\x8a@W\x9c\x9e\xa7\u04b3Vt\xd5-.{\x92\xb2I\xb0%D#u\x84s\xbar\xd4i7\x92\xdfo$\x96\x11\xc34\f\xa7\x85\x9d\x10^\xf7\"\xa1\xea\xba\\\xb1b%_}5\xeb\u0799_|1\x11`\xd6W_\xc8{\xef\xfdC\xeb\xc2l\x05\xf3=\x8e\xe9\xb99)|\b\x87\xc3>\x00\u04f4\x88\u0162\xadw\xae\x85\x9f\xa6^{\xed\xf5\x942d\u0630\u00f9\xfb\xee\xbb\b\x873\x89\xc7\xe3?\x99R\xf8\xa5\u01ae]\xbb\xa8\xa8\xa8@UU6m\xda\xc4\xee\xdd\xe5l\u0672\x95\xd2\xd2\x12@\xfdQ\t\xec\xb6m\u06e8\xaf\xaf\xdf\v\xe8\x1d\xaaE6\xfbZ\xd32\u007f\x0f\xf4\xe3\xf18;v\xec\xf8\xa7\xaf=\x99t\xe4\x91w\xdc~\vc\x8f>\x11\x80\u0173_g\xcb\u06af\xf1\xf9\u04f0\u0756q\xaa\x10(\u00a1X$`#\xdd*\xd3\xc6k\xf3\x14.\x96m\x13\x8b6\x90\u04f9?\u01de};]{\rs\xbay\xe16\x96\x16\x8a#}\x14\x8a0-\x8b\x1f~X\xc1\xab\xaf\xbd\xfe\u0127\x9f}:\x02\xe0\xd5W^\x96\x0f=\xfcH\xebbh\x05\xf3\u0191\x93\x93\u3b46J\u02f2w\uae8ei\x9a\xb2\xb6\xb6\xae\xd9q\xbeu\xb4\x8c\u0474\xf0\u6957^\xc60\f22\xc2\\\u007f\xfdu\xf4\xeb7\x10\u00c8\xb7\xa8{\xe6\x81jmm\x1d\x91H\x14\u06f6\xa9\xact\x14Tk\u05ac\xe1\xddw\xdf\al\x02\x81@\xb3\x8aN\xc7d+H}}\rs\xe6|MUU\xf5>\xc1\xfc\xc7\xd4){&G\xe1\x9f;\xac8\x11z\x82\xcc\xcc,n\xbb\xf5v22r\x89\u0515\xb3\xe4\xeb7\x89\xd6W\xa2\xfb\x82\xce\xc6\xe2\xd4\xe6:\u007fW\x8af\u0562\x8e\x0eG`#\\\xf5\x8b\xc02M\"\r\xf5\xb4\xebv(\u01ddu\v\x1d\xbb\xf4\xc30\x12\xa9&\u04e2Io\u044a\xca*f\u035a\x93\xfd\xdcs/L\x9e:uj\x1f\x80?\xfe\xe1>~\u007f\xf7\u077c\xfc\xca\u02ed\v\xa3\x15\xcca\xc0\x80Av\x87\x0e\xed5!DUaa\xe1\xa2p8\x8ca\x18\xb2\xa6\xa6\u0199~-\x98f\xf18S\xaf+\x8d\xcf\xe7K=4MsU\x06\xe2\u07ca*\xf2\"\xeeW_}\x95\x95+W\x010~\xfcx.\xbc\xf0\x02\xa0\xe5Z/4m0\xd1(\u0143g\x9f}\x8e\x97_~\x19!4B\xa10\xba\xae\xa3\xeb\xbak\x8bk\xf2\xec\xb3\xcf3\u007f\xfe\xfc}\x82vS^|_\xf7\xb8\xa9\x14\u0463i\xfeY\xa5\xadC\xdbH\x8e?\xe1x.\xbc\xf8*\x00\n\x96\xcd`\xcb\u06b9h\xba\x0f\u02d68i&\x97\xcbW\x1a#t\xefO\xca&\x9b\x83m;t\x8biZD\"\xf5t\xea3\x82c\u03fc\x91\x9c\xdcN\x18F\x1c[\u068dQ=\xa0\ubeac\xad\xad\xe3\u06c5\x8bz\xbd\xf0\xe2K\x1f\xaf]\xbb\xba7\xc0\x13\x8f?\u0395W\\\u054a\xb2\xad`\xeeL\xf8\x11#F\x00\u0436m[\xdb+\x9f./\xaf\xc0\xb6M\xa7B\xad\x05\x02\x9a\xdf\xef\xc7\xe7\v\xe2\xf3\x05\xd1\xf5\x00RJ\xea\xeb\uba69\xa9\xa1\xa1\xa1\xc1Q#\xe8\x81\xd4s|>\u07ef\xfe\x94!\xa5DU},\\\xb8\x80\x8f>\x9a\x02@\xe7\u039d\x988\xf1*\x02\x814\x92\xc9x\x8b\u0778\x02\x81\x00>\x9f\xaf\xc9\xe6\xea\x00qmm-\xf7\xdcs/w\xdf}\x17\v\x17.\xa0\xaa\xaa\x8a\x9a\x9a\x1a\xbe\xff~\x11w\xddu\x0f\u007f\xfb\xdb\vMk!\xf6\xe1\x82(\x9b}\xdcWd\xee\xf7\xfb\xc9\xce\xce\xf9WW\n\x86\x11#=M\xe5\xa2\v/\xa5g\xaf!\u0636\u024ao\u07e7\xb6\xb2\x04\u055f\xe6F\u070d\xcfol9\xd7\u0737E\xba\x91\xba-A\xda`\x99&\r\xd1(]\xfa\x8fe\u0538\xab\t\xa5g\x92\x88G\x9a\x9c<\x04B(\xc2\xef\x0f\u0206\x86\b\xdf-\xfe~\xe0S\xff\xfd\xbfS\xa4\x94}\x00~\xff\xfb\xbbZ\xf9\xf3_\x8a\xe2l\xc9\x17\xb7sg\t\xf7\xde{\x1f\x00\xfd\xfb\xf7\xd7JJJ(**\x92\xa5\xa5\xa5\x98\xa6\xe3\xfb\xdcbvEEA\u05ddb&\u00c8\xb3z\xf5r\xbe\xff\xfe{6l\xd8\xc8\xee\u077b\x89D\"\x98\xa6\x85\xaek\x84\xc3a\xf2\xf2:1p`\u007f\x86\x0f\x1fN\u07fe}\u071f\x95$\x93\xf1_]1\x94\xa7\n\xb1m\x83\u0253\xdfa\xf3\xe6\xcd\x00\x9cs\xce\xd9\x1c{\xec1\x80\xd5\"_\x93\a\xa8YY\x99dddP[[\x8b\xa6\xa9)\u0149\xa2(TTT\xf2\xc4\x13O\xf2\xe1\x87\x1f\u04ff\u007f\u007f\x82\xc1\x00\x9b7oa\u02d6-\x18\x86\xe1\xf4\x1eu9\xf5\xa6\\\xf8\xfe@|\xcf\u0476m[\xbaw\xef\xf63\xdc\x03H$\x1a\x18:\xa4?\xe7\x9d\u007f%\x8f?z\x13[\n\u6ce5\xf0[\xfa\r\xff\x8d\xd3+\xd7J\xba\u0478H\xb5\x99s\xaeQ\xa6\xe8\x16\\\x80\xb6\x05(R`\xdb`\x1a\x06q\x04}\x87M \x11\xade\ue53f\x92\x8cG\xf1\x05B\xde\x1b\x89\x00\x11\f\x86dMM=\xb3g\xcf\x1dx\xdf\x1f\xfe\xf0\xb6\x94\xf2\f!D\u0663\x8f=\xae\xa4\xa5\x85\xec[n\xbe\xb9\x15q\xffS\xc1\xbcc\xc7N\\w\xdd5\x12 ##\xbc\u057b\u6492\x12\x99L&\x85\u05d2\xeb`\x03\x99\xd7V,\x99\x8c\xf1\xd5W\xb3\x98<\xf9\xef,_\xbe\x82\xb2\xb2\x9d\xd4\xd4\xd4\xee\x17Hrrr\xe8\xd4)\x8f\x11#\x86s\u99572f\xccQ\xf8|AW\u07db\xfc\xd5P0\x8e[\xa0\x8fo\xbe\x99\xcb\xf4\xe9\xd3\x00\xe8\u07bd\x1bW^y\x05\xa0b\x181Z\xf6<\u02e3}\xfb\xf6l\u0672\x856m21\x8c\x9a\x94V\xdc;1m\u06b4\x89M\x9b6\xb9^\xe7\x0eMf\x9a&\xa6i\xeds\x83h\n\xec{\x82\xba'S\x04h\u07fe\x1d\u077a\xfd<`\xee\x9c2l&L8\x9d/f~\xcc\x0f\u02fef\xed\xe2)t\xe97\x96P\xb8-V\xd4Db\xbb\x8d4\x1acq\xdbkL\xd4\u060c\u03b5\x10\xb0Ql\x81\xb0\xc10\x12(\"\xc0\xa0\xa3\u03a3\xben7\xdf}\xf16\xa6\x91@\xd7\xfd\xc8T\xb0/DZZ\x9a,.)\x95\x9f~\xfa\xf9\b\u02f4\xee\a\xae\xbb\xf7\x9e\xbbm\x80\x0f>\xfa\x88s\xcf>\xbb\x15u\xff\x13i\x16wQH\x80\xcc\xcc\u0302d\xd2\xd8\x04\xb0c\xc7\x0e\xdb\xe1\xcd\x0f\xae<\u0476mt]G\b\x95\u014b\x17q\xc5\x15Wr\xd9eW\xf0\xce;\xefRXXHMM-\xaa\xaa\xee\xa57\xf6\x16{EE\x05+W\xae\xe2\xe5\x97_\xe5\x82\v.\xe4\xfa\xeb\xafc\u035a\x95(\x8a\xeeF\xba-_O\xefE\xe5\xa6\x19g\xea\u0529l\u06f6\x1d\x80\v/\xbc\x90A\x83\x86\x00\xcdU\x1d--27\xcd\x04=z\xe4\u04f7o_l\xdbIv6-\xea\xf1\x80\xd8\xcbqx\xd2\xc2h4\x9aR\x92\xec\xeb=\xf9\xb1\xb9\xa9\xaa\x8d\n\x99\u07bd{\u04ebWO\xa44\u007f\x96\xe5l\xdbQ\x86\f\xe9\xc6Yg_\x8a\xaa\xeal-\xfc\x96\xe2\r\v\x9d\xe8Y\xd3\x1dO\x00!\x90BA\xba2C\xa1\xb8\x92\u0166f\\\x12\x90\"E\xb7H\x04\xc9d\x02\u0157\xc6\x11\xe3&r\xe8\x91\u3476\x85\u0133\th,E\xf2\xfb\xfc\xac+X\xcf\xf4\x193\xaf\xbd\xe1\u019b\x9e\xf2\xae\xee\u0733\xcfn\xb5\xe0\xf8O\x06\xf3\x13Np\u0295o\xba\xe9\x96h\xbbvmc\xe0\xb8\xce\x15\x15\x15\x1dT0\xf7\"r\u06f6y\xf9\u55f8\xe4\x92\xcb\xf8\xfb\xdf\u07e5\xb2\xb2\xb2\x19X\xec+\u0269\xaa\xaacW\xea\x96[\x03\x94\x94\x94\U000b7ffd\xc8\xc5\x17_\xc2G\x1f}\xe0r\xef\xbe\x16\x0f\xe8\x96e\xa1\xeb\x01\x96/_\xc1\xb4iN\a\xf7\xfc\xfc\xee\x9cw\u07b9\x00$\x93\x89\x16{\xedB\b,\xcb\xc2\xef\x0f1t\xe8P\xc2\xe10\xb1Xt/\xe9dSu\u029e:\xf1\xfd\x01\xb9\xe7w\xbe/:\xce{NFF\x06#G\x8e\xc4\xe7\v\xfel\xc9a\xa7:\xd4b\xfc\xb8\xf1\f\x1b~\f\xb6e\xb2a\xf9t\f#\x86\xa6\xfb\x11( \x14\x84\ua078\u04b8\x8e\x14\a\xd0e\x93\xe8\x1c\xc0\xb2%\xa6ec\xd96\x91\xfaz\xb4`\x16\xa3\u03f8\x8e\xfc~\xc3H\xc6\x1be\xc2\x12/w\xa2\n\xa1(r\xe3\xa6-,Y\xba\xec\xf6?\xfe\xf1\xfe?\xed\x19\u0334\x8e\xff@0\x1f=\xfaH\xe9N\x82\x02\x9fO\xdf\x10\n\x05\xa9\xa9\xa9\xb5\v\n\nS\x8b\xe3`\x8c@ \r\xd34y\xea\xa9\xff\xe1\xb6\xdbng\xe3\u018d\xfb\\\u0626i\xa6\x00\xd9+&\xf1>\x06\x83\x01\xfc~\u007fJ%\xa1\xaa\x1a\xabV\xad\xe6\x86\x1bn\xe4\x85\x17^\x04\x04>\x9f\xbfEG3\x8e\xae\xdcb\xee\u072f\u0678q\x13\x00\xa7\x9dv\x1a\xfd\xfb\xf7k\x06n-}\x1c\u007f\xfc\xb1\xf4\xe9\u04d7\xba\xba:TUiv\x0f\xffQ\xa4\xbd\u03c5\x95\xaaX\x96\xfb\x05\xf3\xee\u077bs\xe2\x89'\xa4Ny?O\x90!@&\xe9\u07ff\x1d\xa7\x9c\xea4^\u07b6~\x11\x15%\x85\bE\x03UE\xb8\u0560RU\x9c\xc2SE8\x0f\x0f\u061b\x81\xb3\xb7\x99\x81e\x83mY4\xd4\u0551\u046e\a\u01dc=\x89\xdc\x0e]\x88\xc7\"N\xc4\xdf\xe4\x87uM\x17I\u00d4\xeb\xd6\x15\xf0\xfd\x92\xa5wN\xfe\xfb\xe4\u06fck|\xfeo\u007fkE\xf4\xffD0\xef\u0631\x8b|\u5557\x04@\x9f>}k;w\xeeB\"\x91\xb0=\xe9\xdb\xc1(@\xf1\xfb\x9dD\xe73\xcf<\u01e3\x8f>FCC\xc3O\x8e\x9aTUu9W\a\xa8u]G\xd34TUC\xd75\xfc~?ee\xbb\xb8\xf7\xde?\xf0\xf6\u06d3QU\x1d\xbf\xbf\xe5\x02\xba\xdf\x1f\xa2\xb8\xb8\x98\xcf?w\xb8\xf2\xcc\xccL&L\x18\x87\xdf\x1f\"\x99\x8c\xfd*\x16\x81e%\xe9\xd7o '\x9exB\xaa\x9d\x9b\xdf\xef\xdf\xe7\xe6\xdc\xf4\xb4\xf5cN\x87^\xe9\xfe\x9e\xf7\xdf;\xcd\b\x01'\x9ex\x02\x03\a\x0e\xe0gO\x10K\x89\xdf'9\xef\xdc3\xe9?`(\xf1\x86j6,\x9f\x81P\x15\x14UsA[`\xab\x02\u02efb\xeb*R\x15HE \xf7B\x04\x99\x92\xb8XR:\xc9S)i\xa8\xaf\xa5c\x9f\xe1\x1cu\xfaU\x84B\xe9\xa9NE4\xb1\v\xf0\u9e88\xc5\xe2,]\xf6\x03\x9f|\xf2\xd9\xfd\x8b\x16-<\n\xe0\xfa\ubb93\x1f\xb8\x06c-y\xac_\xbf\xee_\xfc\xf9\x82V0\xdfs\xec\u0631C\x02t\xed\xdau\x99\xa2(\x12\xf0\x17\x16\x16\x12\x8d6\xfc\xe2/\xc1\x89D\x15>\xfdt\nO>\xf9$\xd1h\x94\xb4\xb4\xb4\xbd\x9ew\xf2\xc9'q\xc7\x1d\xb73q\xe2U\xa9\x04\x97\x97PSU\x15)!\x1a\x8d\x12\x8b\xc5\xdc^\x92V*r\v\x04\x02TUU\xf1\xf0\xc3\x0f3o\xde\\\x84\u041a\xf9\u0534\x98H\xc0\x8d\xc6V\xacX\xc9\x0f?,\a`\xec\u06238\xf4\xd0C\x01\xfbW\x13\x95{\xa0{\xc5\x15\x971d\xc8\x10b\xb18\xc1`\xe0'\x95\xf3\xefo\xd3\xdeW\xa4\xdd\xe8^\b\x03\a\x0e\xe4\xca+/\xc7I\x10\xff\xbc}l\x1d\x9e\xdbd@\xff\x8e\x9c~\xfa\x04\x00\xb6\xae[@\xbc\xa1\x12U\xd7A\x11)\x9e\xdb\xd6\x15\x8c\xa0\x82\xa9\v,\r\xa4\xe2x\xa7\xbbt:\u04a3]\\Su\xcb+*\xb2m\x1a\xa2\t\xfa\x8f9\x8da\u01df\x8em\x19\u0636\xebc\xd3D_\xafi\x1a\xd5\xd55|\xbbpQ\u01bb\xef}\xf0\xb6\x94r(\xc0\xb9\xe7\x9c\xd3\"\xe6\xc7\u018d\x85\xfb\xfd^\u07fe\x03\xfe\xa5\xdf\u0777o\xff\xbd\xbe\xb6e\xcb\xc6\x03\x87M\xbf\x86\xc56d\xc8`\x00\xce>\xfb\xcc\xd5\x1f~\xf8\xe1.\xa0CQQ1\x1b7n`\xf0\xe0\xc3\xfe\xa1\x17\xf4\xcf\u0271\xaa\xaa\x8f\xe2\xe2\xed\xfc\xf9\xcf\u007fa\xf7\xeerrrr\xc8\xcd\xcda\xfb\xf6b\f#\xc9\xd1G\x8f\xe5\x96[nf\u0528#\xc9\xce\xce&\x91HPPP\xc0\x9f\xff\xfc\x17\xfe\xfe\xf7wH&\x13dffb\x18&\xf5\xf5\xf5\xa9\xeb\xb6,\vEQ\xf0\xf9\xf4\x94V}\xf3\xe6-<\xf1\u0113\f\x192\x84\x8c\x8c,,\xabe\xd9\x18\xe8z\x80X\xac\x81\u0673g\x13\x89D\x00'\xdal\xdf>\x8fX\xac\xe1W\xa3\x9dw\xfc~\x92\xf4\xee\u074f\x9bo\xbe\x89k\xaf\xbd\xce\xf55w|Z\xf6\xe4\u021b\xea\u01fd\xc20E\x11n\xbb\xb8\x1f{\xbf4\f#Izz\x1a\x93&Mb\xc0\x80AHi\x1e\x90\xbc\x88\xe1Jw/\xba\xf0l^}\xe9\x19\u028b\v\u0639u\x19=\x0e\x1bO\xd2H\x80\x90\b\t\u00b2As\xa3r\x01\x12\x05M\u06a9\x86\x18 \x91\xb6\xa7F\x97\u062e\\Q\x95\x02#\x99\xc0\fe0\xea7WP\xb6\xbd\x90\xf5\u02d7\x12\b\xa55\xd9\t\x9c\nQM\xd3)/\xaf\xb4\xe6\xcc\xfd\xba\xdb\x1f\xfex\xff\x93R\xcaqB\b\xe3\x8c3~sP|\u043f\xfcr\xa68\xe9\xa4S$@\xef\xde\xfd\xfe\xd1\xdc\u040b\x8b\xb7\x1d\xbbl\xd9\x0f\x9d~\xf8\xe1\a\xa3\xacl\x97\x15\b\x042\xbbw\xefv\x88\xcf\xe7\u03c8D\"\xbb7l\u0638\xda\xef\xf7%{\xf4\xc8W\x87\f\x19\xa2\x0f\x1e|\xe8\xf6v\xed:\xce\xde\xd7\xef\xf3|\xf1\x9f~\xfa/b\xe8\xd0!r\xec\xd8c\xfe\xb3\xc0|\u0528Q\x00\xf4\xea\xd5o\xfe\xf8\xf1\xe36\x86\xc3\xe9\x1d\x8a\x8b\x8b\xf9\xee\xbb\u017f(\x98{\x94\u039bo\xbe\xc5\xf7\xdf\u007f\x0f@,\x16\xa3\xb8x\a\xe3\u019d\u00ad\xb7\xdeB\xbf~}\xc9\xcdm\x0f@2\x19E\xd7u\x86\x0e\x1d\xc6s\xcf=\x83a$y\xef\xbd\x0fH$\x92)u\u011e\u05a9\u0264\x81\xdf\xefD4\x86\x91d\u039c\xb9\xbc\xf7\xde{L\x9cx-\xba\xae\xff\xecQ\u073f:v\xef.g\u07bc\x0584Xo\x86\r\x1b\xf6\x0f#\u05d68L\xd3DUU.\xb8\xe0<\u05ad+\xe0\x89'\xfeD\xc1 \xe3A\x00\x00 \x00IDAT]]\xad[\xca\xefT\x88\xee\xebu)\x8aB8\x1c\xc64M\f\xa3\xa1\xd9s\x9a\xde_]\u05f1,gS\x988q\"\xff\xf5_\x17\xe1\xd4\x15$\x0f\xd8\x06\x050\xa0\u007f_F\x8d\x1e\u00e7\x9f}\xce\xf6\xc2\x05\xf4\x1dq:BU\xb0\xb1Q$\xf8\xa5\xc0\x92\x12Kq\x146R\xb3\xb1m\x81\xb0%\xb6\x02B*\bl\a\xe8\xdd\r\xc0\x96\xa0HP\x10D\x1b\x1a\xc8\xed\u0715\xa3\u03fd\x92]E\u06e8\xad\xac$\xe0\x16\xf7y\u01b9\xaa\xa2 \x84\xaal\u07bc\x95\x99_|yBfV\xe6\x8bR\xca+\x85\x10\xf2\xfd\x0f?P\xcf;\xe7\xdc\x03Z\x1a\xbcr\xe5\x0f\f\x1e|X\xea\xff\x1e\x90\xbb\xefS\xa0\xa4\xa48TXX8z\u0252%\xf9\xb5\xb5\xb5\x87\xf9\xfd\xfe\xa3\x8b\x8a\x8a\xa2\xf5\xf5\xf5\xf6\xc9'\x9f\xa8\x1b\x86\xd5.\x12i\bF\xa3\x11\x99L\x1a6\b_(\x14\xd4\x15E\xc54\r\xa2\xd1X\\Q\x84\xbd`\x81_|\xf4\xd1\x14%\x1cNo8\u7733\xcb{\xf5\xea\x95\x00\xb9\xb0c\u01ce\x9b\x86\x0e\x1d\xb2}\u0420C\xe6ge\xb5\xdd\rp\xf3\u0377\xc8=\xe9\xbb\xff\b0o\u05eec\xea\xf3^\xbdz}\xb3j\u056a\xa3\x8a\x8bw\xb0`\xc1\x02&N\xbc\xaa\x99\xd4\xeb\xc0\x82\xb9\x8f5kV\xf2\xd1G\x1f\x11\x8b9\x15\x8d\u0468\x13-\x8f\x1e=\x8a1c\u01ba \x1e\u00f6mW\xfa\xe64\xf7\xcd\xc8\xc8\xe2\xb6\xdbn\xe5\xeb\xaf\xe7\xb1k\xd7.4M\xdb\xe75\u06f6M\"\x91$\x10\b\x10\b\x04\x88Fc\xbc\xfe\xfa\x9b\x9cv\xdait\xe8\xd0\t!\xcc\x16\x01\x94\xde\u01b6l\xd926mr\x12\x9fc\u018cf\xf0\xe0CS\r\x1c~m#\x91H\xe0\xf7\x87\xb8\xfb\xee\xbb0M\x83\xe7\x9e{\x9ex<N(\x14\xc40\x8cf\x16\xb6M\xefW\xd3\x13\u05be\x005\x18\f\xa4\xb4\xf8\x97]v\x19\xf7\xde{7\xe9\xe9mH$\x0e\xecI\u02f6\r4=\xc0Y\xe7\x9c\u0367\x9f}\u038e\rKH\u0517\x12\nf\x93HDQ\xb0\x91B U\x89\xa5J\x92&\x18\x16\xd8:(\xa6DH\xe9D\xe7\xaa\xe2\x14\x18)\xce\u05fcf\u03f6\x04,I]]\x9c^G\x1c\u03c8\x93\x971\xeb\xdd\u05f1L\x13U\xd3\x1c\xcb\x00\xe1Q?R\b\x81\xbd~\xfd\x06\xe5\x9bo\xe6_\x91\x97\x97W\x06\xdcs\xde9\xe7Ze\xbb\xcaD\x87\xf6\x1d\xfe\xe5I\xbdn\xdd\x1a1o\xde|\xa5\xaa\xaaRy\u8847e<\x9e0\x81f@.\xa5l\xf7\xc2\v\u007f\u02c8D\"c\xb6o\xdf6\xf2\x9ak\xae>\xb6\xaa\xaa\xaa\xe7\xb6m\xdb\xd5\xda\xda\x1aa\xdb\x16\x8a\xa2\x92H$\xb0,\v\xc30\x89\xc7cn\x9eC\xb8\xef\xabt,\x9c\xa5\xed\xe6\xc0\xf4\x80s\x10\x91\x9eT\xd7\x1f\x0e\x87sV\xacX\x010\xd8\xe7\U000d16dbK\xf7\xee\xdd\xe3\xb7\xdf\xfe\xbb)\x1d;\xe6\xbd\u007f\xd2I\xc7\xfd0h\xd0\xd0\"o\u04ff\xef\xbe{\xb5+\xae\xb8\xdc\xee\u0673\xb7\xfdo\r\xe6M\xc7\t'\x1c\xff\u0357_~u\x13\x90\xb1j\xd5\x1a\u05ae]\u01e0A\x83\x81\x03\x1b\xb1z7r\xe6\xcc/(,,l\xa6J\x00\xa7$\x1c \x12\xa9C\u04f4\xbd\x8c\x944\r\xfa\xf5\xebO\x97.\x9d\u0675k\u05cf\xca\xdbl\xdb\xc60\f\xfc~\xc7\xf6\xb7\xb0p=_}5\x8bK.\xb9\xec\x17;\x85\xfc\xe3\u0701\x0eX,X\xf0m\xca5\xf0\xf0\xc3\x0f'\x18L\xffUQ,{\x03z\x94\xb4\xb4\f\xee\xbf\xff\x8f\xe4\xe4\xe4\xf0\u05ff>CII\t\xc1`\x10U\xf5\xa3\xaa*\xc9d2\xb5\x89\xefi\xc9\u073c\xef\xa7s\xc2J&\x93dggs\xed\xb5\xd7\xf0\xbb\xdf\xddF\x9b6\xd9\xc4\xe3\x91f\xf3\xea@\x9d6|>\x9d\xa1#G\x93\x99\x95\u016e\x1d\x85T\x94\xae\xa6\xef\xb0\t\xd4V\xc6\x01\x81%,\xa4\x10(\xd2F*N\xa7\n\xa9\bPAZn\x8b9\xd7\xfaV\n\u0464\xdc_\xa2I\x81*!\x19K\x12\xb5\xd2\x18>\xe1|6.[\u0136\x8d\x85(\x9a\xe6\xb0-\x12\xa4\xf0\\\xd4Ql[\xca\u014b\x97\x88\x8cp\xf8\x8e\x05\xdf~\xbbl\xcc\xe8\xd1\x1fuh\xdfA\xd6G\xea\t\xa7\x85\u007f\xf2k+,\\K\xbf~\x03\x91R\xf2\xc9'S\u0122E\x8b\x94\x01\x03\x0e\xb1\x00\uf0542\f\xa4O\x9b\xf6y\xc6\u018d\x1b\xf2KJJ\x87M\x9cx\u0571555\xc7l\u0738I)/\u07cdiZX\x96I<\x1eO\x15\x8a\x99\xa6)\x15E\x11N#\x10\x87B\u06f3VDU\xb5\x94$\u007f\u03e0,\x99L\xb2{w9R\u06ae\xd8Ae\u01ce\x12\u05af_\x1f\xf0\xfb\xfd\x17v\xeb\xd6\xed\u0082\x82\xb5e\xf7\xdc\xf3\xfbWF\x8d\x1a5g\xfc\xf8\xd3\x16\n!\x12\x8f<\xf2(\u007f\xfd\xeb_\u052b\xae\xfa\xad\x15\n\x85Y\xb7n5\x03\x06\f\xfa\xf7\x05\xf3\t\x13N\x9f\xfb\x97\xbf\xfcu]I\u024e#\n\v\v\x99?\u007f\x01\x83\x06\r>\xa0Me\xbd\u0098\xfa\xfa\x1a\x96.]F<\x9e@\u04f4f=\x1e\xcb\xcb+R\xc7\xee\xfdo\b\xec3\x91\xd9\x14\x04\xbc\xd7\xe0\x1d\xfb\x03\x81\x00\xd5\xd5\xd5\u031b7\x9fK.\xb9l\x9f\r\x84\x0f\xceP\xa8\xaf\xafa\u0672\x1fp\xb8\xc7\xde\f\x1e<\x98\u007f\x87\x11\x8fG\b\x06\u04f9\xe3\x8e\xdb\xe9\u07ff\x1f\xcf?\xff\x02\xf3\xe7\xcf'\x1a\x8d\x10\x0e\x87IK\v\xa56\\\xa7\xefg\xa3\u4c39\xbb\xa2#M\x1d3f\f\xd7\\s\r\xe7\x9f\u007f.\xaa\xea#\x91\x88\xfe2zk\xefd\x90\u04c1\x01\x87\x8fd\u1b19\xec,Y\xc9\u19ddOL\xd6b&-0\xc1\xb6l\xb0\x04~U\xa0\xa2:RD\xd5y-\xb6\x05\xb6pZ\xcba:\xe0.\x15\x1c\x8d\xba\"\x10\x8a\xc46-jj#du\xec\xc5\xf0\t\xe7\xb3\xf3\xf9'\xb0L\xc3\xdd\xf0\x1b\xbdn\x1c;^!b\xf1\x18\x8b\xbf_\xaa\xf5\xeb\xd7\xf7Y)\xe5:!DA8-\u0316\xad[\u945f\xff\x0f_\xd6\x17_L\xa7_\xbf\x81M\u05cb\xf4\x00|\xfb\xf6\xad\x87|\xfe\xf9\xe7\xc3W\xaf^\xdd\xfe\x9ak\xae>\xbc\xb2\xb2*'\x91\x88w6M\xb3\xf7\xb6m\xdb(//'\x99Lb\x9a\x16\xb6m\xb9\xf5\x03\x8dEa \xd04]\xec\x19\xc4\xed\xfd9?b\x94'S\xbeQ^m\x82\xd360\x81\xaa*\xd4\xd4\xd4RXX\xd8!//\xef\xbeM\x9b6\xdf\xfb\xf5\xd7\xdf|\xf0\xe1\x87\x1f\xbcu\xce9\xe7N\xbb\xe9\xa6[\xac\x9bn\xba\x85\x17_|\x9e\x8d\x1b7\xff{F\xe6\xf3\xe7\u007f\xc3QG\x1d\x8d\x10\u00ba\xe3\x8e\xdf}SP\xb0\ue23a\xbaz\x16.\xfc\x96+\xaf\xbc\x1c\x9f\xcfw\xc0J\xe0\x1d\x9f\x0e\x9d\x8d\x1b7\xa6|G\x9aj\u01e5\x94\xacZ\xb5\x8a\xda\xda*\u06b4\xc9$\x1e\x8f\xeds\x12\x94\x95\xed\xa2\xbc|\xf7~\x8f\xe3M\x9f\ub045\a\xfe\xabW\xaf\xa6\xb4t\ayyy?\x1a\xd5\xff\x12\u00fb\xc6m\u06f6\xa5\xbc\xb8{\xf6\xecI\u07fe}\xd8_g\x9e_\xd3\x10B\x90H8\xf9\x8e\t\x13\xce`\xe8\xd0\xc3\xf8\xec\xb3\u03d86m:\xf3\xe7/\xa0\xb2\xb2\x12EQ\xdd\xc8L\xa4\xf2}M;`\x85BA\x86\f\x19\u00b8q\xa7r\xfe\xf9\xe7\u04eb\x97\xf3\xde\x1chje\xcf\xe0\xc1\xc6\xc6\x17J\xa3\xff\xd0\xe1,\x9c5\x93\x92\xad\xab\x88\x8bJ\u04bbeR_[\x0f1\x05b&\"ia\x1b\x02M\xd8H\xdbi\xe7\xa7\t\x05K\xb1\x1d\x8d\xb9\x05\xa6%\x91\xb6C\xb1\b\u06e1X,\x17\xbc\xccx\x82\xb8\x11\xa0\xe7\x11\xc7\xd3g\xf17\xacY8\u05c9^\x9b\x18\x05\b!R\r.**\u02993\xf7\xeb\x0e}\xfa\xf4\xb9\t\xb8\x0e`\u02d6\xad\xec\xdcUF\xc7\xf6\x1d\xf6z-\xc5\xc5\u06d86m:\xd7^{='\x9f<n\xcf\u0377\u05d3O>9~\u04e6-G^{\xedu\x03l\xdb\x1eTVVFYY\x19\x89D\xd2\xcdg\x18)\x16\xbf\xf9zSR\xc0\xdc|~;\xf7\u054b\u029dG\xa3\x0e\xdfk\x8a-]1~c\x91\x95\xe7&\xe99\x9b\x89\xd4)\xad\xe9\x89\u0272L6o\xde\u0136m\xdbD\u01ce\x1d\xcf+,,\xfc\xcd\xef~w\xdb\xfb\xe7\x9cs\u059bG\x1e9\xe6\xabk\xae\xb9\x1e\x80%K\x163|\xf8\xc8\u007f/0\xef\u0631C\x13\xaa\xe5\xb8\x17\xbe\xf9f\u078d\xbbw\xef\x0e\u035f\xff-K\x96,\u1a23\x8eA\xca\xc4\x01\x05\x92\xa2\xa2bJKK\xf7\x99\b\x9b;\xf7k\xbe\xfbn1'\x9f|*\x81@\x90d2\x912k\xf2\f\xb8>\xfex\n;w\x96\xb9\xaa\x06s\xbf\x1bGS@\xf7\x80\xbb\xb4\xb4\x94M\x9b6\x92\x97\xd7\xf9\xe0\xc7\xe4\xee\xe9c\xf5\xea5)\xd7\xc0\u07bd{\x92\x9b\xdb\x1e\u00c8\xff\xdbT\xf99\x00`\u0429S\x17\xae\xbd\xf6z&L\x18\u03d2%KY\xb1b\x05\x05\x05\x85\x14\x15\x15S]]\x8da8ADFF\x06\u077au\xa3_\xbf~\f\x192\x98\xa1C\x87\u0437o?@!\x91\x88\x1e\xd0\xd3\xe3\xfe\x02s\xdb4\xc8\f\xf9\xe9;\xe0\x10\x00vo\xddH\xd5\xce\":\x1d2\x94x2\x06\x9a@\xf8\x05VRAIZ\x88\x84\x8dL\x9aH\xc3FJ\a\xc4l/\x02\x176\xb6\x05\xd8\x12i:\x85D\xd8\x02Ew\x14.\xb1h\x946Y\xed\x18v\xea9\x14\x17\xac\xa2\xa1\xbe\x1e\xdd\x1f\xf0\x10/\x05t\xb6\x9bP.,\\\xcf\xd7\xdf\u033bDJ\xf9\x8a\x10b\xd9\t\xc7\x1f\xc7\xd6\xed\xdb\xf7z\x1d_~9\x93.]\xba\xef\xb9N\xd4Y\xb3\xbe8g\xfa\xf4\x99'\x9fq\xc6Y\xa3\xaa\xaa*\xfbVVV\xb2k\xd7n\xa2\xd1(\x8a\xa2\u062a\xaa\n!R\xddNS`\xbb\xaf\xc0\u010b\u031d\x8f\x8eE\xb0\xd7(\xd5m\x85\x8d\xf4\xac\u007f\xbd\xc6\x1e^\x03\x12ic\xe36\xf7p\xbf\xef\xd9\r\xe3Ya\xa7TOJ\x93\xa8\x1d,\u02d2EEE\xb2\xa4\xa4T\u07fau\xeb\xc5\xc5\xc5\xc5g=\xfb\xec3\xafM\x9at\u00fdB\x88\xda\xe1\xc3G\xf2\xd4S\u007f\x12w\xdcq\x97\xfc\xb7\x01\xf3;\xef\xfc}\xea\xf3\x93N:\xb5\xcd\xdbo\xff]]\xbdz\rEEE\xbc\xf9\xe6[\f\x1e<\x98p8L\"q\xe0\x00\xbd\xb2\xb2\x8a\xda\xdaZ\x14E\xa4\xfcF<P\xaf\xae\xae\xe6\xb1\u01de\xa0S\xa7<\x0e9d0{\x9a\x80}\xf6\xd9'\xbc\xfe\xfa\x1bX\x96E \x10H)\x1f\xf6G\xb7\xec\x19\xb5\xd7\xd4\u0532cGI\x8b\xb8\x17M\xc1\xbc\xa6\xa6\x86@\xc0O\xff\xfe\xfd\xf7\xb9\xc9\xfd;\x8cX\u0331-\xee\u0739+\x9d;wc\u0084qTUUSWWO,\x16sU*\x82@ @\x9b6\x19dee\xb9\x9e\xe7\x0e\a\xef\xd5\x17\xfc\u049b\x9c\x04\xa4m\xd1\x06\x18\u0437\x17\x999\xb9\xd4W\x95\x13\xad*G\xd7AE`\t\xa7x\b\x9f\x00]E\x04\x05\x18\x02\x12\x16\xc4,DR\xa0Z6\x8a\x00[*$m\xdbQ\xb3\xe0x\xb6\xa4\u0339\xe26\t\x9fI\x9d\x9a\xa4\u00e0\x91\f\x18}<\x8b?\u007f\x1f\xfc\x01/\x94\xf5\f\x19\u075f\x17\xd4\xd7\u05f3t\u9cb4\x0f?\x9er;p!@~\x13\u04f1-[6\x88\x1e=\xfa\u0213N:\xa5\xe9z\xe8\xf8\xfc\xf3\u03dez\xf9\xe5\x97]\xbfe\xcb\xd6\xc1\xa5\xa5\xa5Zee\xa5\x97\xb714MS}>\x9f\x02({6\xd4\xf6\xd6X\xf3\xfb RHo#\xb1M{\xaf\xf5g[\x0e%c\xfd\x9f<\x86\x9a\xffm\xab\xc9\xdaQT\x15MU\xbdz\x13\xe1\xf0\xf3\xd2\u07bau\x9b]^^\x11,))\x99TYY1DJy\xb9\x10b\xd3\x1dw\xdc%O;\xed4\xf1\xfc\xf3\xcf\xc8=7\xb5_\x1d\x98O\x9d\xfa1g\x9ey\x16\x00\xf5\xf55C\xee\xbf\xff\xfe\x0f\u05ef_\xef\x8f\xc7\xe3\x00\xbc\xfd\xf6d\xbav\xed\u01ad\xb7\xdeL \x108`\x9cr\"\x11wmO\x95\xd4\xed\xf1\xa2-)%\xf3\xe6\xcd\xe3\x82\v.\xe2\xea\xab'2z\xf4h\xc2\xe10\xe5\xe5\xe5\u03181\x83\xb7\xde\xfa\u007f\xec\u0739\x93`0\xf8\xa34\xc9\xfe\x12\x9c\xb1X\x8c\xda\xda\xda\x16\x03\u6595\xa4\xb0\xd0)\xb8\xc8\xcb\xcbc\xc0\x80\xfe\xfc\xbb\x0eo\xf3J$\x1c\xfa\xcc\xe7\xf3\u047e}\x1e\xed\xdb\xef\xef'l\xa7\x89\x83;\x0f\x0ff2\u061bJ\x83\xf2\xf38\xe6\xb8\xe3\xf9t\xea\x14j\xcaw\x82\x04U\x15(\x96\x03\u0336G\x1d(\x02\xe1S\x10\xba@\x06U'R\x8f\x99\u0204\x85\x86\x8de\xb9d\x85l\xf4b\x91\xa6D\x899\x96\x00I#F\xa8]\x16\x87\x9et6\xdb\xd7.\xa7\xacx\x1b\xbe@\b)\xad&\xe0\x99\xf2\xba\x91\u06f7o\x17\xb3f\xcd>#\x1a\x8f\x8d\v\x05\x82\u04fd5\xf5\xd8c\x8f\xea=z\xf41\xdc\xff\xa7\xbf\xf7\u07bb}\u05ef/\xbc\xf8\xa2\x8b.\xbcp\xed\xda5\x1d***\xa9\xac\xac\xf6\xfa\xb5J\xc5\xe11\xf4=\xd7~s\x8a\u0109\x99m)\x9b\xb9FJ\x01\xd86\xb6e\xa5(\xa6f \xa9\xfb\xd0C!B\x81\x10z \x88\x1e\f\xe1\x0f\xa5\xe3\v\xa5\xe1\xf3\a\x91\xaa\xcf\u9deai(B`\x18\t\x92\xb1(\u0246:\x12\xf55\xc4\x1bj\x89\xd5V\x93\x886\xa4\x92\xac\u0496\xae0Bs\xe7\x87T\x84\x10JCC\xbd\\\xb9r\xa5hhh\x18\xbdk\u05eeO?\xff\xfc\u04f3'L8\xbd\xe0\xb3\xcf>\x93\x17_|\xe1\x8f\xea\xf2\u007f\x15`\xee\x019\xc0_\xff\xfa\xccog\u0318\xd9s\xe5\xcaU(\x8a\"-\xcb\x12\xc9d\x92\xb7\xdez\x9bc\x8e\x19\xcb\u0631cS2\xb2\x03\xc3\x15\x8b}\x82\xae\x97\x10]\xbbv\x1dw\xdf}/m\u06f6\xc5\xef\xf7\x13\x89D\x9a\xa9W\"\x91H\xb3\xe3\xf6\xfe\"q\x0f\x04\xfc~\u007fJ9\xd1r\xe8\v\x85\xf2\xf22v\xedr\xf8\xff\xdc\xdc\\\xb7\xcaU\xfe\u06fb\xe2I)I$\x12\xbf\x9e\xebu\xe3\xc2v\x99\xe9\f\xe8\u06db\xa9\x96EEi\x11\x89H\x12MU1\x15\v\u06e5\x01\xa4t\x13\x9d\xd2%\xdc\x15\x10A\x05|:2\xa1 \xa2&:N\xa4\xeaq\u0136\xed\x16|J\x89L\xd8`@\u012a\xa5}\xf7\xc1\x1c2v\x1c\x15\uff88\xb4\xf7P\xfb8\xa5\xa5(B\x88\x9a\x9aZ\x96,Y\x1a|\xe7\x9d\xf7\xae\x91R\xce\x12B$\xcf8\xf3L\xed\xd3O>1\x00\xdeyg\xf2\xb8\a\x1f\xfc\xe3\xbd?\xfc\xb0r\u052aU+ihh\xa0\xa1!B2\u9038\xa6iBJ)\xf6\x06p\x1c{\x01o\x9d\xb9fa\xa9^\xacV\xf3\x80J\xf3\xf9\b\xb6\xc9\xc1\x9f\x1e\u019f\x96\x8e?-\x03\u007fZ\x06\xa1\xac\xb6\xb4i\u05d1\xb4\xb6y\xa4\xe7\xb6'\xb3m\x1e\xa1\xac\\|\xe1L\xfc\xc1\x90\x03\u07b8.\x94\xb8'\x16\xe9P\xa4\xb6\x91 QWE}Y1\xe5[\n)[\xb7\x94\xe25K\u067d\xb9\x00\xcb2\xb1,'I\xae\xeb\x1a\x9a\xa6\xba\x1b\x8c\x10\xb6m\u02cd\x1b7\x8a\xfa\xfa\xfa\xfe55\xb5\xd3\x16,\x98w\xe7\x981c?\xbc\xe0\x82\x8b\xe4s\xcf=\xc7\xf5\xd7_\xbfO,h\xf1`\xbej\xd5r\xc2\xe1\f\xf2\xf3{\"\xa5\f\x9dp\xc2\xf1\xa3\v\n\nH&\x93R\xd34\xe1M\x90\u035b7\xb3f\xcdZ\x8e:\xea\xa8\x03\xa6;\x0f\x06\x83\xf8\xfd\xfe\xfdz\xb1\xf8|>\f\xc3 \x1a\x8d\xb2}\x1f\u071f\u01c3\xef\xc9\xd7\xed\v\xc8\xc1\xd1rgff\x12\x8dFS\u0296\x83=\xbc\rm\xe7\xce2<\x1b\xe2\xdc\xdc\\:t\xe8\xe0\xc6w\xad\xa3\xa5\r\x87\xda\v\u0465Kg\x84\xa2PWVJ\xb4\xbe\x16\xdd\x1fBS\xc0R@X^\xcc,\x1b9\x11w:\nU \x82*\xaa.@\x15(\xa6D&m,KbI\xe7\xae+\xe0\xfa\b\b\xac\xa8A\xb4\u06a4\xef\u19f2q\xe9\xb7l+\xf8\xc1md!R\xc9C\xcfS]QT\xb9q\xc3F1o\u07bcqg\x9c>\xe1:\xe0\xe9O?\xf9\xc4L\xc4c\xdd\xee\xbe\xe7\xee{\xdf{\uff49\u02d6-\xa7\xba\xba\x8aD\"\xd9\xcc\x12\x03\x10{\xaa\xc0\x1a\xbf\xe7\xca(]%\x89m[\u0626\x99\nk\x03\xe9\x19du\xeaNF\x87.\x84\xdbw&\xabcW\xdat\xe8L0\xbb\x1d\xe1\x9cv\xa4g\xb5\u00d7\x9e\x81\x16LCSU\x04`z\u007f\v0l\xe7/\b\x01\x9a\x04\xd3v\xde7E\x80\x94\nR\x82?\x10\"\xd8&\x9b\x9cn\xbd\xe93\xfa84\x01\xbb7\xae\xa5\xf0\x9b\xe9\xac\xfej*\xdbV,\u00b2LG!$mt\xdd\xe76;q\xf6\xbd\x9d;w\x8aE\x8b\xbe\xcb\x0f\x85B\x1f|\xf9\xe5\u0309'\x9dt\xca+\x93&Mb\u04a4I\xbc\xf9\xe6\xeb\\v\xd9\x15\xbf.0?\xf4\u0421\xfc\xfd\xef\xffO\x00\xf2\xf5\xd7_;\xa3\xa4\xa4d\x80\xdbzM\xec\t\xd8k\u05ee\xc30\f\x97\xaf\xfe\xf9u\xe7m\xdb\u6493\x93M}}\xfd^\xfd\x1dM\xd3l:\u0270m\x99\xf2[\xd9W\xc2\xe5\xa7\xf0\xcbR\xcaT\xc5gzz:\xb9\xb9\xb9-\u6f94\x96\x96R[\xeb\x80y\x87\x0e\x1d\b\x04\xd2~&O\xee\xd6q \xc0\\U}\xe4\xb6k\x8f\xee\x0f\x10\xad\xa9$\x19i@\xf7\x87\x1c@\x15\x8d\x8a\x1c\xe1\xa4\xfaR`\xeeQ*\x02\x89\xa2*\u0220\x8a\x1dT]\xfd\xb8\xc42m,)Q\xa4\x13\xdd#\xc0\xb6\x05\xf1\xfa\b\xe9m\xba2p\xe48vm\xdf@<\x1eE\xf7;y$\xe1\x05\xd2B\"TE\xc4\xe2q\xeb\x87\xe5+\xb4\xe93\xbe\xb8PJ\xb9\xe0\xaf\xcf<\x93\u007f\xe9e\x97=\xb9l\xd9\x0f\xf9\u06f7ow\x03 \x81\xa2\x88f\x94US\u00f3\x94\x9d\xb4P\x90H\u01f6\xd70\xb0\f#\xb5\x0eC\x19Y\xe4\r<\x9c\x8e\xfd\x87\u04b6\xd7@\xb2\xbb\xf6!\xbbs>\xc1\xecvh>\x05lG\xb1\xa3\xba4P\u04b21L\x03\xd3H\xa2\xe2\x18\x8dY\xb6\xd3\x1a\u06e3\xd6U\xc5q\x93\x94\x80\"$6`\xd9\xc2\xf5\xa5\x01UU\x1c\x17N\xa9`\xab*y\xfd\x06\xd2e\xe0@\x06\x9fr.\xdf}\xf0\n\x8b\xde{\x89\x86\xaarL\x04B\x98\xf8\xfd>\x84\xb0\xb1m[\xa8\xaa\u02ae]e|\xf5\xd5,TU}y\xc1\x82o\xca\u018c9\xfa\xf3;\xef\xbcC\\v\xd9\x15\xf2W\x17\x99\x1bF\f]\x0fJ\x80\x0f>\xf8\xe0\xf8\xd2\u049d~\x97\xb0V\xf7\x04\xf3/\xbf\xfc\x92\x82\x82\x02\x06\x0f>\xacY\x92\xf2\xe7\x89F\xa1s\xe7.\xe4\xe5ud\u06f6\xed.ol\xed\x15u7\xe5\xd0\xf7\u03c3\xcbf\x1e\x1e\xfb\x02x\u03ec\xa9\xba\xba\xca\xddH\xda\u04af_\xbf\x16s_\xca\xca\xcaR\x1c~^^\xdeO\u069cZ\xc7\xc1%\xceC\x19Y\xa4\x87\xdb\xd0P]I\"\xda@ZN{$\x12U\x11h\x9a\x02\x86t\xacl\xed\xa6\x80\xeeT\x89\xba\x04\x82\u04c2BU\x90>W\x9f(\x14\xa4%\xb1\\6W\xd8N\xa8.5I2\x91\xa0\u03f0\x93\u067af\x11k\xbf\xff\x12[\xf77\x82\xb1\x10N\x85\xa9\x94\xf8\x03\x01e\xe3\x86M\u031e=wP]m\xed\x9b\xef\xbe\xfb\xde\xc0\x82u\uba29\xa9F\xd34\xe9IR\x9a\xae\r'pRP]\xc7Q\x89\xc0\x966\xa6i\x91\x8c\u01f1M\x03\t\xa8\x9aN^\xdfC\xc9\x1fq,]\x0f\x1bC\xdb\u0783\xc8\ua50f/\xa0\x12\x8bK\xa4i`$\xe3\x18qGo.\xdc\xfc/\xd2=uH\xf0\xb9o\x8d)\xdd\xcaW\xf7-U\x04\xa8R\xa2\xbaI]7\xbcCU\x15\xfc\x9a@W\\\xba\a\x89\xb4-,\xdb\"n\n\x14\xa0]~w\xc6\xdf\xf6\b\xed{\xf6g\xe6\xd3\u007f\xa4\xa2x\v\x00\xba\xa6\xa3\xfb|\x18F\xe3)\xa4\xa2\xa2\u009c3g\x8e\x16\b\xf8\xff\xec\xea\xf2\xb7\xbc\xf1\xc6k\xea\xe5\x97_i\xfd\xaa\xc0\xfc\x99g\x9eS\x00{\u04e6\xc2\xfe\xe7\x9e{\xc1\xa8\xba\xba:\xf7\xbe\xee\r\x1c\x9b6m\xe6\xb9\x17^\xe6\u017f=\x87\xae\aI&\xa2?\x9b\x8b\x8fm\x1b\xf4\xec\u0643\xee\xdd{\xb0p\xe1w?\xda\xe7\xf1\xc7<\xb0\xf7l^\xe0%D\xf6\x04\xfc=\vP\xbaw\xef\xf63v\xa4\xf9\xd7GEE%\x91H\x14\xbf\xdfO\u06f6\xb9\xad`\xfe+\x18\xa1p\x069\xed\xda\xd3P]I2\x16I\u9f85\x04MQ\xb0UW#-,\a\xbaE\x93~\xa1\x02W\xaa\xe8&L]\xac\xb7\x14\a\x98\xa5\xe5&B\xa5\x1b\xa1\x9a\x82D<Iz8\x8b\xc1c\u03e6x\xe3\n\xeak+\xd0\xfdi\x8dk\xa71\xb6\x11\x80\x9c5kv\xe8\xeb\xb9s\x06\x16\x15\x15\xa1(\xc2\xd24]\x95\xd2nV\xc0\xe3\x9d~UUs\n\x97\x10\x18\x96\x8d\x91L\x92\x8c\xc7\\zR#\xdc.\x8f\xae\x87\x8d\xa1\xf7\x98S\xc8\x1bx8Y\x9d{\x10\xcaH'\x123I\xc4c$\xa2N\xe5+R\xa4\xac\xd8=\xce\u06f6\xc1\x96\x8e\x15\x81-!f:\xad\xf5lo3K)r\x1cfQW\x04\x9a\"A(h\xaa@\xf5\uc445\xe7\a\xef\xa6 D\xa3F=Z\x1fE\xf7\x058\xe2\u070b\ted\xf2\xf1#7SQ\xb4\x99\xa4\x91D\xf7\xe9\xf8|>\x12\tG\u07aci\x9a\xb6s\xe7Nk\xf1\xe2%\xbd\x1fz\u807b\x80k.\xbf\xfcJ\xeb\xbb\xef\x16*G\x1c1\xcan\xccd\xb5\xf0Q]]\xad8\x89\x90\xf7G\x00\xfd] \u072f\x89\xf9\xbb\xef}\xc0\x1b\x1fNs9l\x95\x9f#g\xe8y\xac\xa4\xa7\xb7a\xf8\xf0\xc3\t\x87\xd3\xd9W\xd6|\xcf\xe3\xdf\xdeI\x99\u01afk\x9aJ \xe0\xdfo\x13\x83=\xc7q\xc7\x1d\v\xa8?[G\x9a\u007f\xf5\x94\xe2\x9d\x18\x02\x81\x00\xd9\xd9\u066d`\xfe+\x18\x19\xe9id\xe7d\x13\x8f4`\xc4b\xa9\xee\x13\xb6\xab\x8f\x16x\xa0\u04f4\xd0\xc7\x13i7\u2ba7\x1a7%\u0616t8\a[b[\xce\x03\v\xa7\xb24i\x13\x8f\xc7\xe9\xd4g\x04\x03\x86\x9f\x04\x12,\xd3H!8M\xdc'\x15U\x15\xbbv\xed\x92E\xc5;,\u007f UUU\xa5\xb4\xf7\n~\x9c\xa4\xa6\x82iY$\x92\x06\u0446:\"u\xb5$\xe31\x82\xe16t\x194\x82\xe3&\xdd\xcf\xf9\xff\xfb\x01\xe3\xee}\x96\xc1g\\F\xdb\u0783\x90\b\xea\xabj0\xa2\x11t\xe1PF\xaapZ\xe8y\ax[:\\x\xd2r\x94\x99q\x13\x92\x96$fB\xdcr^\xa6)%\x86\xed\xbeg\xd2\x01y\x84@SU\xfc\x9a\x82\xae:\xe5\xff4\xed\xbe\x97z\xfb\x9a`\x84\xa2`$\xe3$\xa3\x16\x83O\x19\xcf\xe9w\xfc\x89\xb4\xcc\x1c\x12\x89\x04\xa6i\x11\b\x04S\x9e\xfa\xae\xeaE\u0670a\x03\xeb\xd6\x15\\=m\u06a7\xe3\x01\xde}\xf7]\xf5W\x13\x99K)\xc9\xce\u0392\x00\x05\x05\x05=\u0768\xdc\xdc\xf3\xba}\x814\xba\xf4\x1dJy\xf1F\xea\xaav\xf1\xe4\x13\xffM\x87\xae\x87r\u0310nhD\u070d\xff_\xbf\x16\x80\xf1\xe3\xc7\xf1\xe1\x87\x1f\xf1\xed\xb7\v\u007f2\xf0y\u050b\xa3+u\"n\xa7\x11\xf0O\xe3\xf5{\xf5\xea\x95j\xc3\xd6R\xda\xc8y\x96\xb7~\xbf\x8f6m\u06b4\"eK^G\x0e$\x90\x95\x1e '3\x83u\x898f2\xe9\x02\x92K\r\x02\xaap|Yl\x84\xc3k\x8b\xa6 \u07ac\xa0\x11\xcbv\xc0\\\x91\xa0\x9a\xeeF\xe0VW\xdaB\xba\xa9T\x81\x11K\xe2\xcbl\u00e1c\u03e1h\xc32\xcavlD\xa2\xa7:\x86\xa6*)m\x89\xaaiB\xd3T\xd59\xd9:rB\xa7\x01\xb5H\x81\xa1\xed6\xc70\xe21\xa4\x94\x84\xdad\x91\x99\u05ddN\x83F\xd0k\u0329t\x188\x9c\xb4\xdc\x0e\xe8>\x05\u06c4d<\x86i\x99h\x02\x14UC\xb8{\x8d\a\u0216\x14\xa9\x86\x1b6{tW\xf2\xf6\x1c/\xf7@\xa3$S\xc1\x89\xb4}\x9a\x82\xae)(\n\xa8B\xa04\xba\xffz\xf9`\x8fU\x02!S\xf4\x8cpv(,\xcb\xc02T\x86\x9dy6\xb5\xbbJ\xf8\xec\u007f\xee!\x1a\x8d\xd0&\xb3\r\xd9\xd99TVVx\xa6_\xc24-\xb9j\xd5*\xb1d\u0272{\xa5\x94\x8b\x85\x10\x15R\u06a9\"\xa4\x16\x1d\x99\u007f\xfa\xe9\x14\xa5\xba\xba\u0192R\x86jjj\xfa\xef\u06b5\x1b!\x84\xb2g\x04\x98\u06f97\u01dcw7\xdd\x06\x8c\x06\xa0p\xd9<\x9e\xfe\xf3\xb3,^\x1f'\x12W\x11?\x13\xd9\xe2y^\x9fv\u0684\xbd\x1aR\xecI\x914}4MDy\x91\xb5cw\x9blf\u04b4\xbfq\xe1\x85\xe7\u04ed[\x0f\xa4<\xf8\x8e\x89\xceuJb\xb1\x98{\xc2\u041a\xa8lZ#\xf3\x96\x1c\x18\xa5\xf9u2BA\f\xd3\xc42\x12\x0e\x9f\x90j\v'Q\\5Fc\u0562\xdb\xfc9\u0144\b\xb7\u0273\xc0\xb4\x1d\xf0p\u0336\x04\xc2rM\xba\xa4\x9b\x10t\x1bA[\x96E<\x16!\xb3c>\x83\u01dc\x89\xae\xfb\x91\xaeX=\x15d\xb9\n)e\x8f\xc6\x16B\xa8(Bi\x8c\x82m\x9bd<\x8a\x11\x8f\x92\u06ed7\x87\x8c\xbb\x80\xa3o\xf9\x13g>\xf5>\xc7\xfd\xfeY\xba\x1e}\x06zN\x1e\r\t\x93\u02aa\x06j\xea\x1a\x88&-\x926\xc4m\x88\x9a\x92\xa8%\xa9KJ\"\x06DM\a\xd4\rK\x12\xb7\x9c\xa8\u0734\x1b#pO\xa9#\xf6\x00h[\x82\xa6\b\xd2|*A\u0761V\xbc\u04cc\xe2n\x8a\xba\x02~\x15|\xaa@\xf5z\xab\xcaF\xa32p\xe8\x18\x80D,\x81\xb4a\xf4\x05\x13\x19~\xfaE\x98\xa6IMM-\xa1p\x06\x99Y\xd9)\xdf\x1f!\x84\u0639\xb3\x8c\x82\x82\x82#\xdfyg\xf2a\xce\xfb\xa4\xfc:\"\xf3\xe5\xcbW*\x80=o\xde\xdc\x1e\xb1Xl\x90k;\xbb\x97\x8a%\xaf\xe7at\xedw4\u0246\x04E\x05\x8b\xa9\xad,a\xfe\x8c\xb7\xe8\xdas(\x81K/\xe0\x90Nq\xfc>\xf8W\xf3\xa1\x8e\xf9\x95\x8fK/\xbd\x94\xb9s\xbf\xe1\x8b/\xbe\xd8o\x04\xbf'\x00\xee\xcb\xe3\xfa\xa7P\x13\xf9\xf9\xf9L\x9cx\x15\xc0A\xf72W\x00MuZ\x8ey\x8bN\xd5tTM\a\x1c\x1d/\x12Z{\xf6\xb6H4O\xf1\u0376ma$\x13H\x17\xccE\x13\xe5\x8a\xf7\u007f\xa9\xb8\x06Y\xd2u\au\x13|R8`g\xb8\xe0+\\\x13-'d\x95\xa9\xed\xdcvs\xa9H\x81\x15Obj\x1a\xf9\x83\x8f\xa1\xeb\xcayl.\xf8\u07b1\xc8udx.\xed\xe0l\x14\x9ew\x8b\xf7\x90\xb6DZ\x166\xa0\xfb|t\x1c|$\x9d\x87\x1dC\x8fQ'\xd3a\xe0p\xec@\b\u06f4\x89\x18&v<\x8a\xb4\xadF{^\x9a\xf2\xd4\xce?V\x93\x90Cu\xbfg7\tElI\xea5y*\x1eM\b\xcc&\xbf\u03ef)\xa4\xfb\x14|\xeeRHu\xd6\x13\x12\v\xc7\xda\xc0k\xab\x8at*m=\x9e\xddF\xa04v\xe2\u00d3P\xc6\x1a\x12\x842\x82\x1c}\xe9Ml_\xb5\x84\x1d\x05+\xa8\xaa\xa9\xa1mN\x0e\xd1H\x84\x86\x86:\xc0Q\u022d\\\xb9\x8a\xbe}\xfb\x9c%\xa5\x9c#\x840\x17-Z\xc0\x91G\x8ei\u0651yeE\x85\x020o\u0782n\x96e\xf5\xb5\x9cb\x85f\t\x11\xdd\x1f\xa0]\xb7CAjt\xe9=\x8a!G_\x8a\xa6\xfb\x89\xd6\xedf\xda;\u007ff\u019c%\x945\x04\x11H\u051f\xe1\u055af\x82\x8e\x1d;q\xdbm\xb7\u042d[\xd7\x1f\x8d`\xf7\x17m\xef\xeb{\xfb\x03\xf5\a\x1f\xbc\x9f.]\xbac\xdb\xc6A\x8b\xca\x15G^\x8c)\x14j\x92\xb0`\xe9\x0fl\u073c\u074d\xccu\x84\x10\x18\x86\x81\x914S\xfe\xed\xad\xa3\x05R-B \x14\x05i\xdbX\xa6\xe7\x18\xe8\x16\xd2\xe0&\u294dg'%\x84\x82PDc\xbb;\xe1D\xe5\x86\xddX\\$\xbc\x877\xb7S2u\x99\xda\x04\xa4\r\xf1\xfa\b\xe1\u030e\f\x18y\n\x81`h\x9f\xa78\x8fJ\xf1|Ql\xdb\u00b6LT]\xa7\xeb\xe1c9\xe6\xd6\xff\xe6\xa4\xfb_\xe5\xc8I\x8f\xd2\xf6\xb0\xa3\x89K\x95XC\x84x4J\"\x9e aX$-\x97\xdep\x81\u0652\x8d\x11\xb7\xe5\tt\x9c\u032esZv\x9fc\x81K\xb9x\xd7@\ua512p\u007f^U\x04!\x9fJ\u062f\xa0\xb9t\x94\x94\"\x15\xad{\xf9\x84\x14\xf7\xeeF\xf86\x8d\a\x10\xd1d\xb3\xf3\"\u007f\xe7\xff\x92h}\x9c.\x87\x1c\u00a8\xf3~\x8bP5\xca\xcb\xcaPT\x95\xac\xac,TEK\x9dNjkk)))\xbdp\xf6\xec/\xdb\x01l\u06f6]\xb4x\x9a\xe5\x99g\x9f\xb3\x00\xca\xcav\xf6/\u06f5\xcba\u063c\x12iU\x03\x04\xd9y\xbd\xc9\xc9\xebC<R\x8fe\xd9\f\x19s1\xbd\x06\x1d\x8f\x10P\xbam9\x1f\xbd\xf9\x14s\x96l\xa7\xc1\x0e\xa1i\xe2_f\u03dd\xe6\x04&'\x9dt\n\xf7\xdcs\xf7\x8f\xf2\xc5?\x06\xe8?\xc5t\u9847\x1e\xe4\x92K.\xe1@v\xa4\xf9)@\xae\x00\xe5q\u061c\b\xf0\xf1\xbc\x15\xdc}\xef\xfd\xacX\xb1\x12P\x11\x8a\xea\x1e\xad\x1d\xe9\xd5\xffg\xef\xbc\xc3\xec\xba\xcas\xff[k\xed}\xca\xf4\xa6Q\x1b\x8dz\xef\xb2\xe4^\xe4&\x9a\xc16\x98bS\x13'\xb9$@n\x02\x81\x9bK\x80`r\x93Pb_\u01d8N\x8c1\xc1\xa6\xd8\xc6\xd8`\x1b\xdce\xc9Un\xea\xbd[3\x92\xa6\x97SvY\xeb\xfe\xb1\xf6\xdeg\x9f\x11\xe4I\x00\x97\x9bG\xdb\xcfQ\x9b\xf1\u0319s\xd6\xfe\u05b7\xde\xf7\xfd\xde7\x88\f\xfd\xd3\x0e\x82'\xaf\xd7\xcf%bpCZ=\xb6\xd1\xf1\xa3\x1a(\x93Q\xf51\xc8\x04vAT\nc\xf2o&B\xbeM\n_\x8f\xc6\xe5u<Q\x1a\x82\t5\x9e\xa7\x99<\xe74\xa6\xcd]I\x18\xd9b\xa4=k\x84\x89C\xb25~\xa9\x88\x92\x92I\xf3Wp\xdeG\xff\x915\u007f\xff]\x96]\xf51\x1a\xa6\u03a30:\xc2\xe8\xd0\x10^$\xdf\xd3)\xbc;\xfd3hS\xe9\xfc\xd3D\xa4\x88Z\xc2@\u06df'\x96 \xc6\xcf;L\x15\xda\x18v\xc9(IcVQ\xe7\xcadZ\xb6\xea\xebF_#\xd4\t\x1f\\\xb5\xa9\x98\xa4&\x98\n\xff`H^w\x81\x88F\xfca\u025a\xb73\xf7\xf4\xf3)\x15F9v\xf4\x18\xb5uu\xe4\xf2\x15\xaf\xa7R\xa9\u012e]\xbb\x1a\x8e\x1e=\xfa&\v\u00fe\u05fc\xae\x8b\xf9\x97\xaf\xbdV\x00\xa11&\u007f\xe4\u0211\xb3b\xb7\xc2\xf8RN\x06!\x04\xad\x9d\xf3i\x9c8\x13\xaf8\x8a_.R\xd78\x8e3\xde\xf0?h\xef\x98\aF\xb3\xe3\xf9\xfb\xb9\xf5\xa6\xaf\xf2\xd8\xe6>|\x91'\xe3\xfe\xfe\x05\xdd\x16\u0590\xab\xaf\xfec\xae\xb9\xe6s\xbf5\xc9=\u059d\xff&<=)\x96\xd1b\x1e[\xd8?\xf3\x99O\xf3w\u007f\xf7\xbf\xd1:\xa4T*\xbc&\xa3\xfcR\x80#\xa0\xab(xn\xc0\xe5\u0387\x9f\xe1k_\xfeg\xb6n\u0708\x9b\xab\x01\xa5\b4\x14\xfc\x900\xb4\xb8k\x18j\x82P\x13\x84!\xfad\x87\xfe\xbak\u03f56\b)m\x94\xdb\x18\x9e#5\xf4\x99\xae>VK.\x04\xa1\x11\x94C\x13\r\xc8\u0204(\x14X\\8^\xa2\xb1ml\x8c\x9b\xdb.^R\x1e-P\xd78\x89\x99\x8bW\x93\xc9\xe6\xd1:@D.\x82\"ZpZ\a\x94\x8b#\xd45\xb6p\xe6{?\xca\x1b?\xf3u\x16\xbd\xfd\xc34L\x9a\xce\xc8\xd0\x10##C\x16\u0089\xbeYhD\x02\x9d\xa4\xbb^\x13\xad\xdfT\x13\x9e\xa8s\u049d\xb1I?\xe2\r \xfa=\x88\xb2Ok3\x92\xa6\xbc$\xe7V\xfb\x99W\u03caT:}\x13\xd1\v\"\xe9\xf1I\xac\x12td\x8f\x9bl>1m\x10\xe1\u07e5B\x89\x96)\x93Xu\xe9{\xc9\xd55p\xac\xbb\x8b\xd1\u0451*\xe5[\x18\x06\x1c=z\x8cm\u06f6\xaf\x8c\xbf\xff\x81\x03{_\x9f\xc5\xdc\x18\u00e7>\xf1\t\x03\xf0w\x9f\xfe\u07e7\xbd|\xa4\xfb\xb2\x91\xe1\xca\b\xbd\x90\x12\xa4$W\xdf\xc8\u0139\u02e9ik'\b\x03\xb4\x0e\xf1J\xa3L\x99u*\xe7^\xf2\t\x9a\u06a6\x10\x06\x05\x9e\xb8\xef&\xbe\xf1\xb5\x1byd\xe7\b\xa3\xa1\x83#\x19\xb3\x8c\xff\xebW\xa9TB)\xc5\xff\xfc\x9f\x1f\xe7{\u07fb\x89\u014b\x17Uu\xe4\xe9\"\x1dc\x95\xe9\xa9Q\x1b\u079c!\x93\xc9\xe0\xbanR\xe0\xdb\xda\u06b8\xf1\u01af\xf2\x0f\xff\xf0\x8fH\u9f22.\x90\xff\x99b\xdeW\xd2<?\xe8p\xdfcO\xf0\xed\xcf\xfc%\xcf?x\x173O=\x8f\xa9KO\x87\xd0\xc7\vBzF<\u02a1N4\xf3\xf1\xe3dw\xfe:\xea\u0205\xb0\xce\u007fA\x80T\x0e\x8e\x9b\xb1\x1dfU'k\x12\xfd\xb8\x89\xd4(\xb1\xaf7\b\x0e\xd1)\xb8\x00\x00 \x00IDAT\xbc\x10\xbc0]\xa9l\x9foa\v\x91H\x1b\xd3V)\u0698\n!\x1a\x1a\xfc\x92\u03d4\u06671a\xdaB|\xaf\x94$\xf9H\xa5\xec`\x8d\xef3e\xder\xde\xfa\u026fp\xea\x87>E\xe3\xf4Ex\xe5\x12\xa3\x03}\xd6O\x1dY\xe9\x9c\xe3\xcd\xc4T\to*\x8a\x14D\x82g\xc7\x18\xb5I\xc9\x10\xc7v\xec&\xc5\x1b\x84\x91\xea\xa4!\xa7h\xcaI2R$|\x90H\x1a1\x128FF\x9b\x99\x92\x15,\x9eH\xd3c\"\xf9\xa7\x8eI\xe4D\xdeY\xed\x83\x1ek{\x02\x0f\x96\xbd\xf1\n\u67f3\x86r\xb1@oOo\xd5ty\x18\x86\x94J%\x8e\x1d;\xfe\x16cL-\xc0\u05293^\x9f\xc5<%\xe9k\xda\xf7r\u05e7\xb6\xed\u0611>'\"\xa4\x83\t\x02\x9a&Lg\xe2\xdceh\x13\xa0\x85u?\vB\x8fR\xa9\xc0\xac\xa5o\xe0\xd4\v\xff\x84\x9a\xbaf\xfc\xf2\x00\x8f\xde\xfe5n\xfa\xee-<q0d0*\xe8R\xfc\xee\x05]\b\x81um\f\xb9\xf2\u02ab\xb8\xed\xb6[\xf9\xc8G>\xc2\xf4\xe9\xd3S]9d\xb3Y\x9a\x9a\x1aijj\xa2\xbe\xbe.\xc9\xf6\xcc\xe5r\x94\xcbeJ\xa5\x12\x9e\u7854\xe2\roX\xc3]w\xdd\xc9G>\xf2\xd1h\xc3(\xbcf\x8e{BX\xc2wg1\xc3\xd3[\xf7q\xff\xb7\xbe\xcc\u02db\x9f\xa6\xa1m\"\x8b/y?\x8d\x1d3-\xfc\xe3\xfb\x1c\x1f\x1a\xa5\x14D\x04X\xea\x11\x17\xf5\x93\xd7\xeb\xe3\x9e\xf2<\x9fR\xa9\x84\xe3\xba(\u05ed(G\xd2 \x8c\xf9\xcdx\xbb\x01J\x91\x17K\xacp\x11\xd1\u007f:\xe9ze\xa5\x80\xa5\xfd\xcbM\xe4}n\x04\xe5B\x89\x9a\x86\x89L\x9fw\x16\xd9\\\xad\xf5K1\x10\xfa>Fk\xe6\x9d\xfd\x06\xde\xf6\xa9\u007fa\xd6y\x97\xe0\x87\x9a\xc2@\x0f~\xb9\x80W\xf6\b\xcbeL\u08c3\xc0\x8e\xe9\xfb\x1e\xc6\xf70\x81\x1f=\x02\xc2 \xb0|@h\x1f\xc6Da\x1b:\xd2\xc2\xc7\xda\xf0\xdf\xc2+\xc4\x1d\xb7#\x05\rYI]F\xe0\xa4D7\xf1C&\x9dw\xf5C2\xb6#\x8f\xcf?qa\xaf\x04ZXX&\x9e4\x15\xc9\xe6\xe9{\x1e5-\xb5,\xb9\xe82\xeaZ\xda\x18\x1a\x1c\xc0\xf7\x03\\\xd7M\x92\x95FGG9x\xf0\xe0\x94M\x9b^<#~\xfe\xaf;5\xcb\xc3[\xf6\x8b\v\x16N3\x00\xff\xfa\x9d\xef]\xbde\xcb\xd67\rG\x86N\x90J\x06\x91\x92q\xd3\x17\xd1\u06b9\x800(\x81cU\"\"\x14\xf8\xbe\x87\x11\x92\xc5g\xbd\x87\xc2H\x1f\xcf=z3\u0151\xe3\xfc\xea\xe6/\xe3\xe6\xf2\x94>x\x15+&\xb8\x8cw}\xa4\xb0\xbb\xf0\xefz\x83\x94\xcbe\\\xd7e\xe1\xc2\xc5\xdcp\xc3\xf5\xbc\xfd\xed\x97r\xc7\x1dw\xb2a\xc3s\xec\u07bd\x87\xa1\xa1AFF\xc2\u0102\xd3J\x11\xad\xe6<\xf6[\x99?\u007f\x1e\xef~\xf7\xbb\xb8\xf2\xca\xf7\x90\xc9\xe41&xM;r\x00\x13\x84\f\x84.On;\xc0/\xbfs\x1d\a\x9e}\x84\x9a\xa6q\x9c\xf1\xa1O\u04be\xf4lv<\xf5\bB(|\u07e7o`\x18?4d\x18sl\u0546\x93H\xcb\xeb\u5494JE\x86\x87\x87\xc9\xe4\xf2H\xc7M\x06\x85\xa2\x066\x1a\x0eJK\x11\xa3\xc6\u0280\xa7\r^\x84K\x88\x04\xaf0\x952&Rmm\xbcE\xa4\x8c\xa9t\xd4\xf6*\t\x81\x1f0g\xe9E\x1c\xd8\xf1\x14{\xb7=\x89T\x0e\x99\\\r\v/x\x1bg\xbc\xe7\xcfh\x1c\xdf\xc1\xc0\xf0\b\x81\xd6\bc\xbd^\xb41\xd6* \xf4#\xff\x15\"\xf5L\xb4\xa5\b\xfbg\x1d\x85B\x04\xa4\xa2\xdd\xe2\u009a\xb4\u0572B\x06G?_<\xd9i\x8c\xc1U\x82\u01ac\xa4&##\xe3\xac4\x142\x16\x86\x8a\xbfG\xdco\x8b\xb1\x1fN\xe1\xf9\x95\xff9\xdeFu\xca\xda#\xde\n\x851\xf8e\x98s\xe6E\xb4\u03d8\u01de\r\xebl\u4723\x92\xc2S\xf6\xcatww\xf3\xd4SO/\x06\x1e|\xdd\x15\xf3\rG\x06Y9\xa9\xd1\x00\xf4\x1as\xee\a\xaf\xfc\xe0\xff\xdal\x13\xae\x13xEHE\xe8{\u050d\x1f\u03d4%\xe7\xe1d\xea\t\x87\aQ\nK\xb4hkG\xe9{%2\xd9ZN\xb9\xe0j\u02a5a6=\xf9\x13\x06{\x0e\xf2\x8bo}\x01\xad4\x85\xf7\xbf\x8f\xa5\xad\x19:\xb3\x1eY\x19a]\xbf\xe3\xf3\xb6\x1e\xe7v\xe7\xbc\xe0\x82\x8bY\xbdz5\x9b6mb\xe3\xc6M\xec\u0673\x87#G\xba\x18\x1c\x1c\x8cB\x9a\xb3\xb4\xb5\xb5\xd1\xd99\x85\x8e\x8e\x0ef\u0318\u03aaU\xab\x90\xd2\xc5\xfa`\x17-\xae\xf9\x1a\x16\xf20\b\xd02\xc3\u04fb^\xe6'_\xfb\x17\xb6\xfc\xf2\x87\u0535\x8c\xe3\xd4\x0f|\x92Yk\xdeE\xc1\vP5\r\xb8\xf9:\xfcr\x99\x91\xc1~\xeb\xe1Aug\x1e\xc7n\x9d\xbc^\x1f\xd4\xe7\xf0\xf0\b=\xbd\xbd\xe4\xeb\x9a\xc8ds\xb6\u0324\x8a\x95I\xad9\x19\xfd]G\x05\xa7\x14X\u065d\x13O2#\x11\xd1{l\x03'D<\u0493*\x9e&E\xbbF\xe3\xf0\xc6\xe0\x97\xcb\xd45Mf\xde)o\xa1\xeb\xe0V\x86\xfb\xba\x19?u>K\xdft%\xf9\xb6\xc9\f\x0f\x0e\xa0\x84 @F\x10\x8d\xd5\xc0\xa3\xed0\x91\x91\xca\x16\xed\u0529!\xf6d'5\xa1j\xf9\u0644\xf2M\xac\tb\x1e\xc0\xaau\xe2\xc2n\xfb\ua332\x1dy\x8d##7\x9aJ\x017T|\x1f\x05\x95\x13J\xfa\xa3\x95Bn\xaa\x9eK\xe5\xb3L\x15$\x14\xff9q\x1e\x8e\x88P\x11h\x9a&\x8cg\xea\x92S\xd9\xf7\xfc\x13\x14K%\xb2\xb9\xbc\xb5\xda\xd5Vd\x10\x84!\u01cf\x1f?\xfdu\u05d9\xef\xe9\x1fefsm\f\xaf\x9c\xfb\u037b\x1e\xfc\xf1\x86'\u058d\u04e1\x9f\x9cq\x84T\xc9\xca\x1b?\xfb\x14\xa6,:\x87\xb0XB\x87\x1aGY\xb3\x04-L\xf2\x92\x95K\xa3dk\x9a8m\xcdG\b\xfc2\x9b\x9f\xbe\x83\x81c\xfb\xf9\xc5\u05ef\xc1\u01e7p\xe5\x87\xe8k\xc91'W\xa45+\x12,\xeew\xc5\xf9-1j!\x93\xa5KW\xb0t\xe9\n\x00FF\x06\x19\x19\x19\x89\xc6r]\x1a\x1b\x1b\xc8\xe5*CGa\u8f6a\u0650\xff\xd1\x15\x04!\x197\u02c6]\x87\xf8\xfa\xf5\xd7\xf3\xdc=\xb7\x92\xcd\u05f0\xec=\x1fc\xe6EW\xe0\xf9!\u0294\xc856\xe3\xe6k)\x0e\xf7S\xec?NV\x1a\x1b\xfe\xabu\x82\xb1J%\xff\xdbD\xc8\xfd\xff]\xc6\xed\xd5;8\xcc\xf1\x9e~\x1a\xa7\xce%\x93\xaf\x89\x94 i( E\xeaI\u06f9\n$A\b~d0\"\x84\xed\x85e\xba+\xaf\xa0\xc8X%u\x1c\x9cV\t\x84H \x17\xec\x1a)\x97Kt\xce=\x8b\xa9s\u05f3\xfd\xf9\xfb\x18:~\x84\x83[^$\xdf1\x93\x92\xafq\xa4\xed\xae\x8d\x01\x95\x10\x93\xd2B$&LHY\x11\xdd{!V\xab\x9eH$\x11\xa9gd\x12\xfc<)\xbcFG\xa7\tk\u022e5()\u027b.9iU'\bQ\u054d'\xaf\x8d\x11U\xc4\xe5\xd8\x12\x9d*\xf7c\u0781\n\x8e.\x10U\x9b\x83\x89\xbe\xae\x10V\x11#\x02\x8d\xceH\xa6.YE\xbe\xbe\x89rq\x04'\x93\x8dN%!Zkc\xb4\x16CCC\xf3_W\xc5\u0718\x12k\x9e\xc9\xc6Eq\xfc\xbac\xfe\r\xb7\xdf\xf6\x83\t\xdd\a\xf7V\x93\x9e\x80\x0e\x03jZ\xc73\xef\r\xef%\xdf\u070e\xd7\xdfoM~\"\x12\"\x99B\x8e\xce\xfa\xa5\xe20\xf9\xfaq\x9c\xf1\xe6\xbf\"\fC\xb6m\xf89C\xc7\x0eq\xff\x8d\xff@qh\x90\v\xde\xf7aF&7\xb2\u0414\x98\x90\xd5\xd6\xdfY\xff~\xe4\xa8\x1d\xd7\x1fM2@\xf3\xf9<\xb5\xb5\xb5\xc9\xc0\x90%0*!\x15\xaf\x97\x82\xa7\xb5!\x93\u0272i\xdb.\xbe\xf4\xa5\xebx\xf4\xee;\xa9o\x19\xc7\xe2w\xfe\x053/~\x0f\x81\xd6\x04\xbe\x87P\x92l}+\x99\xda:F{\xbb\x18\xe9=\x8aB'\xa4T\x95N\xf9\xb7$'\x9d\xbc^E\x80%:\x1d\x1d:\xd6\xcf\xf0\xc80\x1d\xcd-dr\xf9\xc8\xe7;\x9ar\x8c\x8b\xad\x88\x8ap\u0535J\xa1\bBA \x05J\tT Q\x0e`4\"\xf0\xa3\xe2])H\xf1z\xb6\xfauRU\xb0\xe2\x96\x1e\x1a\xf0}\x8fL\xae\x81\x05\xa7_\u0391\x83\x1b\x19\xe8\xda\u03ce\xc7\xef\xa5}\xd1*r\xed\x93\x18-\x160\xd2>w\x19\r\xed\x84F\x83\x88\x02'\xa4B\xb9\x0eJ\xaa\b*1Ud\xa7-\u4552jL\x94\xc7\x19oB\xe9IS\xc0\bI\xc6U\xe4\\\x85\x92\xd2nV\xc6$Dp\f\xef\xc2\u0621\xb8\x13;\xed\xb1\x9dz\xa5\xc0\x9b\x04\xd3\x1a\x13\xa7Q\xe9\xe7\xa3\xc1+\xad\xed\xfd4n\xfa<\xf2\xf5\x8d\x14\x87\xfa\xad\x8b\xa2\x90\x84\b\x84T\xa2X*q\xe0\xc0\xc1\xe2\ub998\x1f\x19\x1a\u5fae\x8c|\xe04\xa1\x8d1\xe2%\xc3\xf5?\xf9\xe1\x0f\x97>t\xfb\x0f\x13\xcf{!\x95\xf5i\xd0\xf6X8\xed\u07372\xe1\xac5\x94J\xc2\xfe`\bth\xb1<\x19\xb9\x02\x89T\x01)\x95\x86\xc97\x8c\xe7\xccK\xfe\x1a'\x93e\xcbS\xb73\xdcs\x84\a\xbf\xf3E\x06{\xba\x19\xfd\xd3O\xe0\xcd\xef`\x8e\xf6\x99\x9a\xf3\xad\xe5\xe5\xefY\xd0\xd3E\xda\xf7\xfd\x13,q_OE\xdcz/;\xf8\x81\u6a67\x9f\u57fer\x1d\x0f?\xf0 \xcd\x13\xa7\xb0\xfc\u028f\xd1q\xe6\x9b\xd1BP.\x95PB`\xb4&\xd7\xd4F\xa6\xa6\x1e0\f\xf6\xf5R(\x14\xc8fs\x95\xee$\xf2\xa989\xe2\xff\xdaw\xe5J)\x86\xbc\x80\x03]\xc7\b|\x9f\xda\xc6\x16T&G\x10\x84)\x89\\\x05K0\xc9H\xbfD\v\x89'\x04\xc2u\x11J\xa2\x82\x10\x95q\x80\x00\x11D\xbai\x01&\x12L\xeb\x04\xfb\x15\t\rZY\xe7\x95\x13\x80\x1fj\xf0<\xc6MY\xc8\xf4E\u7c79\xff(G\xb7?\u03c1\rk\x99\u007f\xc9{-\x84a\f\xa1\x16\x84R \x94\x83\xe3f\x11\xcaE\x1bM\xe8{\x04\xa5\x12^\xe0\x11ze\x82\xe2\bAq\xd4\x12\xa0~\x19\xed\x951:@\x87\x9a0\b\x18\x1b\x9cb\xa2\rF)\a\x95\xc9\xe2\xd4\xd6S\xdf\u0502jmA\xd7\u0510\xc9\xe5psy\x9cl\xce\x16\xd70\xc0\u8832Y\xa5\x02\x9f\xd3K\xbc\xba\xdb6\x15\xc8)!\x98+\xffF\x15\x1cS\xbd!\xc6\xde0u\ud4e8il\xa6\xf7\xf0>\xb4\xd6(\xa9\x92S\x8e\xe7\a\f\f\x0e\x8a\xd7M1\u007f\xf28\xe2\x1d3\xa56\xc1\u05dc\x8d%n\xb9\xef\xd7O\xbe\xe7\u07ef\xfb\x02\xc4QSR&]\xb91\x9a\u018eY\xcc~\xcb\x1f!\x9b\x1b(\xf4\x0f\xa2\xea2\xa8Q{,\x11\x84 t\x12\x1b\x15\xbfa\x00\xe5\xf2(\xb5M\x139\xe3\xd2O\x92ije\xe3#\xb7P\x18\xe8a\xfd\x0f\xbf\u03b1\xfd{\xe8\xfb\xcb\xcf1\xb0r%\xbdy\x98\x93/\u04d4\x93\x04z\xec\xfe\xfa\xfb\x17\xf6\xd7\xd5\x15u\x01n&\xcb\xe8h\x81\xbb\xef\xfe%\xff\xf7\x86\x1by\xe1\u0157\u8637\x843\xde\xffW\xb4/_\xcdh\xa9L\xe0\x95\x93\x13\x8f\u059a\x9a\xd6vj\x1a\x9a\x018\xde\xd3C\u05d1nf\u035a\x811\x12\xc7Qd\\'\xf2\x948YP_\xebj.\xa4\xa2o`\x80\x03{\xf7`\xb4\xa6\xb1}\x12nM=\x85b1\x9a\xb4\xa4\xe2\xfd\x1f\xfb\xa4 \"\xfbWM9\x10\x11\x84\x11!\xe2R!\x1d\x89p\x01\xcfD\u49b4\xddx\nX\xf9M+>\u0351\x06\x81G\xb6\xae\x819+\xde\xcc\xcb;\x9f\xe5\xf8\xa1m\xec]w/\x13\x16\x9dJ\xe3\x94\xe9\x84a\x80v\xf3\xb6\x93/\x15\xf0\a\xfb(\r\xf53r\xfc0\x83\x87\xf70\xdau\x90B\xcf\x11\xcaC\xfd\x04^\x89\xd0\xf7\xac\xef\x8b\x0e\xaa\xa6[#l&u?Vj\x83\x88\x88P\xe1\xb88\xd9<\xf9\xfaF\x1a\xc6M\xa0\xa5c:\xadSg3n\xfa|\xea\xdb'\x90ol!_\u05c0\t\x03|\xaf\fJ\"\x1d\x17\x19\x9f\f\xc6\xf4\xdfT\x9dE*8\x8d\x10\xa2\xaa\xa6$\xf8z\xa2\x8d\xafl\x05Z\x83\x93\xaf\xa1\xa6\xb95B%Bdd\xa7!\x84\xf5\x8b\x17B4\x19c:\x84\x10\x87_\xd3b~\u02ce\x11\U0004e675\x06`\xb3\xf7\x17\xff\xf8\u0736\xc3W\xde\xfc\x95\xcf\xd3\u007fx\x9fA\b)b\x9c<\xfa\x81\xa5\x94L;\xf72Zf/\xc5/\x87h)\xd0\rY\x8c\x12\xa8\x11\x1f\xe1\x03\xa1\xed\b\x8d\xd4\x15\x1aY\xc4\u064d\x05\u071a&V\xbc\xf5\xa3\xe4Z\xdby\xe9\x17\xdfb\xa0{?;\xd6\xfe\x82\xa1\xe3/s\xf4C\u007f\xcd\xe9o\xbe\x9c\xd1\xf6Z\xe6\x86\x05&\xe4\xad\xd7q\xf0\xdfP]g\x8cA)\x85\xeb\xe68t\xf8\x10\xb7\xfc\xe0\x87|\xf3\xdb\xff\u0191\xaen\xe6\x9c~>g\xbc\xf7c\xb4\xcf[\xc1\xf0h\x01\x1d\xf8\xa9\xde\xc1\xfe\x9aoh\xa2\xb6\xb5\xddb\xb1\xbd\xbd\x1c>|\x98\x85\v\u7864$\x93\xb1\x99\x86\xe2d_\xfez\xe9\xcd\x19\xea\xef\xe3\xe0\xee]\xb8n\x96\xfa\xb6v\xa4\x9b%\x18\x19E\vI\xd97\x14\xfd\x10)\x05\x19G\xa2\x94\x81\xc0\xe0\xa1)\x04\x10b\x13\xe5\x851\xc8x\xbc\x11cG\xcc\x1d\xd7f|\x1a\x1dy\xa3\xc70\x86I\x8a\u007f|*\x8d\x15(\xb1\x02\xc4\x18\b\xca%\x9a\xdbg2k\xe9\xc5\f\x1c;\xc0\xb1]\x1b9\xf2\xecZ:f\xcdg\xa80L\xcf\u07ad\f\x1c9\xc0\xe0\xa1]\xf4\xef\xdf\xce\xd0\xe1=\x94\x86\xfa\b\xcb\x05\x82r\t\xed\x97O \x19S~\x1fv[\x12\x95M\xaa\xfa\x95\x89\xe0 \x1dF\xb8\xba\xfd\x8c.\xa1P\x99\x1cN&\x1b\u017e\xcdb\xf2\xc2S\x98\xbc\xf0\x14\xc6\xcfZ@\xe3\xf8\xc9\xd6\u07e6P\xb0A\u03ae\x83P\u059a7\u0749\x8b\xb1P\xcc\tR\xd0\x18\u007f7i\xda8\xf9\fk\xaf\xebP\xd3\xd8\x12y\xcfW\xc3\x05e\xcf#\x9f\u02f5\x06~i\t\xf0\xda\x14\U000d738d0\xec#\u039e\\g\x00^\x1a6\u007f\xbd\xad\xab\xf8\xa9[\xbez\x1d;\xd6\xfd\xda\xc4o\x03\x91#\x98\x10\x02\x1d\xf84\xcfX\u0234\x8b\xae@\xe5k\xf1\v\xc3\b#0J\xe1\xd7f\xd0\x02\x9c\x11\x10:\xb4\x93V\x91-g\xa5\x15\x10\x18!(\xfb\x05d6\u01e2\v?Dm\xcb$\x9e\xff\xf9W9\xb6\xfbE\xba\xb6=\xcf\u03ff\xf4I\x8el{\x91s\xdes5\xbd\v\xe617\xf4\x99]\x13P\xebZ\xd8E\xff7\xaaL\xb9\\\r\xc6h\x1eyl-\xdf\xfc\u6df9\xff\xbe\xfb\xf0\x85\u00eaK\xdf\xcf\xf2\xb7\xff\x11\u0353gP*\x8cb\xc2\xd0\x12P\u046b\xa8\x845br\x1c\x87\x96\xc9S\xc9\xd5\xd4s\xb4\xbb\x9b\xbd\xfb\xf6R_WK\xb1XD)\x99`\u007f'\xaf\xd7\xfe\xf4\x050\xd8\xd7\xc3\xc1\xdd;i\x1e\xd7NC\xebx\xcaAH)\u0414BC9\x88\x92\x854\x94\xb5\u5374\f\xf0\x8dU\xb0\b'\x9a\x88\t+\x86[\xf1\x9b+\xa5\x8bQ!\xda\xe8$9\xc8\xf2U\"\"\x11+\u05b8q!O\xab@\x02\xcf#WS\xcb\xf4\x85\xab9\xb0u\x1d]\xfb^b\xff\xd3\x0fP\xd7\xd0H\xcf\xf1\xc3\xec\xdb\xf0(#\xdd\a\t\xfd2&\f\xac\x1f\xba68\xb9\x1c5m\x13\xc97\xb7\x93kh\u00adm\xc0\xc9\xd5\"39\xa4\x93A(\x85r3\xb6\x19\x14$]x\x02\x99':p\x8d\t<\xfc\xd1aJC\xfdx\xc3}x\x83\xbd\f\x1f{\x99\xe2`?\xa5\xe1~\x06\x8f\x1cd\xff\xb3k\xc954\u0471x\x15\xb3\xcfZ\xc3\xf4U\xe7\xd2\xda9\x9b\xc0+\x12\x94KH\xa9\x10\x8e\x83t\x1c\xdb\u96f8\xf5\x11\x95\x01\xac1\xc7\xfcx\x8f\xd1\x11dc\f)\x8d\x8e\xfdM*E\xae\xa1\xd9\xdaf\xe8\x94\a<\x10\x86\x1a)e\xaeX*\xb6\xbc&0\u02c6\xae\x11\x06=\u0139\x1d\xb6\x90\xef\xf3\u037b6\xf5\xf0\xa5{~\xfa#\x1e\xfd\xc1\x8dQ\xed\xb6lf\x85L1H7\u00cc\x8b\xdfc\xbb\xf2\xe2h\nB\xb1\xdd{X\x13\rA\b\x0f\x8cF\x9a\xc8\xf6&u|\xb4\x83jV\x1a%1\xccX\xf5f\x1a'Na\xe3\xbd\xdfb\xe7\u06bb\x19\xed\xeb\xe6\xf1\x1f\xdc\xc0\xc1-\x1b8\xe3]\u007fF\xd7Eof\xb8\xb3\x89Y\u01a7\xcd\r\xc8\bC`\u018c=\xff\u007ft))q\xdc\f 9t\xf8\x10\xff\xfe\xc3\x1fs\xcb\x0f~\u020e\x1d\xdb\x197y:g\xbf\xe9C\u033b\xe0r\u0716F\xfc\xe20FWf\xd6\u048bPk\x8d\xc20\xaes\x165u\r\xf4\x1d{\x99\x9d\xbbw\x01\xe0\xba\xcek\x1e\xa0q\xf2\xaa\xc0{\x18\xf0\x03\x8fC\a\x0e\xd0\xdf{\x8c\x19\x8bW\xd0\xd0:\x8e\xc1\xd12\u00de\xcd\xca4Q\xa1\xd3X\x13-\xdf\x18L\x18\u0372K\xa7\"\xdf3\x06\x19\x8e\t\x95\x90\x02\xa5\\\v\x8b\x1aM\xa8u\x05h0Q\x11OKB\xc4\x18Y\x9e\x81Rq\x94\xe6q\u04d8\xb5\xf4\"z\xbbw\xd3wd7\xeb~p-~\xb9\x801\x1a\xe1\xb8dj\x1b\u0237\xb4S7\xbe\x93\xda\tS\xa8\x9f4\x9d|\xebD\xf2\xcd\xe3\xc8\xd67\xe2\xe6\xebPY\xab\x9f\x17\xcaI\b\\De\x9c^P\x19\x15M\x80\x11a\xe1\xa1\xd0+\u23ce\xe0\x8f\xf4#\v}\x04}\xdd\xf4\x1f9H\xcf\xdem\x1c\u0779\x91\xc1\xae\x83\x14\xfa\x8e\xb3\xfd\x91{\xd8\xfb\u0323t,Y\u0172\xb7\\\xc5\xfc\xf3\u0782[S\x87_*$Cs\xcaq#hX\x9c@pV\xce\x04\xd5\x1fM \xb1\xb1p\xba\x14\xb8\xb9\x9a\x14\xa9\\)\xf4\xae\xe3\xe0y\xde`}}\u04de\u05e4\x987d`e\xab-\xe4]\xa5\xf0\x8am\xa3|\xef\xf1\xc7\u05b9w\xff\xeb\xe7\xed@\x80\xdd\xee+\x18\xb3\x90h\xbfD\xc7i\x173\xe3\xe2w[\x02\xa4\\Np\xf4\xcaiE\x12\xe4\x15h\a\xe9Ysb\x13\x92\x1c\xe9*>\x956\xa7P\a\x1e\xda\xd3LY\xb8\x8c\xf1\xb3\xff\x0f\xe3f\xce\xe6\u017b\xbe\xcf\xe0\xd1\xc3\xec}\xf61\x8e\xef\xdd\xc1\x8e\xf5\xbf\xe6\u0eeef\xe5\x19g2\xbb)\u03f4|H\xb3\xf4QBW\x19\xe9\xbc\xde\x0f\u064e\xa3\x90\u02aa\x85F\xcbe\xee\xbc\xeb\x1en\xb9\xf9\x16\x1e_\xfb\x18A\xa0\x99\xbb\xec|\x96_\xf8>\xa6.9\x9b\xc0\u04d4\xba\a\x10\xf5\n2\xc2\x06\x16D\xb7g\xec\xe6\xe1\n;X\xd1:u6nM\x1d\x00\xbbv\xed\xe6\xe0\xe1\x97\xe9\xec\x98L\x10\x04'%\x89\xaf\x13(MH\xc5\xf0\xf0\b\x9b6m\xa1T,2\xb5\xb3\x93\xd6q\xed\xec\x18\xf1\b\u3390\x8a_\xb9L\x95\x1d\x13%V\xc4\xf7\x9a\x88\x9d\x12\x13a]T\x18\xa5\x83t2h\x1d\"D\\pL\xc5L\xcaD\xb2A\xf1\x1f\xc5,\n\xa6\xcd=\x8b\xfd[\x1f\xe7\xf0\x9egq\x9c,H\x89R.n\xae\x96|\xdb\x04&\xad\xbc\x80i\u7f55|\xdbdT&\x17\xf17!h\x8d1\xf6w\x1d\x04\x10\xf8Ie\xb4\xcf\xddDj\x96\x8az%\r\xbb\xe80@\x87>N6\x87\x93\xef\xa0&;\x9d\xa6\xba\x1c\"\xf0\x18\xed=\xc6p\xf7A\x8e\xed\xde\u010e\xb5\xf7\xf3\xf2\x96\r\xf8\xc5\x02\xbb\x9f|\x90\xae-/px\u04f3\x9c\xfd\xa1\xbf\xa6q\xfcd\xca\xc5QL`'beR\xd0S$\xa8H\x17\xf6\xdf\"e4&\r\xb1c\x8c@erV>\x99\xf8\x1d\xd9G.\x9bedd\xa4K\b\xf9\xe4kR\xcc7\xf6\xc7<\x9an}|X\\\xf3\xe2\xf6\xae\x9a\x9f|\xe5\xd3f\xb0\xeb\xa0H\b\xcf\x18^\x91\n\ud5e9i\x9b\xc8\xdc\xcb\xfe\x94\x86)\xb3\xf1F\x86*\x8b\v\xa2\u022b\x10\x11\x86\xa8\xb2\xc6)\x9b(%E\x82\xd4cH\x97hzMG\xde\xceyA\xc00\xb5-M\x9c\xf1\u078f2i\xdeR6\xfc\xf4\xbb\x1cxa=\u00fd\xddl\xb8\xeb\xfb\x1c\xde\xfc\x1c/\x9e\xffVN\u007f\u06fbY\xbah\x01\xb3\x9asL\xad14\xcb2\xae\xd1\x15\u00de\xd7\u064d,\x85\u0779\x91\x19\x00\xfa\x8ae\x1eY\xf7\x14?\xbe\xf5V\x1e\xb8\xf7\x97\f\x1c;\u02b8\x8eY,>\xedm\xcc]y\t\xf5\xad\x1d\x94\a\x8b\x04\x04\xe0(\x8co\x90\xf5\n\x99\x17d\x1cA\x10V\x16\\F\x1a\x1c)h\x9c4\x95\xfa\xf1\x939\xba\u007f\a[\xb7\xed\xe0\x85M[\xe8\xec\x98\xf2\x9fr\x84<y\xbd\xf2]y\x18\x868\xd2ahh\x98\xe7\x9f\u007f\x01\xc7q\x98\xdc9\v\xc7m\xc0/\xf6#\x1c\x89N\x87\x11\xa7<E\"\xe64\x82)\xa4=\xf2\a \xab\xc8\xc4Ja\x92\xcaA*\x17\x13\x86\x15\x1dK2:o\x92oR=dc\x1b\x04!\x04\x81W\xa0e\xdc4f.<\x8f\xa3\x87\xb6\xa0\xc30\"g\xb5\xed\xd0{\x8f\xf2\xf2\xb3\x0f\xe1\xd6\xd41\xf3\xa2w\xa1\x85\xc0/\x17\xa3|\u0354\xd4PJT&\x8f\x8c|\xdb1a\x02\xf3\x98\xc8F1\fC\xeb\x03\xe3\x95(\r\xf6\xa12Yr\x8d-\x84h\x8c\xe71\\.bJ\xc36|\xa2\xa1\x89\xc6\xf1\x13\xe9X\xbc\x8a\x99g\xaea\xdb\xc3?\xe7\xb9;nb\xf8x\x17\xe5\xe2\bO\xfd\xf8\x1b\x18\xad\xb9\xe0#\x9f\xc5\xcd[{\x02B\u06d0\u02884\xae\xe80NT\xf5\xc4V\xf0\x90ruL\xe9\xe1\xa5\x148\x99\x8c}-\xc2\xd0Z\x13D\x03\x01\x8e\xa3\xc8d\xdc$\x14\xf8U-\xe6\xbbz\x87\xd5\xec\u05ba\u0418\xc1\x86u=\xe2\xd6\xed\xc7\v\v~t\xdd\xe7\u0341\xe7\x1eO\xde\b1&\xdeSe\xf3\xccz\xf3\xfb\xe9<\xe7R\xc2XQ\x01\x88\xc8s\x810\xc4)\x858\xc5\x00\xe5iD\x18\xb1\xeeJV\x8c\xf6\xab\xec\u0362\x1dR\x82\xca\b\x94+\tJE\x9cL\x8e\xd9g\xbf\x91\ts\x96\xb0\xf5\xc1\x9f\xb1\xe5\u05f7st\xcf6\xbawo\xa6\xe7\xd0^\xb6<\xf2K\u67bd\x86S\xdfx)\v\xe6\xcfc\xfe\xc4f\xe64\t\xeaM\t\x11\x1d\x0142uC\xbc\x8a\x1dX\xb4\x8d\x9b\b\xb7\xccf2\b'GIk\xf6\x1f9\xcecOm\xe0\xe7?\xbd\x9dg\xd6=F\xef\xe1\x83\xe4\x1b[Yv\xee;Y|\xe6\x15L\x98\xb6\f!\x1c\u02a5\x82\xbd\x81d4\xb5\xe0\t\x8co\xa0V\x92\xa9U\xe0(4\x90\x95$\xc9*\xd9\xc6\x16&/=\x9d\xbd\x1b\x1e\xa3\xa7\xeb0\x8f?\xb5\x81K\xdf\xf4F\x94\x14\x91v\xf9\xe4\xf5Zv\xe5ah0\x04\xec\u067b\x97\xed\u06f7\xd3:~25\xeds\xe8\xed\xf5Q\xc3!8\xc6\xdab:\x02\xa3\x04:\x8a\x8d\x8b\x83\"\xac\t\x96J\xf0\x10\x11\x18\xc4\x18\xab\u0724\xaf\x14\x12\xa9\\\xb4\f\x10Z\xdbD\xa2\x14\x962\x16m1\xa9\x1b\xc5\xda\xce\xda.{\xee\xa2\xd5\x1c\xdc\xf1\x04\xfbw=\x83\x92.\x06C\xe0{\xf8\xa5\x02#G\x0f2xp'RHf\xbe\xe9}dj\x1a\xac\xea#\f\u0441oC,\xfc\x80\xd2\xc0!\xca\xc3\x03\x8c\x1e=H\xb1\xb7\x1b\xa1\xa2\xf0\x94r\x11od\x88\xd2\xc0q\xbc\xa1~\xca\xc3\xfd\x04\x85Q\xda\x17\x9d\u0182+>L\xae\xa1\x95\x902\xa1\x86\xb2\x16d5\xc8\xc0\xa7<j=\xd5[\xa7\xcc\xe4\xec\x0f}\x9c\xb6\xa9\xb3y\xf0\xc6\xcf\xd3\u007fx\x1fR(^\xb8\xfb\at,=\x95eo}\x1f\xa5\xa1~k/\x1c\x1f\xdd\xe3\x13\u0258\xae;60K\xac\x86\x8dA\xc62\x03Qq\xbe!\xb1)\u0589Z(\xc6\xfa3\xd9,S\xa6t:\xafz1?6<\"\xda\xeb\xebB\x80g{\xf2\u007f~\xb4\u031a\a~\xfa\x03\x9e\xb9\xe3\xa6\xe8\x04oc\xa2\xd2\x15\u0284\x01\u35de\u02fcK\xff\x14\x95\xc9\xe0\x8f\x0eG\xedx\x88\tB\xa4\x17\xa0\x8a\x01\xaa\xac\x91\x9e\xae`aBF\x0f\x93`eF\b\x9bo(\rZ\x82\b\x81\x82F\xba\x122\x82\xc0/\xa3\u00c0\xba\xd6\xf1\x9c\xf6\x9e\x0f3\xeb\xb4\xd5l\xbc\xef'l{\xf4\x17\x1c\u07ff\x8bc\xbb7\xd2{p\x17\x9b\x1e\xfc\x193W\xadf\xd9\xf9o\xe2\xd4U+X2\xb3\x839-5\xd4\xe3\xe3\x86\x1e\xa1\x8e\xdd\xd1xE\xbbS\x13\x1f\xa3\x8dA\b\x83#\x1dT\xc6N\x95\xf6\x97<6m\xde\xcaC\xeb\x9f\xe6\xbe{\xeef\u02f3OS\xe8\xebFek\x99\xb9\xe4\x1c\x16\x9f\xf5N\xa6/X\x8d\x9b\xab\xc3\xf7\xca\x04\xdeH\xb5\x9d\\L^\x155\xc6\xd3\b\xdf \x1a!\x93\x93d\x95\xb4Y\x8aR\xa0j\ua63a\xfcl\x9e\xad\xfb&\x85\xc1>\x9ez\xf6yv\xbc|\x94\xb9\x93[)\x16K'@a'\xafW\xef\xd2\xda\xde\xf8\xe5\xb2\u01c6\r\xcf\xd1\xd7\xdb\u00fc\xe5\xe7\x90k\x9b\xc9\xc0P\x11\xe9\x1b\x84\xaf\xed@\x91\x12h\u01e0\x95\xc48\x12\xed`\x8b\xbcR\x18e\x87td\xa8\x11\xa1\xa9\xaaGBT\xf5\x93H\xa5\x90\xcaA\x87~\x95\x83.\txc\xaa\xb0|\x91\x82b\x84\x10\x94\xca%\x9a[;\x98\xbf\xe4|\xba\x0fo\xa30:\x80\xebf\xad\u076eT\x88l\x1eod\x90\xcdw|\x03\xaf8BC\xe7lr\r-d\x1b[\xc85\xb7\x93o\x19\xcf\xc0\xfem\xbcp\xf3?s|\xeb3\x04\xc5\x02\xa1WD(\a\xe5f-1*\x15\xd2u\"\vm\x89p\x9ch\x94\xbf\U00084170A\u03a5\x00\x1c\x19\xf9\x97\x87\x01\xc5\xe1\x01\xb25u,Z\xf3\x0e\x8a\x83\xfd\xdc\xfb\u5ff1N\xad\xe5\x02\xc7\xf6lG\x87aEzhl\xf1\x8d\xcd\xc8\xcc\x18\xe2uL4M\x85R\x88a\xa9\xb8\xe9\u0506\xd0+\xd9F+Eh\x1b\x03\xb9l\x96\xce\xce)\xaf\xfe\xd0\xd0\u06a3H \xdc\xdd3t\xf1^\xdc\xcf>\xb9\xfe\t~\xfd\xad/j\xc2 \x19\f\xaa\"\xd9B\x9f\xda\xf6\xc9\u033f\xfc\u007f\xd04m\x16\xe5\u0442\r\x8a\rBD9@\x15m7.\xfd\x10\xa1+\x13\x9fF\b\x84\x0e\xab(\x86\xc4`G\xa4\xce5\x1a(\x1b\x18\t!\u0087\xb5\x0e(\x17\x86Q\x8eK\xdb\xf4y\\\xf0\xe1\xcf0\xf3\xb4\xd5l}\xf8n\xf6nXG\xcf\xfe\x9d\f\x1c\xde\xc3\xf3G\x0e\xb2\xf9\xc1\xbbX\xbf\xec4\x16\x9cq>KV\xac`\xd5\xc2Y,\x99>\x89\xf1\xb5\x0e9\f\x84\x1e^`1\xae?dQ\x8fG\f\x1c)q\\\x17p\xf1\x81\x97{\xca\xec=\xb0\x8b\xed{w\xf3\u0533O\xf3\xe8\xaf\xee\xe5\xc0\xf6\xcd\xe0\x15q\xb2uL[t6\xb3\x96^\xc4\xcc%khh\x99B\xe0y\x94FG\"WCSE\n\x99h\xc4Y`\x03{\u0168&+C\x8c\x92hW\xe2FR,#a\u072c\xf9\xb4u\xce\xe4\xe0\xa6>^\xda\xf04O<\xbf\x89\xb9\x93/\xaa\x1a\xda:y\xbd\xfaW\x10h\x10\x92\xe1\xe1\x11\x9e|\xf2I\xbc \xa4u\xda|L];\xe5\xbe\x11{\u007f\xe8JR\x90\f\r\x9a\x10\u0436\xa0g#_\x11\t\xc65Hm!\x96\x98\xb4\x94P\x05\x1f\xc4\xe8\xafT\n\xa1\x1c\x8452O\xec]\u01de\x8eI\xe3\xc7\xd1}\xa9\x81\x92\xe73w\xf1jvo~\x94-\x1b\x1fAJ\xc7\u00ae\x91L\xd9\xc9\xd7Q\xe8\xed\xe6\xa5\u007f\xff\x17T&\x87[[O\u0744\xa9\xb4\xccZ\u0338\x05\xabpj\xeai\x984\xcdz\xc0\x14G\b=\x8f\\S+\xf9\x96v2\xf5\u0378\xf9:\xb2\xf5\x8dd\xeb\x9bps\xb5H\xd7%\xd74\x0e\xb7\xa6\x81 \xf0*\u0773\xb6\xb9\xa09mpb\xbc\x1d\xf0\n#H\xc7e\xd1\x1b\xae`\xcf\xd3\x0f\xb3\xf9\xc1;q\x9c,M\x93:\x93\x0e\\V\xba\xad\x13\t\xe04\xb7i8\x11z\x12U\xb80ZkJ\xa3\u00f6sO\rve\\'\xfe&\x8f\xbf\xaa\xc5\xfc\xf9\xeeQ\xb1bBmh\xf6\xfc:\xf7L\xa6\xfeo\xb6o=\\{\xd7\xf5\x9fg\xf0\xc8~\x19\x9b\u0724\v\x9e\t\x03\x84\x90\u0338\xe0\n\xa6\x9e\xfb6\x02/\xb4\x129O\xa3\n>\xaa\xe0#\xcba\xc5\xc1-R]\xd8\x01\x01\x8d\bmjI\x1c\x95m\xa4\xc2\b\xab-\xb4\x8bP\xe0(\x81\x02L\u0672\xf6\xa2\xc1\xb1\xba;\xac\x1dg)\xf0Q\x8e\xc3\xd4\x15g\u04f1p\x15\x87\xb7l`\xd7S\x0f\xb3\xf7\u0675\x1c\u06f3\x15o\xb8\x9f\xed\x8f\xff\x8a\x1dO<\xc8#\x13;\x99\xbap9s\x17-b\xf9\xc2\xf9\x9c\xbal!\x8b\xe6\xcc`\\mM\xbaW\xc2\x18\x9d\x04V\xc4\xef\xcao\xaby\xa2*\xd8VDV\xb8\x95\x93K\x00\xec9\xd6\xc7\xc6-;xi\xe3v6m\xda\xc2\xd6M/\xb0o\xfbF\xcaC=\x00\xd46\x8dc\xf2\xf2\xf3\x99\xb6\xe8\\\xa6\xcc?\x8b\xa6\xc6NB?\xa0T\x18\u0184a\x92rb\x17j\xd4%%\xfffC\x1c\x05\xa0B\x83S2\x04\xa3\x86 \xa3Py\x97\xacR\xe8 \xd7\xd4\u01b4SWsp\u04f3\x8c\x1c?\xc2\u06a7\x9e\xe1mo\xbc\x88&G&\xb9\x89'\xafW\xbf+\x0ft\x88\xa3$;\xb6\xef`\xeb\x96-\u0535L\xa0}\xfeJF\xb5\xb5]\xad8\x01\xc6)C\"I\x90\x17\x81FZ'\x14\xf0\x02BW 4\u0220\xd26\xc6E\xca6O\x95\x82%\xa4c\xbd\u024d\u0104\xe6\x841\xf6\xb4\x8a\u00cc\xd5u\x18C\xc9\xf7h\xa9og\xc1\xd2\v\u0637k\x03\xc5r\x81L&\x97nJQN&\xc1\xbc\x83r\x81BO7G7=\xc9\xee_\xddJ\xcb\uc974/\\\xc5\xcc7\\I\xf3\xccEH7\x83r\xb3\x89\"\u013a\x12Z\x88VD>\xe3a\x10\x10\xf8^\x15!\x89\x90\x84\xdaP\f\f\x8e2\xb8\x91\xef\xad\x04\xbc\xd1!\x9c\\\r\xe7^\xfd)\x1a'vR\xd3\xd8\u00ac3.\"\xf4\xbcd\xe8'\x9em\x91\xe6D}y\xfc\x9a\xc4\x19\xaa\xb1\x90\u008c\x11\xbfHi\xdf\u02d1\x9e\xee\b\x86\x16),]RSS\u00e2EK^~\u054a\xf9\x86\xee\x11VL\xb0\x83A[&\\\xfc\x8e\xfdG\xcbk\xee\xfe\xb7\x1b\xd8\xf3\xe4\x03Q\xb1\x92U\t\xd3\xf1.>~\xc9\xe9\u033f\xe2\u00e8l\x8e``\x98l)D\x16}d)\x88r\x99t\xb2\x83\xc5\xc7\x11\x11\u0623`\x92I\xa8e\x1c\xe4Wy%\xc3Hq%\r2\x8e!)i\x8c\f\x10\xf5N\xb2\xad\x1ac5\xb0\xa1\uf8e4b\xda)g\u04f1\xe4T\x16\\\xf06\x0eo|\x9a}\u03ed\xe3\xe0KOS\x1a\x1ed\xe0\xf0>\xfa\x0f\xef\xe6\xc5_\xdd\xce=\xad\x13\x98:k\x1esf\xcfd\xe1\x9c\x19,\x9e;\x93\xe9\u04e60i\xe2\x04\u018dk\xaf2\xd8\xfa\xcf^~\xe8\xd1\xd5}\x8c\xae\xa3\xc7y\xb9\xab\x8b]{\xf6\xb1}\xd7\x1ev\xef\xdd\xcf\xce\xed\xdb\xe8\u06b7\x1b\x82\xb2]\xe8\xcae\u0714\xb9L]v\x1e\x93\xe6\x9e\u0284)K\xa8k\x9c\x84\xf6|\xca\xc5\x02\x84:\xc9f\xac\xe0Yc\b\x9a\u06379\x9a\xed\x16Q\xfa\xba\xe3\t(J\u00bc\xc4S\x90-\x97\xa9\xa9\xabc\xfa\xa9\xaby\xfaG\u07e4<:\u0313\x8f<\xc8\xf3W^\xc5\u014b\xa6a\xbc\xc2\xefl\\v\xf2\xfa\xdd/?\x88B\x8d\xb5\u6847\x1f\xa6\xeb\xc8\x11&/?\x97\xe6\xd9K)\x95<\b-\xf6-\f\x89\xef82vB\x8c\xd1Z\x89\x8a\xf8\x13U\xb2\u0713\x8cn9\x13y \xe98\x861n\xaa\"\x0f\x13\xa52\x18\x13F\xa4\xa3\x8eh\u0394@\xbd\u2ddb@1q\xeeg\xa85\x05\xcfg\xfa\x82s\x98\xdcy\x0f;\xb7?m\vo\x94\x04\x11\xdb\xef\x1a\xa5\x10FYO\u01e8\u0287^\x89\xee\x97\xd6\u047b\xebE\xba^Z\xcf\xf8%g\u04b1\xeaB\xc6\xcd_\x89r3\x98\xa0\f~\x19\xc2 \xf1^\x89\xa3\xe5D\xe4\xefbR)E\x00\xe5\xd0\xe0\x866\xffS\x1a\x83\x89\x14*~\xb9H\xeb\xd4Y\x9c\xff\xe1\xbfKN\r\xa1\xefY\u062a*\x9c\xe27\x9d\xabM\u22de\xda\xe7*\xc0DdC,$\x04^\x89\x81\xeeC\x89 \u013e\x01\x9a\\\xae\x86q\xe3\u0195O9e\xf9\xab\u04d9\x1f\x19\x1c\xe1\xd1cQ\xd3\uc3f6=Q\xe0\x8bk\ufedb\xb5\xb7}+*\x1a\u056ezB\b\xc2\xc0\xa7v\xdcD\x96\xbc\xfb\xafh\x9d4\x8f\xf0\xd80\xd9Q\x0f\x15\x00\x81\x8e<\xb2M%4V\u0605)=[\xc8\x11\xa6\xc2\x06\x1bK\xc4\bm\xc7d\x8d\xb4/\xa4\xd4 C\x99\xec\x84B\x02%\x03\x8e\x86\x1aY\x95\xb8\r\x10\xe8\x90`h\x10\xa9$\x93\xe6-e\xe2\x9c\xc5\xcc=\xf7\xcdt\xef\xdc\xc4\xe1\xcd\x1b8\xb8\xf1i\xfa\x0e\xed\xc3\x1b\x1d\xa1\xd8{\x8cm\xbd]l{\xfa\x11~\x8e\xa0\xb9m\x1c\xe3\u01f5\xd1\xda\xd2D[[\x1b\xadmm475\xd1\xda\xdaB}}=\xd9\\\x0e\xd7\xcdD\x836v\xc4\u0646\b\x94\xe9\xeb\x1f\xe0\xe8\xb1\xe3\x1c;~\x9c\xe3=\xbd\xf4\xf5\xf7\xd3\xd7\xdb\xc7\xf1\xa3]h\xbf\x92\t\x9a\xcd\xd5R\xd36\x81q\xd3\x17\xd11\xff,\xc6O]B\xcbf\xa3\xc1\x82\x00\x00 \x00IDAT\xa4\x99d\xf2\x8d\xe8\xd1\x12\xa5\xc1A\x9b\xcen\u0184\x0f\x881\x06\x9ecL\xf8e\xf4\x9e\x18$Z+T\xe8\x90)I\xfc>\x8d_+p\xb3\x06\x9c\x80\t\xb3\x171u\xd9\xe9\xec\\\xff\x00;\x9f{\x8aG\x9e\xda\xc0\xf2\xf9\xd3hS\x12\x1d\xe8\x93\xc3C\xafvW\x1e\x84(\xc7\xe1\xc0\xfe\x03<\xb1~=\x81PLYt\x06\x8e\xd3@a\xb8\x80\x0el\xc7h\vz\xa5\xa0\x1at\xe4\xa9#PH\xa4\x0fF\x9a\x8a\xe2BG2;i\xd0J`\x14\x84R \x8d@\x06\xd1\xf4\xa7\x14\b\xe1 \x8dk!\xbctdb<\xb6n*^\xe3\xe9\x8f\u0152A/\b\xc8\u05f6\xb1h\u0645\x1c\u073f\t\xdf\xf7q3\u0654\xe5m\xf5\xf0LlK\x81\x90(7\x8b\x0e\x02z\xb6=G\xcf\xf6\xe79\xb8\xf6n&,9\x93)g\xbc\x81\xc9\xcb\xce\"_\u05c8\x0e}\xc2R\x11\x13\x06\xf60.L\x14\xe5f\xed~U\xd4\xc8H \xc4P\xf45\xae\x14d\x85\x8c\x8c\xe4l\xf3\xe9\xfb\x1e*\u04b2\x1bm3\v\xd2F\x06R\x8a\u02ae0F\xfd\x93D\xdcE\xbfK\xaa\x05\x8b\xb1%\xc9\xf0\xf1.\x86\x8e\x1dI5\xbe\xf6g\xad\xa9\xc9\xd3\xd1\xd1\xf1Rg\xe7\xf4\x97\x00v\xed\xda\xf1\xca\x16\xf3m\x83\x88\xabf[M\xf9N\xa7\xe6\xcf7<\xb7\xb9\xe3\xe7\xdf\xf82\xde\xc8P\xc5C8\xfd\x86\xea\x10\xe5\xb8,^\xf3G\xccZ\xb4\x06ul\x14Y('\fzl\xfc\x9b(-\x05\x88\xd0 }\x1d\r3\xa4X\xe3d\x81\x88\xca\xc8l\xec\x02d\"\xf1\x94\xa8\u021a\x84\x06S\b\x11\n\xc8\xc9\xea\xe2\x86\u0571k\xad)\x0e\r\x80\x10\u0536\xb63\xe7\xac7\u0439\xfc\f\x96\r\xbe\x97\xfe\xc3\xfb9\xb2\xfdE\xbavl\xa2g\xff.J\xc3\xfdx\x85\x11\xfa{\x8e\xd1\xdfs\xec\x84\xd7F\np3\x19\x1c\u01f58c\x14{d\xb4&\fmZJ\xf9\xb7\x848g\xf3ud\x9b\u06e8in\xa7m\xd6b\xc6\xcdXF\xfb\xe4\xf9\xd47M\"\x9fkB\xe1\xe0\x97\n\x94z{\x11Z$\x1d\x80\xd1&\x05\xab\x90\u023a\xaa\xe2\x1e\x93p]S!\x92\xa5\x02\xe1 \xb4\x83(\x83\xf2B\xf0\fA\x9d\xa4\xe4\r\xd3\xd8:\x89\xb9\u7f45\x9d\xeb\x1f\xc0xE\x1e\xfe\xe5=\x9c\u007f\xe1\xc5\\<\xbd\x11',\xe0\x9f\xac\xe6\xaf\xf8%\x84\xf5W\xf1\xfd\x900\f\xc9fs<\xf2\u0223l\u077a\x95\xd6\tS\xe9\x98}\x06z$\x80r`\x15\xbb&\x1d*n\u05de\x8c\xd15%\x90Q\x97NhR\x1dqtOi\x8b\xf7j!\b]\x81\xd1\x12\xa1\xb5\xbdO\x8dU\xb6\b\xe5 B\x1f\xa3\xd3\x16\xafT\x85>\x8b\xb4{a\xfcA#\b\x82\x10\xdfHf.8\x8b\xc9\xcf\xfe\x82}\xfb\xb6\u0635+\u0485<\xa5\xd1\x16\"j\xdc*\xae\x83\"\"n\x87\xbb\xf63\xd2}\x88\xae\x17\x1fg\u0082UL;\xeb\x8dLZ~.\xf9\xc6\x16k\xd0U*\xe2\b\xaa\xf0nA\xf5p\xa0\u0586R\xa0q\x94\xc0!\n\u0280$pF\x18\x91\xd8\xf1\xc6E\u061e$*\x83C\x82\x94\xbdA\xb2a\x9c\xe8\xfd\x14\x8d:\xa2\x94B\n\xe8\xda\xfe\x12\xc3}\u01d3\xcd\xc1\xdapHr\xb9\x1cs\xe6\xcc~\"\xfe\xfff\u03de\xfb\xca\x14\xf3\x83\x03#t6\xd5qag]T[\xcd\xe2\u06f7\xf7|\xfa\xc7_\xbf\x8e\u00db7\xfcFxE\nI\x18\x86\xcc8e\rK.\xf8cra\x0eot\xb8\xc2\xfe\xc6$\x89\xa80\xc32\xb4\x8c\xbc\b+\v\x04\x93\xda\xe5\xa9\x18\xea\x98\x04\x96\x13\bi\x1d\xc9|$\x19a\x902\xaa\xae\x010\xaa\xed\xa0\x84\x9bJ\x1eO0\xb0\x94\x13b\xb1@ \x04N&K\xcb\xe4\x19\xb4t\xcc`\ua2b3\xf0J%\x06{\x8fs\xec\xc0^\xfa\x0e\xef\xa6\xff\xe0\x1e\x06\x8f\x1e\xa68\xd4Oyx\x00\xbf8\x82_*\xe1{%\u029eO\xb9<:\xf6\xb6\x04\xe5\"3YrMM\xb8\xb9<\x99|-\x99\xdazr\r\xcd\u050f\xef\xa4y\xea\\\x1a\xa7\u03a6~B'\u0646\x16\x1c\x95%\xeb)\x18\xf5\u0445\x02a\xb9h\x8f\xd2(\xabJ1\xa6z\xdb7\xa6\x1a\x97\xa7\xe2\x05m\xa8V\x04\t\xa9\x90\xd2E\xaa\f\x02\x05\x81]n\xca\xd8\x00g?\xab\xc9\xe7\x05S\x97\x9d\xcf\xc4\x05\xcb\xe9\xda\xfa\x02/<\xf4\v\x1eZ\xff^fO^C\xa7\x94\xc8\xc87\xfb\xe4\xf5\xca]\xf6T\x17R\xf6|\x94r8p\xe0\x00\xf7\xdd\xff+\x06FF8\xfd\x9c\xb3hn\x9eN\xe0\x85\t\xe9Y9\x8e\x99\xe4\x18&\x8c\xb4]v\xe4RJ\xc5f%)\xc2I\x90\xb1 \x95\x9c\x1c\xed\xf9\xc2rUV\x8b\xa0,, C\xdb\u045b\xf4]\xa9\xab\x03\x89\xc6\xe8\xd6\rP\xf4<\x9a\x9a:X\xba\xf2M\x1c9\xb2\x97 \bP\u02a9\x10\x86\xa6\x02\x87\x86a\x88\x10hG9\"\fC\xa1\x03\x9f0\x88\f\xf7\xa4-q\xc3\xdd\a\x19=v\x84\x97_x\x9c\x89KNg\xfe\x9b\xaeb\u0082Udkj\xc0+B\x18\xa2\xa4\x8d\x88\u04c0\x17\xa6\x03\x9e\x05\xa1\xb1\x01\x1d\xb5\xd2$\xd8u\x1c\u0111l\x88\xa2\x12\xa5\x97\x14u\xaa+\xb6\x10)\x188\xc5\x05\x1b\u049b\x1cH%\t|\u0631\xeeW\x84QS'\x85\x04cp3\x19:;;\x99>}\xfa\xa3\xe95\xf0\x8a\x14\xf3\u03a6:\x8e\x8c\x96\x99Tk\xa7\x0e\x9f\x1d\xe5\v\xbf\xba\xeb\x8e\xdc\x13w\xdc\x12U\xee\x8a\x13\xa2\x05\xfa\x15:\xf4i\x9d<\x9b\x15o\xf9\v\x1aZ;)\x8e\f&8\x9a\x88\xbc\x1eLD\x02\x1a\xa1\x11\x81%d\x84\xa6\x92p\x93\xb2\xbe5b\xcc\xd1&\x15\xfddM\xf25^`\xacw\xb1\x93\xac*K\x88\x8e\x82hP\x95x\uf60d7\xa6*B\xca\x18CP.\x13D\x13\xa9\xc2qqs5\xb4M\x99N\xd3\xe4\xe9hs\x01A\xa8\x19-\x96\x19\x1d\x1a\xa0\xd0\u007f\x9c\xd2@\x0f\xe5\xe1~\xbc\xd1a\x82b\x81\xc0+\xa0\x83H\x9b+%N&\x8b\x93\xad\xc1\xa9\xa9#[\xd7H\xa6\xb6\x9elC3\xb9\xa66r\xf5M(7WI\x8f\x05t\x10\x12\x84>~P\xc0\xf1}T\xa0\x13\x85B%p0-\x1c\x88\x19\xf6\xf8\x06\x16iuY\nw\xb27\x82\x90\x0eB\xb9v\x80+\xe9H\xc0\x04\x91\xb1\xbf\x94\x94\x06\x86\x187~.s\xcfz\v][_\xc0\x1b\xee\xe3\xa1;o\xe3\x8cs\u03a5\xa5#G\x9d(\x9c,\xe6\xafpW\x1e\x04\x9a\xb2\xe7\xe3\a\x01\x99l\x96G\x1f[\u02f3\x1b6\xd0\xdc6\x89\x99K.@\xca,Aa4\x9daV\x89o0\x15\xcc\xda\xe2\xdb\xf2\x04\xf5\x85 \x95t\x9f\x92\u0449\xd0~\xaa\xebH\x9c\x88 \xb1\x84\xaa\"t\x1ckb\x15\xc1\xa1\x15\xb5Fe\u011e(\xad\u0224<\x94$\x06?\xf0(gj\xe9\x9c}\x1a\xe3\xda\xef\xe6\u0211\xbdQ-\x90\x91\u72c98\"I]}\xab)\x16Kr\xa0\xf78H\x11*\x89\xc4\x18\xa1CKrZ\u0264\v\x02\x8a\x83\xbd\xec[w/=;^b\xc69\x970\xef\xc2\u02d80s.\x19GQ\x1a\x1d\u0173)\x17d\x94\xed\x9eCc\vy`\x04~\xe4c\x93\u02d0H\x18IQr2vG\x15\xe2D\xd1NU\xf1Oo`c\x06\xfd\xa3\u04f1r\x04\xc7\xf7\xecd\xdfs\xeb\x00R\xf9\x9f\x06\xd7\xcd\xd0\xd9\xd9\xd9=y\xf2\xe4g\x01\xae\xbb\xeeZ>\xfe\xf1O\xbc2\xc5<\f\n(\xc7\x16\xf2\x97\x8d\xf9\xd8m\xf7={\xd9\xdd\u07f9\xde\xc6s\x83\x90c\x16\x8a\xd6!\u065a\x06\x96\xbd\xf1O\xe9\\|>^i$\xe5\xbef\a\x19b\xa3,!%\xb2,\xec\x00C*\x930\xc9\xeeHgIUy\nW\xceR\u0080\t\xad\x87\x84\x0e\xac\x86\xb5*\xe2{Tc\x14\x88\xdaH\xab\xae\x89\xa4V\xd5T\x86I\x919Bk\xf0\xca@9\xe9j\xa5T\xb8B\x91\xcfg!3\x81|\xdb\xe4J\x8cZ<( \u04a9%\"\xbe\x13\"c\xba\xf8\xe8\xa8#\x9bQC\xa8C\x1b^\x1b\xb9\xd4\xc5x\xa1q\x05\"\xa7\x90e\x93\xe8\xea\x05\xd5\x1dy\u0711I!\x93\x9b\u0264N=\t\xeb.\x94\xbd\x01\xa4\x8bRN\xe4_a\xaa\xa6\x04\x85\x89\n\xbag\bU@\xb6\xd6a\xf6\xca7\xb1\xf9\xc1\xdb\xe9\u0677\x9d\xadk\xef\u7a67\x9faJ\xeb\xb9,\xccJ\x94\xd0'\xc9\xd0W\xa4\x90\v;%\xe9\xfbx\x9eG6\x9b\xe3\xf0\xe1\xc3\xdc{\xdf\xfd\xf4\x0f\f\xb0\xea\xfc7\xd02y>\xa1\x1fB\xa0#\xbf\"\x12\x88\xc3\xca\f#\x92\x1b\xab\x9a\x12B\xd9\xf7WX\xb23N|\u0462:?GG\xd0BNJ\xb2Y+$ 4\x18\x19}L\xabJb\f\xf1\xa8\xe9\x18\x9f\x12\x93Z\xfb\xa9/n\xb4\xa1P*Q\xdf<\x85y\v\u03a4\xbbk_J\x9e\x17Y\x0fD\x039\xabW\x9f+::\xa7\xbd\xfc\xc2\u018d\x93w\xef\u06a9\xf6\xef\xdcn\ub0b4~&:4\x11\x89j\u05f5\x00\x86\x8f\x1eb\xe3\u03feC\xf7\xc6\xf5,\xb8\xf0r\xe6\x9f\u007f\tM\x13;\x19-\x96(\x95\xcbv\x03J\fj+\x9a\x1b/4d\xb4\xc1Q\x95\xe7\x11\xf3\f1\xf9\x99\xa44\xa5\x92\x8a\xe2B\x9cL\xacF7\xa5L\v\u0222_\xa5P\xb8\x19\xd8x\u07cf9\xbe\xdfz\x1eIe}\u0305\x90477\xd1\xd8\xd8p\xeb\u99dfy\x04\xe0\xf2\xcb/}\xe5\x8a\xf9\v}B\x02\xda\x18s\xfa\xdd;\xfb\xfe\xe9\xce\xef|\x95c{\xb7G\xb4\xb6\xb5r\x14VRb\x85\xf5\x02f\x9dv\t\v\u03bb\xca\x16Y/\x88\n\x87\xb1\xb0J$\x19\x14\xda \x03\x81\xf4\x05\xf8Q\xdd3q\xe7]\tI\x8d\xb3\a\x8d\xa9:\xc0$\xb8[\x14\xa7\x12\xad+\x83\x0e\xc2h\x047\xeaV}\"\x8a\xdbA\xd4\xc8\x13:r\xfb\xb5S\xbe\xb81\xfb,\xd2\u040e\x86P\xa3M\x00\x06B\xdf\xee\xee1\xd6fDu\xa3R\x95P\x12\x1dI\rc&X\u0349\x98\xb7I\x1d_\x83\xacDf\xa5\xdd\xe8b2\xcaT\ba#H\x05\xf6\x926g\xab\u0605FS\xb8\u05ab\u0669$\xb1\x8c\x91\xc0\xc6I\xe4\u06b7)\xbd\xe5\xa1a\xa6\xccX\xc1\xfc3/\xe1\xf1}\xdb)\xf4v\xf3\xf0m7\xb1h\xe5\u9d0c\xcf2\xc5-\x9e\xb4\xc4}\x85\xf0\x95 \b\xf1\xfc\xc0F\x9f)\xc9#\x8f<\xc6\xfa\xf5\xebii\x9b\xc8\xecS\u0788r\xeb(\x15KQg\x1b\xdd\x1f\xa6\u04bf\xc4P\x8a\x10\n%\xdd$\x872>\xb9\x19a\tO-\xa5%M\xa3\xc9F\xa3\xb0\u0120\x13\xf1|\x0e\xa9p\xe2\xc8$/\xe6\u0174\xb40\xa6\xd1T\a\x8f&r\x96d\xb6!n@\xfc\xc0C\xe4\x1b\u9735\x92\x86g\xefchx \xcab\x96\x897\xb8\xef{\x94\n\x05\xae|\xd7;~\xfa\xa1\x0f\xff\xd9\xfao|\xe3;\x97n\u06fa\xe5\x8a\xed\x9b7\xe5\x8e\x1e>@\xe0{\x91\xaa&\x88\x1cU\xa3\xcdJJ0\x9a\xae\x1d/\u0473\u007f'\x877>\u0242\v/\xa3s\xe5j\xea\x9b\xda(\x97J\xf8\xbe\aZ#\x05\xf8\xa1\x85H\xe2\x00x7\n_Q2.\xe4\xd2n\x84\xca\xde?FV\":D\x95\u0657\x957J\x11)i\xc6\xeem\xc6P\xd3\xe0\u0435}'\xcf\xff\xe2G\x16>q\x1c\x94\xb4V\xbeJI\xa6M\x9b\x1e.[\xb6\xec\x81\xf8\xff\x99>}\x96=\xa5\xfc\xa1\xd7\u05be\x82\x91\v\x9b\\\r\xf0\x96\xff\xf5\xf9/\xfd\xf0\xdb\xdf9\xe5\xeeo|9\x04T\xea<E\xack5:\xa4c\xe1\x19\x9cs\xd5\xe7ih\x9d\x867:R\x91\xebD\xd2'\xa1\x05\"4(_\xa3|PZ\xa4 \x841\x05\x8e\xb1\x12\xbbT\xe7\x9b\xcasE\xa4vTc\xacdQ\u01dd\xbe\x01?\xfa\xb7\xacD\xb8c\xb2\x00\x93\xef'*P\x10\x8c\x89\xa2\xaa|\xcc\x0f\r\xe5\xd0$2\xb0\x18\xb2\x11Qwm}\x8au\xd2m\xa7C\x91\x93\x82m*\xf7@|<\xad\xcal\x8c\xb0K-,\xfc\x14\x0fx\xa4\xd5`UZ\u05b4\x061\n\xb3\x95\xd2A\xban2)g\x17\xfc\tMT\xa2vI\x9e\x8b\x10(i\xc8\xd5\xe4\xc9\xd67s`\xcbz\n\x03=\f\x1e\xef\xa2q\xea<\xda\xe7\u0367Y\x84\xd4:\xe6$\xdc\xf2\a\x86WB\xad){\x1e\xbe\x1f\x90\xaf\xa9a\u03de\xbd\\\u007f\xfd\r\xec\u0739\x93\xa5g]\u01bcS/'\u0502 \xb6j0\x11\xb4\x96\xea\uc171\xd3\xd7\xcaq\x91\xcaI\xab\xe8l\xef\xa3\x04ZEo\xba\x02\xa3$AFb\\A\xad#\xc8\xc4\x1f\x13\"\x82\xf8\"\a\xc6P\xa3uh\xbb\xe3\xf8\x84\x9d$\x17\x8f9\xe9\x91\xea\u0423nAG$|]\xae\x86\xfe\xa3\xbb\xe9:\xb2\a%]\vgD'\\)\x04\xa3\x85\x02\x8e#7\xbf\xfb\xb2K\xbf\xf4\u033a\xc7~v\xedw\u007f\xf0\x8c\x10H\xc7\xcd,,\x95\u02b2\\\xb6\xf6\xb9\xb6V\x1aat\x98</k-\x1b\xd2s`7\x876=M\xef\x81]()i\x9a<\x8d\x9a\x86&\xeb4*\r*^\xf4Ql\x9bR\x8a\x8c\xe3\xa0\x1c\x85r\x14\x8e\xe3\xa4|\u0355-\xda\"M\u041a\xe4\xf9*Qi\xae\u0318\x8d9[\x93\xc7\x04p\xdfu\x9ff\xfb\xda\xfb\x91R\xdab\xae\x14\x80inn\x16\x17^x\xc1\x86O~\xf2S\xd7^s\xcd5\x85\xad[7\x89\xaf}\xed\x1b\x89\"\xe6\x0fzm\xef\x1eT\x00\xbd\xc6\\\xb5\xee\xa1\xf5W\xfd\xfc\xa6\x1b\xad2u\xec\x14d\xe4\x02\xd68\xbe\x93\xb3\xdf\xf1\xb7\x8c\xefX\x8272\x12\x8d\xf6F\xc56\x04\xe5\x19T9\xc4-j\x94\x17\x17)\x81\x92.*\xc2r\x11\"96&\xaf\xb9\x10\x91\u02df\xf8\r+\xa6R\x95uh\u0426\xf2A\x11R\xc1\x15K\x1a=\x18`\x02\x92\x01\xa4\x8a\x06\xfb\u0109\xaex9V\n\xb1\x1dN\b\xb4FkK\x18&\x8c\xbb\xae\xb8\u02c9\x14\xbc]\U00069c0fP[\x88\xc5FhE\x12*c\xd9\xf6*\xd7\xc6\xc8HH\xbb\x02\xbfF\x1286n+\x8c\x8e\xbcUi(\"\xea\xab\x13\r\xa2\x1d\u00d6\x8e-\xe4(\u01e6\x80\x13\x1d\x99SG\xc1\x84\x9fH\x9fTB\x83\xf6\xc1\x1b\x19a\xe2\xf4\xa5,^\xfd.\x84\x14\x8c\xf4\x1e\xe3\xd1[\xbf\u0266\xdd\xdd\xec\xf7s\x94C\x13\xb90\x9e\xbc\xfe0\xf0\x8a\xa1\xec\xf9x~@&\x9b\xc1\xf3<\xee\xbe\xfb\x17<\xf3\xcc3\xb4O\x9a\xc9\u0715o\xc6\xc9\xd5\xe3ye\x12\xec.nXte`G`\xb3>e\xe4\x8bdHd\x1d\x95H\xb9\xd4\xfd\xab\x1d(\xd7Jd[\x86\\s\x06\x91\x89&4\x1da\xff\xec\xdaiN#\x04B\xc9\xc4J\xc3(\xecC$\x06\xa6\x89-E\xfc\u0426\xb2\u07a5\x90\x94\xbc2\xd9\xc6\tt\xce\\N\xc6\xcd\u0606H\xa4\bF)\x18\x18\xe8\xe7\xf8\xb1\xe3o\u0771s\xd7B\x80\xb7\x9fs\xca\x03\xdf\xfe\xca?~\xe0\xfd\u007f|\xf5\x9b\xdey\xd5\xfb\u007f\xb5\xf2\u0333i\x9d0\t\xa4\x93\x0e\xdb\u0104!\xa1_\xb6\x1e\xe9\x02F\xfbz\xd8\xf2\xe0\x1d\xfc\xfa\xfa\xbf\xe5\xa1\x1b>\xcd\xd1-\xcf\xd0\xd0PO\u02f8\xf146\u0593\xc9f\xc1\xcdbT\x862\n\xed\xb8\b7\x13=\\\x90\n\x13E]'gkQ\x11p8R$J\xc5Tnv\xd2df\xf2y\\\a\x1e\xbb\xe9Z\x9e\xfa\xc9wm\xb7\xed8\x11\xf4\x85\tC-\x16-Z\u013cys~$\x848>n\\\x9bZ\xb0`qr;\xfdA;\xf3\xa7\x0e\r\x88\xf3\xa75\x85\u0198\xa6\xfb\xb7t\xfd\xeb\xbf]\xfbOSv<\xfdh(b\x11uJEa\x8c\xc6q\xb3\x9c\xf5\u058f\xb3\xe8\xac+\xf1K%K\x04\xc6<\x896\xa8\xc0X\x82E\xa7\x18\xe0\x8a\x99\x83=.\xc5\x1dA\xfc\xfbX\xe8\xa3J\xd5)\xaa\x8c\xf1\x13T#6\xaf7\xd5\x04\x86\x9d\x8c0V\u0652U\xd5#\ufc5f\x82H\xdd\x14\xa9\x82\x19w\u035e\x86b\x90\xf2\xd8I\x9f\x0e\x12\x83|\xfb\xd6[\xa2h\f\xfcq\xc2\xc8/'<\u007f!R7\x9c\x10v\x98\xc3\u0122\x01\x11I\xc6R\xafQ,\x9b\x8a'\xf6\x1c\x17\x91\u0260\x1c\xbb \xb5J\xe9cS\xafk,\xbdJ`\x19Y\xbdO\n@e2\xb4u\xcc\xe0\xe5\x9d\x1b\x188z\x88\x81#\a\xc9\xd471a\xf99\u4961\xd5\xd5'\x90k'\xaf\xffb!\x8f\u0580\xe7\x05\x94\xca\x1e\xc6@]m\x1dO=\xf54\xd7^\xf7\xaf\xf4\xf5\xf5\xb1\xf2\xfc\xab\x98\xb9\xfc\u0354\u02be\u0748\xe3\xb5l*\x837\x96l\x94\b\u9814\x9b\x14\xf3*\xf6.\uaa0d\xac,\xdc\xc0\x11\x98zEc\xa3C\xaeVAF\x8e\xa9Pv\xdd\xfb\xc6j\xd75\x1a\x1d\xf9$Y\x8bQ]M\x10\xa61\xe34Y\x18\xe1\xcc\xca\u0252s$G\x0fm\xa1\xbf\xefh\xc4\xe3Tn\n\xad\r\x19\u05eds\x1c\xe7\xc1\xfb\xef\xbbo;\xb8\xea\x9ak>\xa7\xef\xbd\xf3\xa7{\x9f]\xbf\xf6\xd6\xfaq\x13\x0f\xb8\x8eZ822\xd2R,\x96D\xe0\x95\x01aD\n\xff0QL\xa5T\x8a\xd2\xf0 \xc7vm\xe2\xd0KO\xe1\x15Gi\x9e4\x95|\xdbxL\xb6\x16\xa3\r\xa1\x94h\xe5D\u0779\x8ax\xaf\xd4t\xa6\x00W\u0187^\x81\x12\x16\xeb\xafVb\xa7g_\x05\xf9\x86\x1cJ\xc0\xba\u007f\xff&\xf7|\xe5o\t}\x1f\xc7q\xad@CJ\x82 \xd4\x13&L\x90+V\x9c\xf2\xe0\xe7>\xf7\xf9O]s\xcd5\u07bd\xf7\xfeB\xde|\xf3\xf7\x93r\xf0\a\xed\xccO\x9f\xd2d\x00^\x1c\xe1\xf2\xfb~\xfc\x833\xd6\xdf\xf5C\xbba\x981\x9dlT|\xe7\xad|+K\xcfy?B+t\u0673\x9a\xf1\xd0 \x83\xa8\x90k\x93\x14\xf2j\xb2\xa4\x82\xb5\xc9(\xe8U\xa9\fJ:Q\x81\x97\x95\x02O\xcac2)\xba\xd5\xf5D\x87\x10\x84\xb1A\x96H&\xe0@@ \xa0`\xc1-\xa1RE0\xaadf\xccfB\x14\xf6j\xb0\u01f1b\b^\x84\xd1K\x91\x86\xbe-\xf9\x14K\x9f\xd2A\xe6c1\xed\u050cDu!\x17\x15hE\xa7:h-\x05~N\x11\xba\x12\xa3\x04F\u026abo\x84\x04\xa5\xec\xa8s&\x87\xccd\x11n\x06\xa3\x94u\xcfs\x05AF\x10:\x820\xfa\xbbQ\u0092a\xa9<\xd3D\xd2(m^d\xe0\x19\xc2b\x89\xd6\t38\xe3\x1d\x1f#\xdf\xd0L\x18\xf8\xac\xbd\xf5\x9b\xac\u007f\xf8!\xb6\x15\xb3\xbc<\xaa\x91\x9ctT\xfc\xbd`r\xb0\x83e\xe52A\x10\x92\xc9dx\xf9\xe5#|\xeb\xdb7\xb1s\xc7\x0ef,<\x93\x85g\xbe\x03T\x8e \x8c\"\u03f5\x1d\x96\x8bo\x85\x8a<N #\x99pZ\xfcDJ|\"\xb0\xf7\xa5\b\xa3\xf5\xe5J\xea\xf2\x8aZ'\xb2\xe2\xa8U\xc8V\x17\xd1\xea\"\xea\x1d\u00ac$pA\xe4\x14\"\xe3\"\x1d\aGH\x1c)P\xaab\xdf!RSj&u_\xa4\xbe=BH\x8a\xe5\x12-\x13\xe7\xd19c\x99\xdd\xc8t\n\xeb\xc4\x12\xc0{\xf6\xec\xe5\xe0\xc1\x83W\x1bc\xf2\xe0\x87\x1b6o\x8b\x90O\xa1\xdfs\u025a\xef\xdd\xf4\xf5\xaf\xce~\xff\a\xde\xff\xe9\x8b.\xbe\xa8w\xe6\xdcy\xe4jj\x84\x91J;n\xc6\xc88cX\x87\x84\xbeg\u036d\x82\x80c{\xb7\xf1\xf07\xfe\x81\x1f}\xea}l\xb8\xfd&\x8a\xbd\xdd\xe4\xeaj\xa9\xc9\xd7 \x8dI\u049at4\xc3\x11\xd1T\xa8\xf4\xcfV\xf53\x99\xea|\xd4\xe8\xf5\xadk\xc8\x12\x16}\x1e\xfe\xc6\x17\xb9\xf3\v\x1f\xc3/\x15\xadE\xb7\x92\x91\x8d\x87\tkkk\u054a\x15+JW\\q\xf9\x17\x85\x10#\x9f\xfb\xdcg\x9c\u056b/\xa8J\x81\xf9\x83u\xe6?{\xf4)\xf1\u36ff\x8b1\xa6\xf9;\u07ff\xed[\xff~\xe3\xb5\xe3G\a\xfb\"VWU\xfc\u0123\xe3{{\xc7\x02V\xbf\xe33\xb4N\x9cK\xb90RU\u016a\xcc\u0628\x16j\x8eEJ\xa2\xe5\x98\n~\xae\xd6|\xa6\xf1\xf9\xaaH\x8f\xd4G\xe3\x89+G\t\x94\xac\xdep\xec\xc6\x00\"\xefX\xfc<Z\xfc\x89\xbc2\xb50\x85\x8c\xe7\xc6\xec\xdfK!\x14\x02S\xe5A\xa1\x11\t\xb9\xa9\xa9\xf8V\x98\u07d0\xf0md\xe5y\x8b1I\xe0\xc2\xd8\xc9;\x11O\xf2\x85\x06\x19\x82\n@z\x06\xe9[O\x15\x15\x18\xa4\xb6\xa7\x0e)\x04\"\x92i)\xc7E\xb9\x99\xc8\x01\xb12\rk\xe2\xae\\F\x18\xa7\x14\x15eL\xf4}\xd3oB\xb4\xb7%c\xd1\x12;\xd4\xd0<y\x16}\xc7\xf6\u0475\xf3E\xca#\x83\x8c\xf6\xf72y\xc5yd\x1b[h\x91ej]y\x12?\xff\x1d:r\xb0\xa9\uc972\x87\x1f\x84(G!\xa5\xe4\xfb7\xff\x80\x9b\xbf\u007f\v5\xf5-\x9c}\xc9_0~\xe6\xa9\x14\n\xc3\xf6\x84\x1a\x91\x96\x95[\xa6\x92\x8b)\xa5\x83\x94c\x93qRS\x8a2\x82K\xe2\x10f\x05\xb2\xc1v\xe5\x99\xe8\xc0\x1d\xf352+!'m\x03 \xc0\x91\x96 U\u00a0\x84FI\x83\x02;\x89Le\x88M\xa4\xe4\x8a\xe2\x84S\xb6=\xc5g\xf3\r\x84\x85>^>\xf0\x12\xa5r!\"\x1c+\xcfW\xeb\x90q\xe3\xda\xe6ds\xd9;o\xbb\xf5\xb6\xaeo\u007f\xfdk\u0737\xf6\tf\u031e#\xd6>\xf4 \x00\x8f<\xf8\xe0\xba\xc7\x1ey\xf0\xa1\xc1\xa1a\x84\x10\x1d\x9e\xef\xd5\x0f\r\x8f\b!\xa4\x96R\x89\xc4\xdf)\xad\xd8\t\x03\x06\xba\x0er`\u00e3\xf4\xed\u07c9R\x0e\xf5\xcd-\u0536\xb4Y\x8f\x980\xc0\x11\x06%\xad\ubd8c\xeeSM\xac\xb2K\x87R\xa4\xd1\x02\x83r]j\x1b2\xf4\x1c8\xc4\xfd\xff\xfa\xf7\xfc\xea\x1b\xff\x94\x88C\\\u05cd\xe0\x15a\xc0\xc8\x05\v\xe6s\xf9\xe5\x97\xddt\xe5\x95\xef\xbd\x01\xe0\xfb\xdf\xff\x9e\xb9\xfe\xfa\x1b\xf8\x83\x17\xf3\xdbn\xbf\x9dw\xbey\r\x00SW\xad\xfe\xe0O\xbe\xf7\xed\xab\xb7?\xff\x94]6\xd1\x00B\xa50\n\xb2\xb9Z\xcex\xe3\u01d8\xb7\xf2R|\xafhM\xe4\xc7\xe4U\xff\xb6\xa3\xb8\xf8\x0f\x96z\xec\xbf\x1c\u007f\x9fd\xca,u\xb0I\x13\xa2U\r\xae\x10(!\x927C\b\x81P\")\xa2\xd2\x11\xd6\xcc[\xc9\u0502\x8b\xb5\xa5\x95i/\x1dg\x8dj\x18\xf6Lb\xfcobC\x9d\x94\x83\xa3\x11\x15V6\xfd\x86\x8b\u0602 *\xd020(/\xe2\x0f<\x83S68\xbe\xc1\xf1\f\xaa\xa4Qe\x8d*\x1b\\\u03e0J\xd1\xe7\xf8\x06\x11\n\xa4\xb6Gi),.\xaeT\x06\xa5\xa2\"\x1eMz\xa4\x9a\x1c[\xcc\xd3p\u0558l+a\xa8\xba\u046a\xce\xfd\x11,kB\x9fl\xbe\x9e\x86\x89\x9d\x1c\xde\xf5\f#=\xdd\xf4\x1c\xd8\x05\xd9<\x13\x96\x9dC]\u01a1=\xa3c>\xee\xe4\xf5_(\xe7\xbe\x1fR*\x95\xf1}\x9b\xa6S[[\u01e3\x8f>\xc6W\xfe\xe5Zzz\xfb8\xf5\xc2+Y|\xf6\xbb)\x95}\xc2\xd0J[e\\4\x93\x81\x16\xa2\xf0a\xebA.\x1d\xf7\x84BN\n7O\xbb\x8f\uab24\xa6-CM\xde\x0e\x18U\xa9\xac\x88< \\\x89\xcc)\x9c\xac\xb4\xc5\u0704\xa0\xc3\xd8\xf1?\x81\x1bBm\xc64h\xa2\xca\u0714\x14\x1c\xa9\x81\x86\xda\x1a\xba\xf6o\xe4\xf8\xb1CH\xa9PJ\xa5,\x024\xc5B\x91\xfa\xfa\xfa\x17\x1e_\xbb\xf69\x80/\xfe\xc3\xdfs\xd9[\xde\u00b57|\x95\xd0\xf3\u0637o\x1f\xd7^{]\xd7\xfau\xeb~q\xf3\xf7\xbe\xf3p\x10\x84\xadm\xad\xad\vF\x87\x87\xc5\xf0\xf0\xb0\xad\x1d\xc8\xdf\xe8r\x1axe\x8e\xef\xd9\u0281\xa7\x1f\xa0o\xffN\xbc\xe1Ajjkhnk\xa7\xb6>\x8b+\x9d\xe8\xa0,*[a\xea\xd4n\x930\xad\xeaE9.\xf9\xfa\fRJ\xb6>\xf2\x00w\xfd\xf3\xdf\xf0\xdc/\u007f\x9c|/\xa7\xba\x90\x8bi\u04e6\xb1f\xcd\xc5\xcf|\xfa\u04df\xf9\xe3k\xae\xb9\xa6x\xf3\xcd\xff&V\xaf\xbe\xf0\x84[\xe7\xf7\x96&~\xff\x96\xefs\xe5\x15W\xc4\xd8\xef\xacw~\xf0O\xfe\xfe\x99\xc7\xecN(\xe2\xc2\x1a\xb5\xdbR*\xc2\xc0c\xfa\x82\xf3\x98\xbf\xf2R\x00\xc2 H\x82\x9b\xd3\xed\xa90'\x1a\xe0S\xa1\v\u007f#\x8a\x98,Y\xa9P\xd1\xd1\xd1\xc6JE\t\x1dq{\xa1OTtTO\xa1\xa5gm#\u054dg\x10%\x83\xa8\xa9hD\xab\x95\"&\xd1Q\xfb\x1aF|\x83\x1f\rD\xe8\xcag\u06db)\xf6\xb9\x88d\x83\x96\x17\xa8\x98\x19\xc5$\x95\xd4$#\xd2\xc9\xe4^<\x82\x1d\x9b9\xe8\x8aBE\x88\xb8s\x16\t\x14$\xa5\xb4_\a\xfbzXZX$\x01\x1d:\xd6\xfd\xa6HRK2\xc9$?\u057a6ZH\xc5(\x13\x195\xa5<\x9aSu@\x1b\x83\xe7\x01\x03CL\xecX\xc4\xd9\xef\xf98w_\xf7Q\xcaCC<q\xdb7\x994g1\xad\xef|7\xadY\xc1\x94\xac\x1fC\xb9'\xaf\xff\xc4\x15\x04\x01\xa5R\t\xdf\xf7\t\xb5\xa1\xae\xbe\x8e\x1d;vr\xe3\x8d_g\u07fe\xbd\xccY\xba\x9a\xa5\xe7\\\x89\x91Y\x82p\xb4b\xb3:\xf6\x14\x9a\xbaO\xac~\xf9\xb7\x8bFM\x1a\x02\x14\xe0\xe6\x1459\x99D\bV\x11I\xba\xb2\a8\xae@d\x1ck\x8d\xa1\x02d\x18\xcdFDr<%@iC\xe0[\x05\x97L\xddK\xe9\x1e!\xfe\xd5\xf7}\xea\x9b:\x980y\x0e\xfbv\xbfH\x18\x068\x8e\x8a\x8a\xa6\x95g\x8e\x8e\x8e288\xf0>\xe0[\x00;w\xec\x00\xe0\x13\u007f\xf91^z\xe9\x05\x96.]\x9e\xfc\\\xabW_\xf4\xbc1\xe6\x03?\xfc\xe1\x0f\xee\x986\xb5\xf3Ov\xec\xd8q\xe1K/md`p0z\xbd\x94\x95-kS%C\x1e\x1d\xe8g\xf3\xaf~\u02ae\xb5\xbfd\xca\xe2UL]v&\xd3V\x9c\u0174\xa5\xabh\x9a\xd0n\xa5\x8ba5\xc1\x19\xf3ZRY\x88h\xa4o\x80\xfd\xcfmd\xf3Cw\xb3\xe1\x9e\xdb\x18Ly\xaf(%\xad\u07cbMc\x13\xed\xed\xed\x9cz\xea\xaa\x1d\x1f\xfb\xd8G\xaf\x16B\xf4\x02,_\xbe\xfc7\xbeY\xbfw1\xff\xecg?\x9b\xfc\xf9/\xff\xe6\x93\x1fY\xff\xc8\x03\x13\x83r\xc9v\xb31;n\fR:\x84\xa1Gc\xeb\x14\x96\x9e\xf3~\xeaZ&S*\x0e\xa7\n9't\xe7I\x896\x95\x9d\x9bTa\xac\x02\x95S9\xd8\xd1N\x82T.B(\x8c\b\xd0\xc2&\x88W\xa6g\xac\xec\u0764\xc0\xe9\xf4\x10ME\xdc\x1f-\xfc\x10(j\u06dd\xbb\"\x19T\"\xe9\xba\x05F\x1a\xfc\x10\x86}M94Q\x87\x1d\x15\xe8\x88\x0f\xb0\xb0\x88\x88\xd2X\xe2\xc1'\x11'\u070d1\nK\xfdt&u\xbeHOC\xa7\xb8\x01i\xa2\xa2\x1du\xe2\x91O\x01B\xc5\x03I\xa6\xca@LK\x119\xe0\xc9dH\x84\xe8\x18\xac\x04H\xd7~M\x95z\xed\xb5\x16\x11\xa9e\x12#}\x1d\xdd\xc1\xb1\t\u007fh \xf0|\xc4h\x81\x05g^\xc6\xd1\x03[X\xfb\xbd/Q\x1c\xe8\xe5\x81o\xfc#\xed\x1dSi\xba\xf0tj\x95\xa6\xd5\r#\xa2\xec\xe4\xf5\x1fI\x10+\x13\x9e!A\x10\x92\xaf\xa9\xa1\xaf\xb7\x8f\xaf\u007f\xfd[\xac[\xf78\xe3'\xcd\u4d0b\xaf\xa6\xbeu*\u00e3#v87\x9eGO\xcf\xc0GJ\xa6\xf4)V0\xb6q:\x91\xa3\xd2\x00\x8e [\xef\xe08\xb2bn\xf5\x9b\x98Y*>/d$\xb21\x834\x01\fht\xd1@`UR\xd2\x11v\xbe\u00c8\xe4\xebUZ\x9e1\u007f3\x86\x92\x96L\x9e\xb1\x9c\x86\x97\x1e\xa2\xa7\xe7\b\x86\xac\u0154\xad/\x93)\x97\u02e2\xab\xab{U\x10x3\x1d'\xb3\xe7\xc2\v/N\x9e\xda\u04a5\xcby\xe8\xa1_s\xe1\x85k0\xc6\xf0\xd5\x1boPB\x88\"\xf0#c\xcc=\xd7_\u007f\xed%\x1d\x1d\x93\xfff\xf3\xe6\xad+\xf7\xec\xd9C\xa1X\xc0D\xba}m*\n\xb5\xb8\xe1,\x17\v\xec~\xe61v?\xf3\x18M\x13:\x980k\x01\x93\xe7-f\xd2\u0725\xb4M\x9bMMC3N&\x83#\x05\xbeW\xa68<\xc4\xc0\u04579\xb8\xe9Y\x0eo}\x91#;7\xd1\u007f\xe4P\xd5=,\x95\xaa*\xe4mm\xe3X\xb6l\xf9\x81\xcb.\xbb\xec\x1d\x9d\x9d\u04f7\x00\xdcr\xcb\xcd,]\xba\x82?x1\xff\xc2\x17>\xcf\xe7>\xf7y\x00\xee\xf8\u065do\xbb\xe6\v\xff\xe7O\xbb\x0e\x1f\x8aX\xe1\xc8GA\x9b\x84\x95\x96B1\uf5372m\xc1j|\xafT\x95\x9c=\xd6M\xac\xaaS\x16\x86\x8a\x9dd\x95\xa9C\xd5\xfa\xab\xea\u06a3\x91\xf5X/-\x8cFF\xc1\xafFT\x8c\xf3c\uf4b8\xb3\x16\xc6Nr\u0274\xd66.\ua041\x92\xb6\xea\x96\xd8\x18\v\x83\xb1\xc1\xe4\xe8\xc0P.\x85\x84e\x8d\x1b\xe5%&\x98v\u0509\x93\x1e\x9f\xaf8\x83Uu\xb7\xc9\"\x8e\xd4-\x15[\x83\x94z\xc4\xc8H~)+\xd3{\bl\u53a4\x9a\xe67l|\xa2\x8a\u07cad\x8b\x95\xe1%G\br\x11w \x85\xdd\x0f$\xc90\xaa\ud0a4$\bt4\x19K\x1c\x17i_\xcf\u8865\xc0+{\xe4\xca\x19\xcex\u04df\u04f3{\a[\x1f\xbf\x8b\ue75b\xf8\xf9\xbf\xfc\x1d\xed\x13\xbeG\xc3\xf2Nra\x89\x1a\xa5\xf1\xf5\u0262\xfd\xdb$\x88a\x18\xe2y>\x9e\x17\xe0\a\x01\xd9\\\x8e\xb2\xe7\xf3\x9d\xef\xdc\u011dw\xdeA\xbe\xb6\x81\x95\x17}\x80\u0273O\xa5P\x18%\f\xc2\xc8\x02\xda$\xa6r\t\xe7\x92\xccW\xd8\xe1\x16Q\xa5\x81H\xb9\xf3\x88\xd4L\x82\xb1\xfc\x89S\xa3\xc8\xd5\u02a8\xdb\x14c\uee0a>\xbcJuf\xacp@\xd6: 2\x18\x05\f[\x82\xc5\bc\xe7\xf7\xc4\x18W\xdc(\xfdb,\xd4\xe7\x05!\xed\x9dKhi\x9bB\xcf\xf1\xc3h\xadQ\x11\xdf#\xa5\x12\x85b\x91}\xfb\xf6\xca{\xef\xbdw%\xb0g\xecky\xe1\x85k\xd2Ma\xf8\xf0\xc3\x0f\xc9{\xee\xb9\xc7\x11B\x8c\x02?6\xc6<q\xe3\x8d7\xfc\xe5\x83\x0f>\xf4\xc1-[\xb6\x8c\xeb\xee\xeeft\xb4\x80R\x8eA\"\xaa%\u0155\xaeo\xa0\xfb0\x03\u0747\u067e\xee\xd7\xe4\xea\x1a\xa8mj!SSg\x9d\x1e#'X\xafT\xa484\xc0p\ufc6a\xf76&_\x85\xact\xe4Z\x87b\xe2\u0109\xac\\\xb9\xf2\xc0y\xe7\x9d{\xf9\xdb\xdf~\xc5\x16\x80\x1bn\xf8\xbf\xe2\x03\x1f\xf8\xd0o\xed{~\xafb\xfe\xd9\xcf\xfe=\x9f\xfb\xdc\xe71\u01a8\xcb\xdf~\xf9\xe7\x0e\x1e\xd8_\x8b1HiI\x99x\xba3f\x8a;f\x9f\xce\xf2\xf3\xaf\xc6\xcd\xd6Q*\xfe?\xf6\xde;\u0732\xab\xba\xf2\xfd\u0375\xf6>\xe7\xe6{+\xe7*\x85RD9`\x81$\x04B \x92\rn0m\f4B\xed6\xc16m\xbb\xbb\xdd\xee\xee\x87\xc1&\xf8\xb5\x03`c\x03\xc6 \x82\xdbX\xc6&H\xc6`L\v\t0 \x19$\x94\x91J\x15\xa5\n\xaa\\7\x9e\xb0\xf7^\xf3\xfd\xb1\xd6N\u772a\x92\x84x_\x9b\xea\xfa\xbe\v\xa5[7\x9c\xb0\xf7Xs\x8e9\xe6\x18\xd3 \xa6>\xe7D\xfb\\\xc4J@\xef-\xc6\xf5\xd8\xe3\xfe\x9a\xc3M\u0e6d\x05\xa2B\xff\xed\x01\xbd\xbe\xc8\xe3\x03\xa2\rX?\xc0\xc1\x80D\xc6\xf7\x85N\x83U\xae\xcfN\x94\xc4y\x8b\u05eeC;J\xdaq\xd0u\f\x17\xaa\x01\xed\xd1[Ulu\xb4\"&\xaa\x96FR\x1a^\xf9\r\u03bc\u0291\xca\xd6^\x0e\xe2\xb64+\xab\f\xa9\x8a\x90\t\xa9\x94\xf4\x15\xb5\xa5\x0f\xec\xf0\xab\xda\xd2\xf0[\xa3\x00\x91\x85!\xeb\u0346\n]O\xb1\xecPv\bF\x15q\xc6o\x1c\x86\xe9\xbd\x03\x92\xc4!\x89\ay\u007f0\x1a\xba\xd3\xf3LN\xad\xe6\x9a\u05fe\x83\xf9\xc3\xfb\xd9q\xff\xb7\xd9|\xc7\xd7\xf9\xebw\xfd'\xec;\xfe\x18s\xf6j\xcen\xce\u04f4\x92\x1b\xf4\xfd\xdf?5j%\xa3\xd3\xe9\x86\xc5 o\xb3\x9a$)7\xdc\xf0)>\xf2\x91\x8f\xd2M2\x9e\xfd\xfc\u007f\xc7Y?\xf5r2'dIRvn.\x18;Uc\f%l@\xda(t\xcez\x94\xe1Hy\xdbe\x06h\n\xcd\u0248\xa8a\x8aJ\xba\x8fZ\xae\xeaj+\xffh\x8c\xc1X\x83\x0ey\xf7M\xc4\xc1\xacCS\x83D\x95\x03!-\xfd\x8fLAe\xe6A\x18~\xc8\x19\x8d-c\xdd)\xe7\xf1\xe8\xb6{H\x92\xa4\xb0\xa0\x15\xe3\x03\x8fgg\xe7\xe2\xef~\xf7\xf63\x9e\xc8k{\xf5\xd5\xcfw@\xf7\xc3\x1f\xfe3y\xcb[~YE\xe41\xe0\xbft:\xad\x0f\xbe\xf3\x9d\xbf\xf3k\xb7\xdf~\xfb/n\u0672u|\u03de\u0752e\xae\xdbh4bU\x95,\xcbJm|\xa5\x93\ah\xcf\xcd\u041e\x9b9\xee\xef6\xf9\xba\u007f\xf8\u007fk\f\xaa\x9a\x1ac\xa2\xd3O?\x8d\xcb/\xbf\xfc\xa1\u05fd\xeeu\xaf\xba\xe2\x8a+\x1f\x00\xf8\xe5_~\xab\xbc\xedm\xbf~\xcc\x06\xf6)\x81\xf9g?{#\xaf~\xf5\xcf=\x05\xff\xf0\x00\x00 \x00IDAT\x17\x15\xeb\u007f\xff\xef\xbf\xf5\xae\xfb\xef\u007f\xe0\xe2#\x87\x0e\xf9\x9a\xd1\xd6WC\xb24alj\x05\xe7_\xf9:\x96\xad=\x9bvk\u059f\xe9Zsw\xaa\xd4\xdeR\xd7\xe4I\x8f\x1a\xb5\xee\xe4\u0783\xe0R\xf3\xe9\xeeUhK\x18\x94\x92\x0fO(\xb7u\xb4R\xfbK\x84\x97QY\x81X\x8a\xe9\x9ev\x80T\x90\x18\x9c\n\x92x\xdf\u7d2b\xb8\x14\xe2\xa0\x1c)x\xff\x8a\xefJ-\xbe\xae\"\x0e\xad\xaa\x05j\xa6G\xf9\u07f5^\x9dKE\x8d\xa3\xbd\x0ex\xda\xeb\x8aW\xbfU\x9d\b\x1a\xf9OD\x91!\x1a3\xd8E\rl\xecA\xdcJ]\x9aFO\ua560X\x04\u027c$\xcbW\xee\xe1\x86K\x95d.\xf5V\xab\x99w\xa1\u031c2?=\xc3\xca\xf5\xe7\xf1\x82\u05fe\x93\x9b?\xf4\xab\xec{\xec!\xee\xfe\xa7\xbf\xc3D\u00d8\xff\xf1\xfb\x8c\x9c\xb3\x92\x8d2\xef=\xe7\xff/~\x17\u007f\x92$\xa5\xd3\xe9\xd2MR\xd2,%n4\xc82\xc7g>\xf37\xfc\xe9\x9f~\x88\xb9\xf9Y.\xbc\xfcg\xb9\xf0\xaa\xd7a\xa3a\x16\xe6\xe7Kh\xd6z\x17V\\?\xb9\xa2\xc9D=\xb3\xa6\xfe2JC\xd7\xe6\"C<\x1a\xd1\x18\u036d\x9a\xf5\xb8\xb2\x1b\xa1\xec\xa6ErE[\xe2\x1b\xc7q\xef\x85d\xd5\xf9\xeb\xcd:\\\xaad\x9a\x15\u02b2\xc2>BJ\x95[FB\xd7\u016c>\xe5bFn\xbf\x89C\a\xf7\xe2\xe2\x18\x1b\xf9\xc8\xc94M1\xc6\xd0\xedv_\x04\xfc.\xc0\xe6\u035b\u0638\xf1\xf4c>\u0737\xbc\xe5\x97U+\xf1\x8e\xcd\xe6\xf0\xa3\xc0o|\xf5\xab_\xf9\xc7/|\xe1\v\xbf\xf3\xe0\x83?\xbcl\u01ce\x1d\x8d\x9d;w\xa1\xaai\x1c\xc7VUE\x8b<\xcez,\x9e\xcb\xdb\xd8*u\x94\x0fw\x03x\x17\x15\xb9\xf7t\xd14M\xdd\xc4\xc4Dt\xca)\xa7p\xed\xb5/\xfc\xa7w\xbe\xf3\x1d\xff%\x8a\x9a\x0f\x00\xfc\xfa\xaf\xff\x9a\xbc\xff\xfd\x1f8\xee\xad\xf1\x94t\xe6\xaf~\xf5\xcf\xf3\xc1\x0f\xfe\xb1\x05\xb8\xed\xb6\xafo\xbc\xed\xb6o\xfe\u008e\x1d;\x00\x12k}]W<Q\xa7\x881\x9cv\xc1\x8b9\xfd\xe2\x9f!\t\xc1\xc9%4\x94\x95\xb6\xe6\xe9\xd4\xf4xn\x86\x96O\xab\x8b\f\bN\x85L\ri\xfe\xe1\f\xa9Z\x12\x8dH4\"U\x1b>\xf2\u007f\xb7t\x9d!q\x11\xa9\xb3\xa4\x1a\x91jLFL&M2\x19\"3C\xa4v\x88\xcc\f\xe3\xec0\x1a\r\x03C\x18mbhb\xb5I\x94\u0158,\xc6h\x03#MT\x9b8i\x10\xd9!\xa2x\b\x1b7\x89\xa2!\xa2\xa8\xe9?g\x87\x88l\x83\xd84\x88\x8cW\x93\u0628A\x145\x88L\x93H\xe2\xfa\a\x11\x111\x111\x96\b+\x91W\xa4`\v};=\x1dM\xf5P\xa2\x92$$\xc5\x10\x95B\xd9`RG\x94)C\xaa4Rh\xa04\x9b\xc6W]\x91\xff\x90\xd8x)\xe6\x90A\x86\xa4\xf8\xbb\x19\xb2\xd8a\u007fs\u01e3\x16\x19\xb20d`\xd8\x12OD\x98\x89\x18\x9a\x96\xb0}L\x16\xfc\xa9\x17ff9\xe5\u072b\xb9\xfa\u07fe\x9d\xc9%k\x00\xb8\xeb\xcb\u007f\xc9_\xbd\xe7\xbf\xf1\xad\a\xf7\xb1\u06cd\xfa\x83\xe2\x04/\xcd\xf3C\xb3\x9b\xa4\xb4\xda\x1d_\x91\xa7)\x8dF\x13k-7\xde\xf8\xb7\xfc\xd1\xfb>\xc0\xfe\x03\xfb8\xfb\xa2kx\xd6K\xde\xc2\xd0\xf8R\xda\v\xf3\xfe^s=\x90\xac\xd5\x05\x1c\x0f\xaa\xc6D=\x86W\xd2W\xf8\x14\xdeXV0MCs\xccb\x1b&tj\xdaS0\xd5|i\x8bR\xa3J\x8b\x16\xdar\r\x8a\x8e\u0448xQL<j\x89\"!\x8e\r6\xaa\xbb5*\xf5\xbd\v\x17\xac\x94'W\x9d\xc1\xa2\xa5k|\xe4\x9a+}\xccm\x14177\u01d6\xcd[\xa6Tu\x1c8.\x90W)\x0f\x80O|\xe2\xe3\xc5\u7bbd\xf6\xc5\xff\xf4\xe1\x0f\xff\xf9\xf3\xdf\xf0\x867\xfc\xf2e\x97]\xf6\xf7\xd7\\\xf3|\xce:\xeb\xccH\u0108s\xae\x16\xe9X\xfd\x88\xac\x8f\xce3Q\xe4\x95)q\x8c\x8d\xa2\xb0\xcdi\u02ea\xbc\xa4\x99e\xed\u06b5\xf6\x9ak\xaeI\u007f\xf1\x17\xaf\u007f\xf7\xbb\xdf\xfd\xdek\xa3\xa8y/\xc0_\xfe\u59df\x10\x90?\xe5\xca\xfc\x96[\xbef\x9e\xff\xfc\x17d\xaa*\xd7]\xf7\x86\xff\xbcy\xf3\x96\rI\xb7\x8b\xb16\x92@\xaf\x14\xa7\x94KY\xb9\xe1<.\xb9\xfa?0<:\u51deG\xa5Gr\xefq)&\x91%\xaf[\xd1X\x8bbqD\xe2\x88\xc5\xd10)V2b\x93\x11\x89\u00c8\xd6\x02\x9bJ&0\x18\u072b)\x04-N\reR\xa1\x14\x87\x841\x06\x1b\x1b2\x1b\x87\x03#\"S\x13\xbe\x1e\xc8\xfc\xaa|\x92:\xba\xce\xeb\xcbM\xa8\"L\xe5\xa7y\x99\xa3\x06\u04efj\xc5\x1c\x1e\x95\xabf\u06d6\xb7\xc0\xc0-!\xa9\u01fdIM%\xd3\x1fP5pmT<\xb8G\x0eL\n\xa6\xed\x90\xf9\f\x86-\xb5]\xfbJ[\x91\xbfv\u0554qS\x1dQW\xd8$\xdb\x14\u04a6\xf19\x92F\xc8DqN\xe8v:\xa0p\xcee\xaf\xa4\xbb0\xc7W>\xf5_i\xcd\x1f\xe1\xce/\u007f\x12#\x86\xf8\x1d\xef\xe5\xa5\x17\xac`\x95i\xa3\xa9\u00dd\x90@\ue2e0\x1c\u023b\xdd\x04\xe7\x1c\u0366\xcf\xc0\xfc\xf4\xa7\xff\x8a?\xf8\x83\xf7\xb1o\xdf\xe3\x9c~\u0795\\\xfe\xd3ocb\xd9\x06\xda\xf3\xb3!\u078f~\x806\xa1\x1f\r\x1b\xd3&X`\xe4r\x01A\x06z\x1a\xa9\x88\x1f\x90GBc\xd8\xf8\x83\xdb\x1cM\xf5R\xad\xc2{\xaf\xc6RY\x95\xdblHX\xae\x91\xa1\xc8\xdf\xdb\xf8`\x18\x13y\x03=\xa7\xa5*R+j\x10\x102\x97\x11\x0fO\xb2\xfa\xe4\xf3xt\xeb\xbddY\xf09\x0f\x1d\xc0\xec\xdc\x1c\x87\x8e\x1c^\xf6\xe0\x83\xf7=\v\xf8\xa7'\xfb\x1e\xbc\xf1\x8d\xff\x9eO|\xe2\xe3l\u07fe\x83\xdf\xf9\x9d\xdfED\x16\x80\x0f\x01\x1f\xfa\xc2\x17\xfe\xee\xd5_\xfb\xda-W\x9ey\xe6\x99/\u07bf\u007f\xff\xa9\x0f?\xbc\x89\xd9\xd9Y\xb2,+\ue0fc\xc2/\xabq)l\xa7\x15\xaf\n\xf3n\x8f\x868\x8eY\xb6l\x19g\x9cq\x06\xeb\u05ef\xbb\xf95\xaf\xf9\xf9\x0f\\u\xd5\xf3n\xcd\x1f\xcb?\xfe\xe3\x97y\u044b^\xf2\x84\x9b\xd5'\r\xe6w\xdf}\x17\xef~\xf7\xbb-\xe0n\xb8\xe1c/\u07b1c\u01db\xf6\xef\u07c7\x18\xa3\"\xf5\xba\xcae\x19C\xa3S\\|\xf5/\xb2\xe2\xa4\vH:\v~S\xaag\u02cb\xca\"\x8d\xf6L^\xacu4\xa3\x94\x86\xcd\x18\x96\x84\x86\xa4\fI\x97a\xe92b\x13\x9a\x92\xd00)q\x0e\xe6&+9\xac\x82\xb7\xf6\v*V|[\xe7\x804\x04\x19f\xea\u056fF\\q\x11:\x04U\x03\x91\xc5\x19K7\xb3$YL\xd7\u0174\\L[c:\x123\xa7\x11F\x84Fp\x8bS'\xb8\xcc\xe0\x9c\xff\xb9\x19\xa6\x18h\xf6\xc4\x1e\xd6\x14]\xa6\xea'\xaeT\x92\xed\xeb\x19$\xd5\xd7E\n(\u05c1\xf3\x02\xa9\xef\x9a\xd1\x176\x98\x17X\xa9C\xe6\x1dL(\f\x1b\xfa\xcd\xcd{\x84\xa1R\x0e\xba\x8cJ_a\x16\x19C\xbbaH[Bd\xbd\x8aG\x9d7-\xeb\xb6\xdaH\n\x17^\xf9z\xd2\xd6<_\xfb\xec\xef\u041a\x9f\xe6{\xffp\x03Y\xda!z\xe7\xef\xf1\xaa\xcb\xd61f\xbb$\xdd\xf4\x84\x92,V\x81\xbc\x9d\x03\xb9*###\xb4Zm>\xfd\xe9\xbf\xe4\x83\x1f\xfc\x10\xfb\xf6\xede\xe39\xcf\xe2\xaaW\xfc'\x96\xac:\x9d\xf6\xfc\\!\xed\u04f0}\x9c\x83_!\xd5\x15?\x18\x97P1J\xbf\\\xa0\xf6~kP99+D\rCc\xd4z7\xce\xda\xf5U\u05dd)\x83\xae\xd9z%\xa1H\xcfj\x82b\x9a\x06\x9d\x8cQ#\x18u\x98Lp\xa9\xef\xe8\xb3\xf0\x18sS-\x05\xd24\xa11<\u02aa\xd3/f\xe8\u06dfgnn\x16\xa7.\xb8\n\nY\x9a17;?~\xdbm\xdfX\xffT\u07cb7\xbe\xf1\xdf\xd7\xfe\xfb\x93\x9f\xbcA\xae\xbb\xeez\xfd\u065f}\xd5g\x81\xcf\xee\u0739\xfd\xec\x1bn\xf8\xe4E\xe7\x9dw\xfe/\xec\u07bd\xeb\xca\xc7\x1e\xdb9\xb6g\xcfnZ\xad6Y\x96\x9bx\xd5\r\x05}\xd5ni4b\xc6\xc6F9\xe5\x94SY\xbcx\xc9c\x13\x13\xe37?\xebY\xcf\xfa\xf6u\u05fd\xf1\x8bA]\xc3_\xff\xf5g\u456f\xfcY\xfd\xe2\x17ozR\x8f\xfbI\x83\xf9[\xdf\xfa+\xf2\x9d\xef|'QU\xfb3?\xf3\xd3o\xbc\xf7\xde\xfb\x82\xca\u0358\xdcs\xa5\xfa\x86\x9eu\xf1\xcfp\xe6\xc5?\x83s)i\x9a\x14\xff\x96\x05g5*\xa0dEi\u0614!\xd3e,\xea0\x16\xb7\x19\x8a\x12\x86\xe2\x84f\x94\x10\x9b\x8c\xa6&44#\x96\xd4W\xe1\x15@s\xf9\v\x17\x92L\xf2\u007f1E\r\xa2\x95\\g\xc1\xa9\t\x16\x95a\x80n\xb4\xc2\x15\xe6\xaelJf\xfd\xae|\x86\r\U0010d84b\xa1E\x8c\x03\xac\x962=\xd4\xcb\xf7<\xa5\x13\xd1\xca\x1a\xb4\xb3\x98D-i\xc8\xd2L\xd4\xd0\xcd,\x1d\x17\xd3uQi\xf4U9\x80\xf2}Q#\xa5\x11W\u0756@\x8f\xcaWr\f-~\x11\xc2\xe1r]\xa1@'C\u06e1:\xef\xfbV\xad5N\"\xa5}\xa3\x88\x86%\t-\x00$\xb2~\x13\xb0\x13\xf2V%\x8fn\xf1V\xf6t:m\xd4)\x17=\uf348\x8d\xb9\xe5o\xdf\xc5\xdc\xf4>\xee\xfa\xea_\xf1\xfbs\u04d8\xdf{\x1f\xff\xf6\xca\xd3h6\"\u04a4M\xe6N\x1c \xeftSZ\xed6\xddn\x02x\xbf\x95\xfd\xfb\xf7\xf3\x17\u007f\xf1q>\xf1\x89O23}\x98\xd3\u03bd\x9c\xe7\xbc\xe27X\xbe\xee\x19\xb4\u06c1ZQ)\u02e3\xea\x06X^\x1d\xe6\x89Q6\xea3\x88\xeb\xab\x02BI\xec\x8c@$D\u00c6h\xccz\x95\xabj\x0f\x0f_W\x95Uw\x1c{y\xf8\xde\rfo#\x10$\x89\rA',\x96\x98\x86\x80\xeb*\x9a*Y\xe6z\xe8y\xc1\x89\x92\u0186\x89u\x1bY\xbcj\x1d\xb3\x0f\xdd\xe7\x15&\x01\xcc\xd34\xc5X\u04dc\x99\x99\xbd\x1c\xf8\u060f\xfa\xde|\xf4\xa3\x1f\xe1\xba\xeb\xae\xd7/}\xe9f\u067au\xab}\xdb\xdb~-[\xbb\xf6\xa4\a\x81\aU\xf5+7\xdc\xf0\xf1\xa5\xd3\xd3\xd3\xcf\u0777o\u07f3\xf6\xee\xdd{F\x9a\xa6\x97\xee\u0673\xc7\xce\xcd\xcdfI\xe2+\xf6f\xb3!\xabV\xad2\u02d6-\xdf\x1fE\xf6\xd6\x15+V\xdc\xdbj\xb5o\xbb\xf6\xda\x17\x1e\xb8\xea\xaa\xe7=\xfc\u044f\xfe\x05o|\xe3\xf5\xbc\xeb]\xbf\x1b\xbd\xf5\xado\u0396,Y\xfe\x94\u0298'\x05\xe6\u007f\xf5W\u007f\xc9k_\xfbz\x05x\xff\xfb\xdf\xf7+[\xb7n}\u0561C\x87\x101&\x0f0\xc8\u06dd,\xed\xb0b\xfdy\\\xf0\xdc724\xba\x88n\xa7\x85SC\xa6~\xa8bP\x1a&e\xd8&LF\v\x8cGm\u01a2\x0e\xc36a\xc8vi\xda\x04k\x1d\x91u\xc46\r^\xc9B\x949\xbf(\xa3y\xe8jYaD\x92o\\j\xe1\x87.\x95\xaaV+2-\xff\xb5\xfe\x00)\x82\xe6*b\x13'%\xf4[\xc90Vi\x900\f\xa5\x99\xb7U\x0f\xb6A\xe1\x81+I\xbe\xdc0,\u0448\xc4Y25t\u0552:K\xa2\x96\xc4YZY\x83\xb9\xb4\xc9|6D;\x8b\xe9d\x11\xed,\xa6\xed<\xf8g\xcex\x85Gx\xbd$\x1cL\xf5\x84\xf3\xa3\u0718\xb9t\xac\"\ub522\x82\xf2\xca\x112\xf5+\u0459\xc2B\x06\x13.\x80o\x0f\x87Z\x1d\xa6\xa9\x1fJ\x99\x1a\u0397z4\x01\x86\x87,:bI\xdb\xce\xfbT4(\xaas\x14\xba\x9d\x16\x99\x89\xb9\xe09\xff\x8e\xb81\u00ad\x9f{7\a\xf7n\xe3\x87\xdf\xfe\x12\xefz\xdb\f\x87\xfe\xeb\xefr\u077f\xb9\x8a\xa9\xc6\b\x92.\x90\xb9\xe3\xcf\xdc\xfe5\x03\xb9s\x8eN\xa7\uba55$\xa3\u0448i4\x1b\xfc\xf0\x87\x0f\xf1\xa1?\xfb\b_\xfc\xe2\x17I\x92.\xa7_p5W\xbe\xfc\xd7X\xbe\xf6,Z\xf3sE`H>0\xec\xe5\xbf\xf3hF\x9f\x16\x15\xd5b\x1a\xebB\x83\xf2\xfbT\x84\u0302\xb3\x105\x84x\xccb\x1af \xaf\x9e\u007f\x97\xab\xca\x19\xe9\x8dB+7\xcb\nc6\xa9\u020d\xf3\an\x053f\xbd\xc4u.C\x13\x87I\xfd\x02\x8es\x81\x8e\x14\x1f0\xddM\x13\u2265\xac9\xf3Bv<|\x1fY\x96\x95\x1d\x87\x88v:]\u067am\xdb\xe8\xd3\xf1\xfe\xfc\xd2/\xbd\xb9x\xb9\xde\xf6\xb6_K\xb7l\xd9$\xdf\xfc\xe6\xb7\xe2\x1bo\xfc\xac\x84%\x9e\x83\xc0\u00ea\xfa\xc9\xe9\xe9\x83c\xb7\xdcr\xeb\xe4\x9dw\xde%{\xf7\xee\xd5V\xab\x8ds\x8e\xf1\xf1Q\xb9\xe0\x82\v\xe5\xf2\u02df\xdd9\xff\xfc\v\x0f\x06\xea\x86\xf7\xbe\xf7\xf7\x00x\xd9\xcb^\x1a\xfd\xc6o\xfc\xba\xbb\xfa\xeak\u04b7\xbf\xfd\xb7\x9f\xfa\xb5\xf4T\xbe\xe9;\xdf\xf9\xf6\xea\xf7\xbc\xe7=\xdf\xfd\xfa\xd7o]\xdfj\xb5\x82\xbfGXo\n\v(\xcd\xe11\x9e\xf7s\xef\xe0\x82\xe7^O\xbb\xe5\xe9\x15c\x1cC\xa6\xcbx\xd4f2n1\x15\xcf3\x1e\xb7\x18\xb3\x1d\x9a&\xc5\x18_\x89\x1aQR1\x18\xeb\u0273\xcc\x18\xc4(F\x15\x9b\xf9\xe0X_X\x8a\aS\xa9\f^\xd4ar}uu\xa8\xaau\xa6\u064a\xab\x0f\x12\xf3!k\xc9\x0ec$\x0f}\xf6\x15\xba\xcd\xd70\x8b\xc8(\xadK\xb2\xd4oFV\x01\xbd\x9e}(\xc1\xb6\xd6\xf3\xf2\x8a\x1f\xe0v3K\xe2\":.\xa2\xed\x1a,\xa413\xe9\b\x87\x92Q\x8e$\u00f4\xb2\x06Y\x18\xe4\xaa\xfaX-\x11\x87\xad\xcc\x06t\xc0[YU\xee\xf7\xd6\xf4\xf9\xf3\x1a\x8a\rQ$\u0218\u016ci\u0090\xad\u0336\xf4(\x17J\xdd'\xa7\\\xf5\x0f\xfa \x11\xba\xad\x8c\u03be.\xe9B\xe6_\xd1\x04\xa4\xebB\x86\xa8?\x15\xa2\xb8\xc9Ps\x84\xed\x0f\xde\u02ad\x9f\u007f\x0f\xdb~\xe8#\xb2&W\x9c\xca/\\\xff\x1b\xbc\xe9\xba\xd7q\xe6\x86\t\u0439\x90\x1a#?A \xee/\x93$H\x0f\xfd\x8a\xbe\xf7$\a\xf8\xdf\xff\xfb\x16\xfe\xf4O?\xcc\x1dw\xdcA\xa39\u0139\x97\xbd\x8c\xcb^\xf4&\x16\xaf\xdaH{a\x864I\xcb\xf9\x8cJ\xb0G\xf6T_\xfe\x1e\xf8\u0160\xc8\xe7\xb7\u06a8\xef=T\xd5>\xabB\x17\x19\xd2\xd8+\xb8\x86'\"\x86\x975\xb09\xc52\xb0\x1b\xec\x8d%\xee'\xf7\xc2\xee>I\xbbE\x9a$a#\xdbk\xb6\x9d\xba\xc2\xd8G\x83\x8d\xb3.d\xb8\xd9\f\xd7\xf5\xd6\xd1Y\x9a\x87\x94\x85\x1fl-c\xcb\xc7\xd9\xf3\xd0W\xb9\xe9\x0f\xff3\xdd4\xa3\xd1h\xfa\x19M\x96\xb1x\xf1\x14\xe7\x9fw\xce?~\xe9\xef\xbf\xf4b\x80Gwlc\xfd\x86\x93\u007f,\xef\xe3\xddw\xdf)\x17\\p\xf1\x93.5>\xf8\xc1?\x91\v/<_\xaf\xb8\u2aa7\xed\xb1<\xe1\xca\xfc\x86\x1b>&\xd7_\xff\x8b\n\xf0\xb9\xcf}\xee?\xdfs\u03fd\xeb[\xadV\xb12\xae\x85\x1e\xd9\xe0\\\xc2I\u7f00\x93/x9I\xeah\xd2fj\xa8\xcd\xe2\xc6,K\x9b3LF-\x86l\x12\x86\x95.\x00k\xa9\xadQ#\x88U\xc4\xf8\xab\xd5\xe2\xbc;Cn\x8e\xdf\x13\u03e3\x02\x11\xce{P8oA\xa99\x8dS\xc4\xc7\xf5\xd8\xceV\xeb\x87\xf0W\x93[\u0245\x03B\xc2\u007f\x8bh\x1f4\xd6d\x81\x15\xfd\x9eV\xdd\xd2\\8\u07e8\xef\t\xe5\x81\u0222\x10\x89\xa3iRT\u06e1s\xf0|{\xa2\x1e\xd8\xe7\x92&3\xe90s\xe9\x10sY\x93\xb9\xb4I;k\xd0v1\x9d,&US\x1c\x80\xa6\x10\x97k\x11\x1a\xa1\x03\xd9K\x8a!T>\x84%QoY0\x94'\xa9K\xb1*\xcd1H\x1bz+\xf4\x9c2k\bQ\xd3\xe0:\x19i\nQ\xee@ \xe5\xe9\x99$\x1dP\xe5\xa4s\x9e\xcf\u02e6\x96s\xdb\xe7\xdf\xcb}\xdf\xfd\x1c\xd3{\xb7\xf0\x91?\xf8M\xb6\xfc\xf0>\xde\xfc\x1f~\x85\u02dfy6\xe3\xa3]\xa2(\xf5\xaf\u047f\xf2*]\x80,s\x85\x85m\xa7\xdbE\xc4099\u026e\u077b\xf9\xdc\xe7>\xcf'>\xf1)vl\xdb\xc6\u0112\x95\\z\xf5/p\xe1U\xbf@sd1\v\xb3Gp\x9a\x15\xf5o\xd9\fJmP\x98\x87q\x8b\x8d\x91\x82'\x1f !\xac\x8cR\xd4\b\xce\xfa\xfd\x83F\xc3\xd0\x18\xb3\u0626\xfch@N)f\x10c\xbc\xfcT\f\x98P\u03c7\xa7\xa2&l\x83\n\u0208/\b\x99\xf1\xdeDe&y\u0634\xce2\\\n\xe3+Nb\u046a\xb5\xec\u067e\x05\xd5\x06\x1a\xf6Ifff\x19\x19\x19=OU\x9f+\"\xb7\xed\u06ff\xff\xc7\x16r\xf5T\x80\x1c\xe0W\u007f\xf5mO\xfb\xe3yB`\xfe\x8ew\xbc\x9d\x1c\u023f\xf1\x8d[/|\xf7\xbb\xdf\xf3\xeb\xfb\x0f\x1c\xf0\xb8k\xad\x94\v\xbfB\x9a&,Zy:\x17_\xf5Z6,\x1fa4\xd9\u02b2\xb1y\x964f\x19\xb1\x1db\x93\x16tAUl/a\xffW\x83\x0e\xdaX-\x92uL\xa5as\x01\xd0\x05%B1\xeaC[\xadj\xa1V\x91Z\xb1\xec\xe5q`\u020aX\xaa\xd2\tP\x04\xacq~\x81\x06\x10\xe3j\x1b\xa5\xf5\x14\x94\x92{\xd7\xdeK\xba\a\xd9D\xf0{\xf0\xa6\xde\xd2JE>\xe6zt\x04&\x1cj\x16\xa1I\xc68m\x966\xfc\xefs\b\xa9\x8bh\xb9\x98\xf9\xb4\xc9L2\u0311d\x84\x83\xddQ\xa6\x93\x11f\xd2a\xba\xce\x06.\u0495@-=\x83\xd7\xea^Gx-\x9c\x06\xd7\xc5N\x99cU\x8clU\x06\xc8\u0434\xf4\xb3\xd1j\xd6G\xa9\u007f\x17\x03v\xc8`\xe6\x8dW\xa78\x17|B\xea`\x90\xa6]\x16\xe6\xa6Y\xb2\xfa\x19\xbc\xe8\xf5\u007f\xc8\u0112\xb5|\xff\x96\x8f\xd3i\xcd\xf1O_\xfc\b\x9b\x1f\xfa\x01\xaf\u007f\xfd\xaf\xf0\xaaW\xbc\x82\x8d\xa7\x8e\x11\xd9\x0e\xe0\x82g\u01bf\xcej\xbc\x9b\xa6t:\t\u076e7\xc3\x1a\x19\x1e%u)\xb7\xde\xf6\r>\xf5\xa9Os\xeb\u05ff\xce\xdc\xfc<\xabO>\x9b\x9f\xba\xf6\xdfs\xe6%/\x05c\x99_\x98\r\x87\xb4)\xfc{\xaa\U000196e0i\xd0u[\x1bC\u0413\xf7\x8f\xb4\xc3\x15]\xd9\x15r\xd6\a\x9a\x98Hh\x8cx\xae\\\xe4h\xbar\xa9\u02e6D\x06\xb6\xfcZQn\xe5v\xc99\xe5Y\xec^h\xe9\xcb+\xaa\x9e\xff\x1e\x01\xe3\xac\x1f\xce\xe7a(\xb9U\x84\x13\xba\xf3\x1d\xa2\xc6\x14K\u059d\u02ae-\x9bp\xea0j\x8a\x8e\xe3\xf1=\x8f\xa7\xb7|\xfd\x96\x04\u0ef7\xdfqB\f\u045f\x10\x98\xaf\\\xb9\xb2\xf8\xfb\x8d7\xfe\xcd\xff\xb8\xe7\xde\xfb\xe8\xb4\xdb\x18\x13\x891\xd6k=\xf1-N\xdc\x1c\xe6\xaa+\xae\u19df\xb9\x8e){?\xa3#s4M\xeaU&5\x11S\xd8N\xab,Gj\x0e\x80\xd6S\ty\xa5\xaa\x05\xf0\xf9*\xbdAF\xa4Y\xc1K\xe7\xa1\xce5hT\u03e7;\xf1\xc6R\xb9g\x9b\xa0\x18\xeb)\x1fc\xb4\xcf\x1d\xdfU\x00\\\x06\xd0\f2\x803\xa4F\xd8\xf4\xaf\xcfk\xb5\x1f\xc8}\xb0r\xbe>\x1f\xd4\xf6\x82l\xe0\xfb%\f\x1a\xad8\x1a\x921\x12uX\x1c\u03e1\u00de\xa2ie1G\x92\x11\x0et\xc69\xd4\x1d\xe5p2\xcaL2\u0302k\x92\xfa\x8d$\xff3\xf2\xdf]\t\xf3\xcc\xfd\xd4s\xcb\x01M\xfc\xf2\x86D\xd5\v\xa3W-S\xbe\x83Z\x1b\x8c\xf6v,\x826\xbdf]\xbb>\xcb\u0569\xab\xf8h\x97\xff\x9fe)\vsG\x18\x1a[\xc6U\xaf\xfc\u007fX\xb1\xfe\\\xbe\xfb\xe5?a\xcf\xf6{\xd9\xfa\xd0\x1d\xfc\xbf\xefy\x98\x1f\xdc\xf9\x1d\xae{\u00db\xb8\xe2\xd9\xe735\tQ\xdc\xc5e\u067f\x1aP/\xd4*\xdd\xc4\aF8%\x8ab\xe2\x86a\u04e6M|\xfe\xf3_\u099bnb\xeb\x96\xcd4\x1aM\u03be\xe8\x1a\x9e\xf9\xa2_d\xf5)\x97\xd0I\xbat\xdb\v!\u4efa8\x96'?yE\x94\xe6\xaa\x151\x9e\xfa\xb4qE\xef]\x16'\x94S\xa2R\x8d\x12xr\f4\x86\f\xf1D\xe4\xe7'z\xac\x81{\x8f4\xaa\xe7_\xb4RIh\uf234\x92\xf9\xe9\xfa\x0e\b\xdf6\x9aQ\xeb\xbfo&-\xae\xad\x00\xd7d\x9d.\xcd\xc6\x18\xe3\x8b\xd7x\u05be\x90\x05B\x96e\xb4;\xed\xc9\xfb\xee\xb9g)\xc0\xc2\xfc\xfc\t\x11?\xfb\x84\xc0\xfc-o\xf9e\x00>\xf5\xa9\x1b\x9e\xfd\x81\x0f\xfc\u0275\xd3G\x8e\x14Uy\u0357A3\xce\u06b8\x91\u05ff\xe8RN\x9f\xd8K\xb7\xbd\x00\xa2\x85\xf6\x1b\xb4\x00\xe5\xfc\xe22\x81\u05b0\xe2<?nK\xf5\xb4CH\x89\xfc\xe0\x93\x94X\x1d\x11\x19\u05b9P\xadKm\u04ed\xf8\xbe\xe2\x1a3E5\xefr\xb3\a\xa3\xbe\xfa69p+\xbdK8ZQn\xf4zO\u0531\u007f\xf0\x05^\r>.\x979Cwa*\xdby\xf9\x9c2\x1f\xd4\x16^'Z?-\x02m\xa4\xd4Z\x00\xac8\u01a3\x0e\x13Q\x9b\xb5\u00c7\u9e88\u0664\xc9t2\xc2\xc1d\x9c\xc3\xc9(\xd3\xc90\u04e9\xe7\xddS5\x98bh\xac^d\x92{\xe78\xc5%\x0e\xd7\xcdhD\xb6\xbc\xf1e\xf0-\x9c\x1bj\u55a6\xd5\n>\xb7\xa0qF\xc8b?0\xd5pP\x9az\xc0^e\x96\xa1\xb4\x17f\xb0\x8d\x06\xe7\\\xf1\x1a\x96o8\x9b\xef}\xf5\xc3<\xf0\xdd/\xd0^8\xc2\u035f\xff0\x0f>p\a?\xfd\u04ef\xe5U\xaf\xfc\xb7<\xe3\xec5L\x8cCd\x13\xd24)d\x8c\xff'\x02{1\xe4\xec\xa6d\x99\xa3\xd1h\x80\b\xbbw\xed\xe6K\xff\xf0e\xfe\xee\xef\xfe\x8e\xbb\u007f\xf0\x03\x92\xa4\u02d2\xe5k9\u7c97s\ue56fat\xf1:\xe6\x17\xe6H\xb3^gQ-=u\xb4\xea\xc1\x13\xb6\vm\xe4\xf5\xe4\x95\xd9F\xff\x9et\xa5\xbf\x14p\xd6x)b$4\xc6\"\xec\xb0=\x06\xbdBeVt\xb4\xc9\xdb\xd1.\x9c:\xfc\xe7y\xb9\xb9\xec\xd5I\xe9\x0e\x8a\x11\u0730A[\x02]\x17\x16\x87$\u0200S\";\xc5\xf8\xc4j\xaf3\x0f[\x98\xde\xee \u0262\xc8N\xce\xce\xcd]\x04\xdct\xe8\u0421\x13b\x15\xed\xb8`\xfe\xe8\xa3[e\xfd\xfaSTU\u01ef\xbf\xfe\xba\xdf\u06f1\xe3\u0471N\xa7\x83\xb1\x91\xe4\u0546\x15%IS\xc6\xc6\xc6\xf87/\xb8\x8asNZL\xb7;\x1f\xb8\xdc|PG\x9f\xc9S\xfe\x9e[\x93\xf9ac\x14\xacV\xd5W\xe5\xa2\x10k\x86\xa8\xd2$\xf5\x03H\xadT\xf6\xf4.\xd6\xe4K:e\xb5\xab\x95\u0163\xd4\x18\x1f\xcc\n\xc1Y\xd9W\xfa&\x1c2B\xa9\x98\xf5\xa0W\x17ZQY\u0411ZUr\x94J]\x06\xb2\x1b>\xb1\ajUj\xde=\xa3G\xd1\r\u0200\x9bD\xa9t-B\u04e44\x9b\tK\x9a\xf3\xacs\x87ie1sY\x93\xc3\xc9\x18\xfb:\x13\xec\xedLp\xb8;B'\x8b\x83\n\xcd\x0f\x9fr\x1c\xeev\x1c\xdar4\x86\xad\x9fW\x1c\xe5\xa8R\xf2<R)X\xfa\x1e\xcfb\x9cS\xba\n\xed\x8a\x0f\xbaV;\xea*\xf0V\x82\xb0\xb3\xa4K+KY\xb2\xee<\xaey\xfd\xffd\xfdYWp\xe7\xd7>\u03ae-w\xb1\xf9\xe1\xbb\xf8\x93-\xf7\xf3\x8d\u06fe\xc4+^\xfe:^\xfc\xe2\x97p\xc6\xe9+\x19\x1f\x8f\x81 }\r-\xf9\xff)\x1a\xf5\x1c\xc8U\x85F\xa3I7\xe9\xb2o\xdf\x01n\xbd\xf56>\xfb7\x9f\u5bbb\xee\xe4\xf0\xa1\x03\x8cM,\xe6\x94s.\xe7\xfc+_\xc3\xda3\x9f\rv\x88\xd9\xd9#%h\x86*E\\X\xe8\x91\xea\xec#\f\xf9\xc5`L\x8c\xb1q\t\xfeG\xad\xa9+\n\x94\xbc*\x0f\x16\xb7v\xd4\x06\xa7\u0363T\xe5\xc7RR\x95\u00e9Z\xdbVv\xa6%}Z\xfdr\xcde\xc5Z\xce{2\xf5\xb4+\r\x03\xad\xb0\xf2\x9f/\xe08\x05\xb5\x8cN\xae`dl\u048b,\xac\xb7\xad\xb5\xc6\xea\xbe}\xfb\u067d{\xd7Ya\x89'{\xfc\xf1\x9d\xb2r\xe5\u069f\xe8\xea\xfc\xb8\xe1\x14\x87\x0e\x1d\x92{\uee57ukW\xbf\xf5+_\xf9\xc77m\u0672\x05D4\x8a\xe2B\xfb\xe1\u0375\x84k.\xbf\x8c\xb7\xbc\u654c\fY\u04b4\x8b1\x1a\\XC\n\x8f)\xe5\u007f\x0e\xc1\x18%6\x19\xd6(\x1aU<J|\x19\x87qJ\xac\x19\rR\xa2\x8a*]\xfaj\xe9\n\x81\x93\x0fS\f!2\xcd\xff\xfe\xd4X\xba\x12\x05\u0661\xb7\xb5,@\xbf\xc7\xd5@\x82\xda%\a\xf7\x9a[a\x8fk\xa3\xf4\fc\a\u04075\x80\x97\x92 \xaf{\xb5\x14\x9f\xa3\x12\xa6[\xba\xddIe\u05f3\xe8^\xab\x19\x9cU\xffu|74d\x13&\xa26K\x1b\xb3\xac\x18\x9afys\x86\u0278\xe5\xb5\xf3\xea\xe5\x92Y\x98%d\xa2\xa4\x06\x1aC\x96\xe11\xbf\f5\xa8\x12\u02c1\xdci\x19D[\xb8_\x86\x8f\xd4\xc1\\\u2f27{\xaa\xd8\xc4\xdb\xff\xdaL\xa9\xe4\x83\x15\xaf\u007fm\x19)\xbc\x17Y\x92\x107\xc7X\xb3\xf1\"6\x9e\u007f\r#\u32d9=\xb4\x9b\xb9\xe9}\xec\u067d\x9d\u007f\xfe\xf6\u05f9\xe3_\xfe\x85\x83\x87Z4\x9aKh4\u0188\x1bC\xbej\u0574P8\xfd\xffm\u0765!(!_\xf7\x8e\xa2\x18E8tx\x9a\xcd[\xb6\xf0\xa5/}\x99\xf7\xbd\xef\x8f\xf9\xccg\xfe\x9a\a\ueed7\xcc\xc1\xba\xd3.\u44ab\xdf\xc0\xa5/|\x13K\u059dG\xe2\xa0\xd3i\xd7\xfd\xf4\xb52\xac4e\x88\x89\t\x96\u01de#o`\xa2\x86\xf7A\xe9\xd3\x1d\u02601\xb8_\x0e\x8a\fY\uc8c1\xecT\x84\x1d\xb3D\x12t\xe0\x03G\xdf\u04bf\"\xd4\x13\x01V\xa3(\xf3\xeb#\xcb|\u05a6Vh\x91\xdc\u05e4\xd2\xc1z\xe7^\xc59\x9f^\xe5:J\x9a(i\x16\xb6\x8dss\xaaF\x03\xb26{6\xdf\xc9t\x9e\r\n\x18+,,\xcc\xcb\xe2\u014b\xcds\x9f\xfb\x9co\xfd\xd1\x1f\xbd\u007f\xef\x99g\x9ein\xbe\xf9\xef\u007f\xa2\xc1\xfc\x98\x95\xf9=\xf7\xdee\xce?\xef\"\xa7\xaa+^\xfb\xda\u05fcv\xd3#\x8f\x00\xa4\x91\x8d\"#\xa5F4u\x8e\x95K\x97\xf2\xb3/|\x1eK\x17\x8d3\xbb\xd0\xc2\x19A\xa2\xfcb\xcc#\u01f4\xc8\xef\xb3\x01p\x8c\xd12q'\x84X\x9a\\?\x1bj\xbfc\xd7\x18T\x94$\xa59P\x0ejF\xb5\xa0\f\x9a\x95\x9b\xbc!i_\xcb_\xe4\x1d\xf6r\xe5\xa2\x03\xf4\xb3\x14\x1e1\u054d8\xe4\xe8\xecb\x1f\xb4\x84\xc7K\xdfQ\x12n^W\xf1\xcf\xea\xe9>\xd4T~\u007f@\x11\xa9\x01\nEt\x9e\x11e\xd4v\x18\xb1\x1dV4f8i\xe4\x00\xfb:\x13\xeciO\xf1x2\xc9\x117BK#T-MBu4 \x06(\u007f\\\xf9AX\x030\xf17g\xea`\xbe\xeb\x98K\x1c\x99z\xb32\x97o\xf0U\xed\x84+s\x93j\xadX0\xbb\xaat\x17\xe6\u0212\x06\xa3S\xeb\xb8\xfc\xe5\xbf\u0269\xe7]\xcd}\xdf\xfe,\x9b\xee\xfa*\xfbwm\xe2\aw\xde\xc2=w\u007f\x9b\x9bnz&\u03fd\xea%\\\xfa\xcc\xcb9\xed\xb4SY\xbf~\x19\x13c\x06c\xbb\x88\xa4\x15J@~L\x00^\x1a.\x19c\x18\x1a\x1a\x06,Y\x96\xb0\xe3\xb1\u01f8\xf7\x9e\xfb\xf9\xd6?\u007f\x87[o\xfb\x06[6oaf\xe60\xd6X\xd6m\xbc\x80\x8d\xe7^\xc9i\x97\xbc\x94\u0255\xa7\x93e\x8eVk\xde\a\xaaH\x91A\x86:\xa9\f7)\xe8\x95\"\xcd&O\f\n\x15y\xfd\xaa\xae+\xb1*\x11.\xbe\x832!\xe3\xd5\bi\x03h\n\xc3F\x8e\xbe\x96\xa6u\xb6\xbd\xa0\xe3BA\x93\xef\u007f(up\xef\xe3\r{7P\xb5\xfc.\r\xee\xa5\xdd\f:\xed\f\xd7J\x91\xd4\xfb\xf6D\x95\x94\xaf\xa4\xdbab\xd1*\x16\xafX\u03ceG\xeeF\x1b\xc38\x1cVD:\x9d\x0es\xf3\xf3g\xddx\xe3gO\x03\xee\u0773\xe7q\v?\xd9.\x11G\x05\xf3\xbb\xbe\xf7\x1d\xce?\u03db\xa0\xbf\xff}\u007f\xf4s\x0f=\xf4\xf0%\xd3G\xa6\xb1\x91\xb5\xd6V\xc7\x16\x8a5\xc2\xf3\x9eu1\x97\x9c{&I\x96\x90\xe2\u021a\x96\xc8xm\xb6\x84\x05\x1d\u07d3\xfbIvL\x1alUC\x85\x1b<\xbf\xab\xc0j\x8e\x9aI_\xb7\xe9\xf4\xd5x/\x05Q\xfaN\xf8'\x9aa\xaa\xd5v\x0f`\x0f\xfa|\xfd'j\xcd8_+\u0740\x1c\x85)\xd4c3\x88\xc7\xe9\xcfC\x84\x9b\x96R\xc1\xa2\a\x19\x14\x84\x9a\x83\xb7\xeb}\xd4\xf5\xd7+2)K\xe3Y\x16\xc7sl\x189\xc8\xe1t\x84=\xdd)v&\x8b8d\xa7\x10\x1d#u\x06\x1bUm\x05J\xb8uZ\x06S\xf7V\xa4\xdd\f\u6e8e\x85\u0115C\u047cC\xa2\xb6@\xd838\xa5\xb7\xe7.Z\x98,Ih\xa5G\x88\xa2\x06\xcb\xd7_\xc4\v6\x9c\u03f9\x97\xbf\x92\a\xef\xf8{\xb6\xdc\xfbuvm\xbd\x87{~\xf0M\xee\xf9\xc1wX\xb5\xfa$\u03bf\xf0r\xce;\xf7\x12\xce:\xf3,\xce:\xfbTN9e5##\x16#)\u05b8\xe0_\xdf;\xfdx\xe2CT\xe9y\u074d\x91\x90\x16\x1f\x85\u7532c\xc7c<\xf4\xf0#\xdcs\u03fd\xdc~\xc7\x1d|\xf7\xbbwp\xe0\xc0\x01\u04b4K\xdc\x18b\u0769\x17p\u0499?\xc5\x19\x17\xbe\x80\xa5\xeb\xcf!q\x86v\xbbE\x16\xf4\u05e5o\x8f\x94iV\xb9@\xc0\x14\xe1o\x14f\xc8&p\xe4b*Wf=.\xb1\x8f\xeeSBU.>\f\xdcB:li\xc6&\x80\x82\u053f\xa3G~:\x88:\xaf\u0280k\x15y\xf86c,N\xd2\xda04\xf7W\xd7\xca\xf6j\u6105\xaec\xae\xe3H\xda\x19\xcd\xc4\xd1\u0410tU\x19\u00a7i\u00a2\xa9\xa5,]\xb1\xbev\xb5\x87\x82\xdf\x1d>|\xc8l\u07fe\xfd,\x80\xb7\xbf\xfd\xb7\u007f\xe2w\x89\x8f\n\xe6\xf7\xdds\x8fg\xe9TW\xbe\xeeu\xbf\xf0\x86M\x0fo\xf2\a\xb9\x11\xbf\xb6\x1f\x02bS\xa7\x9c\xba~-/\xbf\xf6y\x8c\x8e\x8f0\xdf\xed\xfaTxc0\xa4Xu\x1e\xc8\xc9\x17\x19\x94H\x9d\xaf\xbe\xc3\xe2\x8f8\u05e3 \x19D\xcaI\xed\xfa*+\xf0\xc1\x15pu\xa2.(1Ye\xff\x91\x01\xb6\x9f\xfd\x9f\x91~8,\xe0\xfb\xa9\x9a\xb5>\xe9\xe6_\xeaC&=\xc6\xd7iX\xcb\x14W\xef\x12\xea\xc9Ee\x18\u0148m3\x1auX\u079c\xe1T\xb7\x97\x83,\xe2\x80]\xc6\xfet\x193\xf1\x04\xa9D\xde/#\xf0\x94N\x198Ps\xaa\xb4S\xc7\\W\xe9dZ?g\x8c\xa0qE\x02\xa7\xfd\xd4XM\u03a95c\x91B\xf5\x9a&]\xb2,\xc1\u0688\x95'\xff\x14\xabO\xbe\x84\xf3\x9e\xfdsl}\xe0\x9bl\xbe\xfbk<\xb6\xf9\xfb\xec\u067d\x99=\xbb7\xf3\xb5\xaf\xfe\rk\u05de\xca\xc6\xd3\xce\u1b33\xce\xe3\xb4\xd3O\xe3\xb4\xd3N\xe5\xe4\x93\u05b1b\xf9$\xe3c\x11qD!\xe9)\xaaU\xc9\x13e\xfay\xef\xf29\x19\xeaf\xa3\x8e\x03\a\x0e\xb0e\xcbV\x1e~x\x13\x0f\xfe\xf0!\x1ex\xf0\x87\xdc{\xef\xfd\xec\u07bd\x8b,\u02c8\xacedl\x82U'\x9d\xcb\xfa\xd3/c\xdd\xc6KY\xbc\xeat\xc4\xc6,\xccuH\xb3\xcc\x1fzZ\xd3y\x96\x9d\x9b\x04\xaf\xe1<K\x17A4\xb8\xf4\x05\xf3\xaczoS\xbf\x92u\xc0b\x99\xb7\xb6\xf5FZ*\x905\rCc\x11c\xb1\xc1\x86\u074a>\xab\xba|o\xa1\xd7\xee!7d\xab\xf8{\x9b\x9e\xd3Z\xfcp\xccW\x02N\xea\x93\xd0\u028f\xcaTi%\x8e\xd9VF\xe2\xbcL7\xf76\xd7\xfc\x1a\xcf\xd4o\x8a\x8a\"&f\xf1\u04b5\x8c\x8c\x8c\x91d)Q\x14\xf99\x9e\xb5\xb2g\xcf\xe3\xcc\xcf\xcf]\xa4\xaa\x93\"2}B\x82\xf9\xe6\a\xefd\xe3\xd9^\f\xffG\u007f\xf0{/\u007f\xf0\xfe\a.\x99\x9b\x9f\xc7\x18cLem\x1f\x81\xd8Z^\xf2\xfc+9\xfb\xec\xd3h\xa7]2\vj\fq\x90\x0fVR\x12|\xc6e\x05\xc8sS)\xe9\xc9\xc3\xe9\xa3\"jU\xb8\x1c\x15\t\xb5oPW\xd5>k\x1f\x84\xf4\xd6\u294b\x9c\x1es\u03a3\x95Fq\x90\x19m/\xe4\x1d\xebsOk\xe3_\xf0\xa9Z\x13\xc4\x14\x80\xdes\xf3\xe4\xaf|$\x8e\xa9x\x81)\xd3f\x9d\x1cb:y\x9c\xddf9\xbb\xa3\xe5\x1c1\x93$\xc4\xc1\xa3\xd9U\xba%\x0fz\x9dLYH\xfd\r\x98\xb8\xf2F\xae\xfe*'\x12\u031b\U0008391e\xd4R\x02\x98\xf6(\x93\xa4^\x14:%u\t\xe9\xcc462,]s6K\u05dc\xc5i\xe7_\xc3\xe3\x8f\xde\xc7\xceG\xfe\x85\xc7\x1e\xbe\x83\x03\x8foe\xe7\xceG\u0631\xfd\x01n\xfb\xfa\x17X\xb2l\x15\xabVm`\xf5\xeau\xacY\xb3\x9aSN\xde\xc0\xa9\x1b\u05f3j\xd5r\x96-]\xc6\xc4\xe4\x14C\xcd!\xe2\xc8\xd2lZ\x1a\x8d\xa8\x06\u058a\xd2\xedt\x99\x9d\x9d\xe5\xe0\xa1C\xec\xdf\u007f\x80\u077bw\xb3}\xdb\x0ev\xec\xd8\u03ae\xdd{\u063a4\xa7\n\x00\x00 \x00IDAT\xbe}\a;v<\u0291\xc3\a\x01\x88\x1bC\f\r\x8f0\xb9d5kO\xbf\x84u\x1b/e\xf9\u06b3\x18_\xb4\x864\x83v\xbb\x85s\xed\xf2\xc2\xcez{\xa0\xb0OQ\x84/\u7b85\x06\xa3>\xecA\x8c\xc5`\a\\Ez\x9c^\xd1'e\x15Uy\xc3\u0418\x88\x19i\x1a\x1a\x86b~Q\xab\xf0\x85\xbay\u0500\x8a\x9c\xaa\xc1\x9dT\xa6\xdc9\xa0\x1b\x1f%\xa9d\x15)n\u0391\xfb\xfflg0\x9fx\xa5U\x1e\xfc,\x15\xea\xd0\x05\xff\xfc\\\xff\x9e\xa4)K\x96\xaebjj)\xbb\xf7\xee&\x8a\xe2B\xf2\xdciw\xd8\xf9\u062eE\x0f<p\xdf(pb\x82\xf9\x1f\xbd\xff\xcf\xfc\ub9fa\xec\xf5\xaf\xf9\xb9_zx\u04e6\xd0R\x9aR\vm\x84$I\xb8\xe0\x19g\xf2\xfc\xe7=\x8bh\xb8\xc9\xec\xfc\x02\xc6X\xef`\x18\x8c8\\x\xb7\x8d\v\x1f\xd5w[{\xe5}\x955\x9d\x9a\x87\xb9<%\xd4\xd3\xda\u0630\xce\x16\u66d6\x1a8e\x82l\xce\xf4if\xfb\x97\x84\xfao\x1b)\x8e\x05\xcf\xf2\xe7\xbf\xd3T\xf4\x1f!7T\xa4/\x00\x8f\x1f\x85\x8e9\n=\xa3\x15\x11x\x11y\xaa\xbd\xa7\x99T\xdad\xdf^\x8fH\x87\x11\xf6\xb3\xa4{\x84\r\xe9n\xf6D\xcby\u052cd\x0f\x8bHL\x8c\xa8\u00d2\xe1\x1c\xb4\x12e.qt\xb3\xa0\x00\xaa{y\x15\xb0\xa4\x86\xb0\x02*a1\xacR\x98\xd5\\\xf4\x82\xb9Y\xcf\xeb\xa3Tl\x11\xf2\xf4\x9b\xd41\xd7=\x82\x901>\xb5\x8a\u0265\xeb9\xe9\xcc\u02d9\xbdb7\x87\x1e\xdf\u009em\xf7\xb0{\xfb=\x1c\u07bb\x8d\xb9\xf9i\xee\xbf\xffv\xee\xf9\xc17\x111\x8c\x8eN2\xb5h1\x93\x93\x13L\x8cO2>1\xc5\xc8\xc8\x18CCC\f\x0f7\x19\x1e\x8ei\u013e%hw\xba\xb4[m\xe6\x17\x16\x98\x99\x99\xe6\xf0\xe1ifff8r\xf80\a\x0f\x1c$I;\xa1\x03\x89\x19\x19\x1bg\xe9\xea\rL,Y\u0272\x93\xceb\xd5)\xe7\xb3x\u0169LL\xacfhh\x8a4I\x99_\x98\xf7^#b\xe8[\xe6\xd1r\x10\x9e\xf3\xc2B\x1e}\xe8kT\xa3\x12\x92\x82\x82\xb7\xbd\x1bTvT\x87\x90\xd2s\x9d\x86n\xcaz\x19\xb0\xb3\x06;\x161<j\x89\r\x03\xc9\x19\xed)D\xa4\xd2(\xeb\x00&T\xfb.1-\xa6QbL\xbd\xab\u00ebVP?8og\x1ab\xe0\fY\xa6\xc5,\xa8\xba\xbfP\xca\u05c5,I\x19\x1d\x9dbdd\x1cuY9\x93Q\u03db/Y\xba\xe49;w\xee<\x03\xd8}B\x82\xf9\x87\xff\xe2\x06\x05\xf8\xf8\x87\xde\xff\xb3\xf7\xdd\xff\xc0E\v\xadv%\x1d#\xb4[\xaa\x8c\f\r\xf1\xe2k\xae\xe0\xd4\xd3Nf\xb6\xdd%\x8d\x9aEE.\xb9\xfdN\x00q\xa9\x82\xcb\xc0\xab\xa0\xa2Q\t\x91f\x88<A\u042e\x03B\xee\u0218\x03\xa7\xe9\x19\xd6(\x90`\xc9B\x8c\x9b%\xf3v\x005S^z\xa6\xf2U\xc0\xaev\x98\xf9W\x94\xc0\x9d\x8fl{)\x19\x11*|{\xbd\t\ueb45\x06\xc5\x06\u0213\x04\xf5\xe2\x97:-\x8d\xb0\xb4\xda\u0754RN1AQ\x122\x15\x1b$,\xcb\x0e\xb3\xc8\u0370F\xf6\xf0\x98]\xc1\x0e\xbb\x9a=Lq$\x8bh'>X\xd89\xad\x9d\xb5\x03;\x19)\xa3O\xeb<uYa\x15\xaa\x8d\xda\u0560\x95\xe3\xbd\\\xac\xd2B\xf5\xe0_\xf3vk\x014\xc3F1KV\x9c\xc6\xe2\x95\x1b\xd9p\xd6\xe5t\u06f3\xcc\x1c\xdc\xc5\xc1\xc77sh\xcff\x0e\xef\xdd\xc6\xec\x91},\xcc\x1dazv\x96\xfd\a\xf6\x93t;\xb8,\xe1\xe8A\r\xbdG\xae\xc16\x87h4\x87\x18]\xba\x9c\xa1\xd1qF'\x97\xb0x\u0769,Zw*\xcbN>\x83\xc5\xcb\xd6\u04b0c\x183\x02\xa9\x90\u0336\x98\x999\x12\xb6\x1eM\xbd\tQ_\xfbK\xd1\u0494\x85\x8c\xe4\xe1\xaa\xc1,\xcb\xe2o\f\ttK\r1u\x90\xba\x8b~\xff\x15\xbc\x141\vC\xcfh\xc4\u041c\x88\x88\xad\x10\x99\x90\xe5J\x9f\xfbN%\x01\xb1'\xc36\xbf7\xf2\x9c\xdf\xda\x1c\xaaz\xb5W\x12w\x8cx\x0f\x19-\xfdvT\x95N\xeaH3\xafV\xc9[\x15\x13\xd4m\u04ab\xce\t\x85\xa1K\x1c\xcd\xc6\x18\xf1\xd0\b\xe41\x95\xe1\tdiF\x96ef\u07fe}\x8b9\x01\xfe\xf4\x81\xf9G>\xfc\xa7\xf2\xe6\xb7\xfc\x8a\xaaj\xf3-\xff\xe1\x8d\xffq\xeb\xb6G\x01\xb0\u0592+X\xac1t\xbb\t\x17^\xf4\f\xaey\u03a5\x18\x11:\xce\x12\x19\xa1\x11\xf4\xe0y\x80\xb1uZ;\xb2{\x17_T\xca1e1\xc8|\x92 \x9e\xff=\xc3\xd0\xc1\x1b\xdeG\xb8\x02\xa0\xb5OR\xe5\a\xb3\xb6\x10\xc3y_\x13\xd3\xf3\x95\xda\xc3<\xf6\x91%\xa1\xa2w\b]\xe2`1\x90\xd5\x06\xb7\xdeU\xc6\x06=\xbb\x1b \xab\xa4\u03d6T)Ez\x83\x8e<\x8e\xf3\xb9~0\x95\xb0*->\x86\xab\xfao\x96\xc2\xc9N\x8d\x9fi\x94\u06f4J\xa4\tK\xdda&\xb3Y\xd6\xcbn\xb6\xb2\x8c\xfb\xb2\x95<\xea\xa6\xe8h\x03+\xfe9\x17G\x9f\xd4+\xcd\x1c\xc4]8\xefr\x83\xaf\x1c\x99\xb52\xf8\x10\xad\xce?\xfdB\x99\u04de\xbc\x8c\x9e'\x9dyS\x11\xefS\x9fet\x16\xe6\xc0\x18\xa2x\x98\xf1\xc9q\u01a6V\xb3b\xc3\x05\xa0\t.Kh\xcf\x1fa\xe6\xe0.f\x0f\xef\xa55\u007f\x88\x85\xb9\xc3,\xcc\x1e\xa6\u04de'\xe9\xb6H\xba\x9d`\xd5\xec\x13\xb2\xc4\x1aL#\"j6i\f\x8d0<1\xc5\xc8\xe2e\x8c,Z\xc2\xd8\xe2%L.[\xc5\xf8\x92\x15\xc4\u00e3\xa8X$\x8a0\b\xe9L\x87\xee\x91\x16\xddVB\x9af%\u0329+\x02\u0335\xb7\v5R\xef\xf5\x82<\u0408\u0146\x94)\x0ft\xe1\xea\xecuZ\u8ece\xa4\xcfD_E\xc8bCf\r\u0450\xa1\xb9(&j\n6\xa75\xa8[\xd9\xf6\xa1\xb9\xd6\xff.}\x95\xbfV\x92\x8d\x06\\\x9ba\x05Z\xabn\xbd\b]\a\xad\xb44\xb6\u00c8\xb7\x98\u02028\"\xffy\xc1\x8c&\x97j\xaa\u02c8\x1ac\u010d\xd1zL\x1b\xbe\xf8\u073b\xf7q\xee\xbf\xff\xbe\xe4\x84\x04\xf37\xbf\xe5W\x14\xe0\xbd\xef~\xe7\xd9\u07fd\xe3{g/\xb4Z\x1e\xccM\t-\xce)\xe3c#<\xff\x8aK\u0670~-\xfb\xe7\x12\x8c14Ih\x90\x16~)\xb5\xe4\x13\xd5\xfa\u025a\v\xb2\xf3\x05\b\xe9+\x04\x8e\tR\xdaC\xa5\xa4\x18\xba\x1a\xd1\x15KLV,\x03U\u056b\xd6\xd7@\b\x10\x91\xfa\xea\xbdpI\xac\xfb\x0f\xea\x00~{\xf0c\xf2\xdfms\x97\xa0\xa2\xa1\x84|\xe3\xd5\xd4l\xbd$\xac\xda\x1c\x9bb\xe9\r\x0e\xd0\x01\xdc\xfc\x13\x16\xdb\xe5\xb3\x06\x13\xd2\xd2+\u06e7\xb9'Mi;\xa0\xb5\xda\u0285\x1e\xcbi\xc6X6\xcb9\u0332\x81\xddl\x91\xe5\xfcPV\xf3\x98.f\x8e!\x9f\xfc\xa4Y9\xbb\xec\rL\x92j\x958\xe0Y\xea`2\xab\x8c\x9c\xd4\x1e\x85F\xf8y\xe1\u07camT\u007f\x81\x92t\xdat\xdb\xfe\xf7Yk\xbdEj\x1c3\xbax\x94\xd1EkX\x1dN\a\t\x8f\xcb9\xc5e)Y\xda%s\xa9/\x88\u0160V\x90\xe1\x88hx\x88\xa8a!\xb6d\xc6'*EQ\xa9\xc5vY\x86f\x19Y\xda&I3\x9cq\u0210b3AS!u\x8afZ\fX=X\a\xf0\n\x87h\xe9l,\x15P\xb3X\x13\x87\xcc\xce*\xb0J\x9f\xda\xe8h\u05d4V\xba\x1cg\x84\xcc\nQShN\xc5D#\x16#\x14`\x9eW\u0245+\xa8\xf6\xf0\xe3U\xfd~\xcf$\\z\x93+zd\x04B\x99PU}/3\xf5@\x9e\vy\\\x00s#\x82\xc9*\u0770\n5\a\x8e\xd0\xf1;bT\"\u007f\x8dT\x8bG\x81\xf9\xf9y\xf6\xed\xdb\xcf\t\a\xe6\x9f\xfa\xd4'y\xc3\x1b\xae\x03`\xdf\xfeCo\u07f9k\x0f\x99sXk\xc3p#7\xd3J9\xf7\u0333\xb8\xe6\x8aK\xe8\xa6\xfev\x1f\r@nU}U^\xbb\x93\xfbU)\x1a\x8c\xf0\x9d\x19\xdc\xdc\x1e\xab\x12\xad\x02\x9c\v@\x9e\xe1}\x1c\"\xf5qryU<\b`\a\xc1q\x17S9\x00\\52\xf9\xa8\xdd@\xfe\x15&\xa8eJ\xbe\xbc\x94n\xda\xf0Y\xa5\xee\x8dq\xac\x01\u0560\xdf*\xa1\xf3\xc8B\x0f!\xc1\xa3f\xb0\xf9\xed\x80?\x86P\x05k\x1f\x87Z\x85J-8o_5\xbb\xa0\xf9\xd5J\x8b=)m.\xe6QNc/[X\xce&V\xb2C\x970-\xc3\x18\xbcZ\xa96\u0516\xea{\x96k\u0635\xae\x8d\xcf\x1f\x912\x80n\xa1\xd8\x1bp\x85\x9e\u065fD\xe2\xfc\x01\xed\r\xd5\xf2MBW\xbe\x8eNq\x9a\xe0\\\x82K\xc2s\x0e\x89\xee\xd6HI\xbd\t~\xf1&\x1e\xf6[\u00a2\xb8,|}*\xe8\x02tZ\t4\x12\x88\xc0\x841o\xea\x1c\xear%\x87\x16^1j\x05F-\x91\xf5\x1d\x86\x11p]G\x9a\xfa\x9f\xab\xf9i*ZZ4Ky\xb5J\x11\xb5fJ\x1f\xf2Z\xf4\x9f\x1eC\u07a4\x03\x8a\x1e\xff\xebR\v\xb6!4\xc6-f\xcc\xebD\x82\x1d\x12=\xfbp\xa5\xc3\xf31\xab+\xad_\xd7rl\u05af\xa6p\x14A\x9d\x1f\xa2w\xb3\u04b7\u0204=\nu`\\\t\xfe\xf9\x82b!\xcc\x14\x83:o\xbcU\xdd\xf8-\xe7EB\x969\x92$=\xf1\xc0<\a\xf2n\xb7\xb5\xf2\x85/|\xd1U\v\v\v\x9e\xc26\"\xa5\xb63crb\x8c\xe7_y)\xeb\u05eeb\xba\xd5a\x98\x94\u0607\xa4\x15Ugq\xba\x17\xc5q\xb8\tM~sW\xe4jO`\x188\xc8\xcaU+cF!\u00c8\x90\x15\xd3\xfdrE\xc2\xf4\xd4\a\xf9\x1a\xba\xf6\x1aF\r\xf0_9\xba\u0250\xf4\x18\x87U\xb74\xb5\xa7&\xa9\xd7\xd7R\xfb}G;&\x06\xcf\x04<\xa5\xe3\x87a\x16\r\x1d\xc1\x13\x1c\x9e\xf6\xe53\xd6o\x00\x97?\xa2\xf0\xc68u=L\xb2G\xda|\xc0;N\x87\v\xe41Nc\x1f\x8f\xcab6\xebr\xb6\xb1\x8c\x83\x8c\u1430\xb5\xab\xc5T\xa0\\t:\u01aax\xcd\u0295\x92N\b\ufa69\xe4\xa9J\xde\xd9i\xd9z\xbbJ&i^i\xbaj>\xa67\xf6\xf0\xfbiR/2\x8b\xdf\x19\xac\x94]\xde\xdew\x02\xfdg\x05\xed(f\u0220\x8d\x88,\xb6%\x90\xe7\x86i\xb9\xd41\u007fZC\x06\x91\x88H|\xe1\"\x89\xa0]G\x96iPrI)I\fT\x8b\x02V\xbc$\u03c6\xf7z\xe0\xcaE\x0f\xf8\xd6\x05\x05\xfd.Bi\bgn\x8cZ\xcch\x14\xf6;\x14c\xfd\xe1\xe1(3l\xa5\x17\x82\aJ\xcc\xfa\x03(\x8ev\xe7\x94~\xa3\xf5qY\xa6\xd0I=\xfdc\xaa\xc3U\xc1W\xe5\x15*\xa7W\xb5\x9c?\xc6j\u982a\xb5\xd7 \xcb2\xba\xdd\u0389\x05\xe6_\xfb\xdaWy\xc1\v\xae\x05\xe0\x03\x1f\xf8\x93\x9f\u06fcy\xcbD\xab\xdd\xc6Z#\xa5\x1cQ\u025c\xe3\u0513\xd6p\xcd\x15\x97x\xbe\xca%\x05\xdf\\\xb1\xaa\xa6w\x9b\xba\xba\xf4p\xd4\xd3Z\xfb\xa9\x96\xa3\xa1\x92\x14f\\\x8a\xadU\aYm\x01Hj\xff&\x94A\xf5R\x8c-%\xf0\xe7\x1c\x0fd\x8eR\xf5T\x0f\n\x83\x1bx\xf0\xc8\xc0q\xef\x13\u05ea\xfb\x0e\xc0\xd1\b\x9d@\xb9\xb2e\xca\x03\xf4GT\xc3\x14\xac\xc51\xd46\xd5\xe5\xa9\xfc&\x1a\xa1\xcbY\xec\xe6$9\xc0>&\xd8\xc626\xb1\x82\xdd:EBDD\x86\x13Wn/\xf6\r\x97{\xdfs\xad\xad\xc5he\xde\"aP\x9aW\xebN\r\xc6e\x85iZ\xd1\f\xaa\x92U\xf0E2-\xbeW\xaby\xabT6X\x1d\xe5\x82VX\x1d\xaf\xea\xe3\x9d\xf3\x9f\x88\x1a\x82f\x12\x12\x90\xca\x1fT|\xbd\xd6\xdfmi\b:i\xb1\r\xc1\xb4\x14\x8d\x1cY'C\xb3\xde!q(\xe1\xadE\xd5\xfa\u047d5\x85\x84\xb3\xda9\xf5]\x1c\x85LE\xfa.YU\xbc\xb5\xed\x90!\x1e\x8f0\xe3\x11&\xf2\a\\^\xfa\xb8^)kM\xf6$\xf4\xa6Ni/\xf1%\xbdc\xd3\xd2\ufa38\xb6D+\xc0\xeb?:\x99zI\xab\x8a\x1f\xc2W\xc0>\xe7\xcb{'W*\x82q\x14IK\xfe\xec\x1d@A\x1a\xc19\xf5A\x1e'\x12\x98\xe7@\x0ep\xf7\xddw\xbf<I\xba\x11\xa1\x95)NQ\xe7h6c\xae\xb8\xe4\\\u05af^J\x96t=?.=\x9b\x93=\xc3\x1c\xe5\tJ\v\x9f0\x12i%\xb2\xd0\x15\xaa\x06\x1b\xfe\xe6z\xb7\xd7\xfa\x86\x8d\xbd\x8a\x95\xfa\xb0P\xf3)z\x1fD\xeaQ\xf9\xfb\xaa;I\xef\xe8g\x10\x90?\x19\xe0\x95>@w=u\xfe\x8f\xcf\xe1S\x8e\t\xea\x9e\xffw\xc1\xad\xa9!)'q\x805r\x98\xb3\xd8\xcdfV\xf0CV\xb1\x87I\xbab\xc1@S\xd2\u00b7\x9d\x819IZToZM\xf2\x93\xbaJ\xdf \x181\x1e\x8d4D\t\x86.E\xb5b\xbd\xaa\xe5 Zz\xd0H\xa4N\x03i1\xa9\xf5\xc3\xe2\xda\x01\x13\x02ql\xe4\xaf!\x97\xa9\xa7\x00l.\xbf\u0492\xcf\x12WZ\xb4\u57cf\f\x8c\xfa\xb9E,\x8aSC')\xb70\xfc\xf4\u0440\xf5\xb4J\xbe0=h\x06Y\xbamV\x1e\x9f\xe4\x8b5\x15\\\x95\x12\xdf\u0350\x10OX\xccT\x04\xc1#\xdf\x1a_\r\xbbL\xc3\xfc@\n\xb37=j\x9aEO)S\xbc\x9cR(\u0574\x06\xe8\x15\xe1\x83+\xfd\xcd\xf3\x85\xc3v\xea\x818\u03fa\xcd\v4\u3d28\xcc\xcb\x01\xb9T\xde3\u03df#~\x86\xe7\xdc\x13\x13J\x9cP\x03\u043d{w\xaf\xbb\xe6\x9a\x17\x9e|\xf0\xe0!O\xb1\x88H^\x95\xbb\u0331~\xfdj^\xf2\xbcg\u04b0\x86\xb9N\xb7\x06\xe4\xbdn\x88G\x83\xab'\x10K|||\xaf-\t\xd6o\xd5^H5}U\u07e0){\xce\xc3\x1e\x8dq<\xca\x05\"\xe5\xf0\xae\x94n\x1d\xfd\xfb\xe4\x18\xc7\u00d3;\xef\xb4\xe6\xb5\xf1\xe3\x02\xf2\xe3\x01:\xa1S(\x80\x03\xbf0\xb6\x86#,\x939\xced7\xdb\xec2\x1e\x8eW\xb2?\x9d IlH+\xcfz\x16e\xfa_#Wl\x83j\x9d&\ntJ\xae\xc216\xf6\xab\xe2\xaeLF\xf7\x9c\xbf\xab\r\u07b5\x8aR=\xd9\u01c5\t\\@\xedb\xd2R\xb1\x18\xf0\u0671\xa6\xb4X\x90\xd2\x18>7\x8e\xd2\xde\"\"\xb7f%$h\x8d\b\u0608\x86q\xd0vtSj\x9d\xa3*H\xe6\x02p\x95\xa7\x99\f\xaa\x9b\xfb\xf60*:\xfe\xd0\x01;\x01\xd304\xa6\"\u0322\x18m\x94\x12!U\x0f\xa8\x05\u0154SX\"\xd8 U\xad\xf2\xe5\xf9 \xba\x06\xd0E\xaeD\xa9\xae\xa9\x96Py\xc5o\xf2\xb9G\xe8\x96R\xa0\x9d:\x12\xa7\xc5<$\xa7]\x14\xc1\xa6\x0e\xeb\xf2\x83\xd8o5\x9b\xe0GcB\xf9\xae\x94[\x1cZ\x9f\x0e\xf4w\xfc'\n\x98\xdfy\xe7\xf7\xb8\xf8\xe2K\x01\xf8\xf4\xa7\xff\xd7\xcb\xe6\xe7\xe7\u05e5i\x8a\x91\"\xaf\x06uJ\x14E\\\xfc\x8c\xd3\u0638a\rY\x96\xf9\xb0\x01\xaaaaz\\\x84>\x9a\xdb\u02a0RU\x9e \xd8\xf4%\x81\x0fPz\xc8q`\xb3\x1aKE/\x1f\x9fK\xf7\xd0J\xa8CUc\xab\xc1\x97\x9dB\xae\xd5ov4\xb8B\u007f\xd2M\xc9q\xe6\n\xfa4\xfc\xcc'W\xa1SL\xcb\xf2\xae\xc7\xf9\t\x06\x06\xc7R\xe6Xd\xe69ed\x1f[\xcdr\xb6\xe8\n\xf6\xbbq\xda.\xc6\xc7g\xb8\xa3\xf4?A\xf7\xa3e\x17&Z\xe5\xfd\xb5R\xd6z\xf9\x9eD\x06\xa31\xaa.|(\xea\xb2|\xc70\xd0$\x01\xb0\xabUs1q\xd5\u06ba\x8d\xa1\n\xfcBd\xfc\xd0TB\xe0I\xf1\xb5\xd2S\xa9R\x82{\xfd\x00\x0f\xb2\x91\x91\b\x89\x95\u01bc\u00f4\x1c\u074e\x06P\xcd/.\t\x83\xe0 &\xb0G\xa9\x8e+\x06s\x85\xc2\xc5J\xe1u\xae\x026\x16\x1a\xe3\x11vq\x03\x86\xbc\x99]\xeec\x8f\xfa\xeb62\x82-\xb67\u00dd\xac\xa1\xeb\t|\xa9Sj\x96\x0eE'\x13T8\x92\x1fD\x94A%U;\u072a\xa7\x8a\"\xdeD+\v\x11\x8f\x15jJ\xc5\xcf+l\xe2\x15q*\xf9\xe6x\xa0\\\x8c\x0f\x06\xafJ\t\x9cs\x14\x9cU\x8f\xd8\xc6\x1aK\u0708O\x1c0\u03c1\x1c`\u01ce\x1d\xcf\x1111\xd0\x11#\xcd\xfc\xc6r\xea\x18\x1d\x1e\xe6\x99\x17\x9c\xc5\xc8P\x83v7\xa9\xe7\a\xa2\xc7<\x05\xf5I\xa0\u0493\x01!\ud66bs\x94\x15\xfbz\x04\x9c\xd6\xf3@\xc5`m\x8c\x8db\xd4Z\u007f\xc3\xe6\n\t\x97\x91e.\xe8\xeb\xfd\r\x1eE\x9682a\x131#I\x13\xb2\xa4\xeb\xdd\xee\xe85\xa8\xd2\xe3\xd6\xd0Oy1\xe88\xac\xbe\xf6\t\x1c\x9f\x9eJ]\x8f\xfa\xde\xf5\x1fay\u03f4\xc4\xcc35\xbc\x83S\xe2\xfdlm,g\xd3\xec*\x0e\u034f\x92\xba\x88X\xb2\x01A\bU\xa9\xa2T\xdc\xfa\xaa\U000b6cbc.L#\xf2\xb4\x1d|0\xb5\x1a\x87\xe2\xc1]\x8c\v\xc3M\x17\xec\x85u\xc0u+}v\xc2\x1a\xacQ\xac\t\x8bs\"X\xe3\xd7\xe9\u0542f\x0e\u056c\xa0A\\\x95\xc8\x13jFi\x84\x8a\xdb4\r\x1a\x19\x88\x1d:\x9f\xe1\xda>\xd72\x1f\x14kF\xcd`\xabj\u00ec\x15]zAg\x86\xf0\x12\r.\x95*\u07b1\xb21\x12\xa8\x95\xe1\xf0\xaa\x99\xca\xd05,\xa2J\x1e~R\xe4\xf8\xfa\u1dc4\xa1\x83J\x91RW\xd0R\xd2+=\xad\xddo\x95\xc0\x89\n\xaek\xb8\x17\x12\f\v\xa9w\xd8\xcc\xd7\xea\xf2 \x18\xf0rD*4\xb78\xf1\xea\x9d<0]\xa4\xa6\xfc\u0272\x94,K\xfb:e\x14\x8c54N$0\u07fau\xb3\x9cr\xcaFU\xd5E/z\u044b\xd6\xed\u06b5\u02ff\xa7\x95\x8aC\x15\x16OMp\xc6\xc6\xf5\x18\x1b\x93d\u0770\xf03\b6\x8e\x0f\xe0G\x03\x86\x1f\x1d\u0334\xe6\xb7Ru\x13\xf1'\xb5?\xad\x8d5DQ\x8c\xb1\x11\x9dn\xc2\xdeC3\xec;x\x84#\xd33\xcc\xcc\xce177\xcf\xfcB\x9b\x85v\x9bN'\xf1\x12M\x11\x8c\x11\x86\x87\x1aX#\u0111e\u0572\u015c\xb4n\r\xa7lX\xcd\xe2\xc9q\xb2$!M|\xa4Y\xear\xdd\xeb\xb1\x0f\xa2\xbal\xf1\xe9\xeb\t\x95r'O\x8a[\xe9\xe9\x01u}\x92\a\x80\vJ\x94%\xf1\x1c\x8b\xc6\xe7Y\x1b\x1fbs\xb4\x82ms\xcb8\xd2\x1d\xf1\x8e\x8e\x15W\xcbz\x180T\xa7\xa0J\xff\xa0</\x12\xfb\x1e\x9b\bF\x82\xf9\x12\n6\x97\x0f*\xaaY\x00\xfa\\1S\xfa\xad\xd7|Hr:'\xaf\u028dE\xa2\x18i4\x10\xabh\x9a\"\x89\x16\xdd@_R\xf2\x806P\x03\xdfnF\rqC0-Cw!#\xed\xf8\u007fs\x86RSm(*\xd4\xe2\x8f\xf1&Y\u0396\ubd5a[\x17\x1b\x0f\xe4\xcd!C4\x19!\xa3\xb6b\xb5\xe0\x03`Ll\x8a\b:\xa5\xaa\u007f\x0f3T\xa7%HW<\x91\u051f\x85d\xc1\xee\xd8U\xc1<T\u84bf\x8f\xe2A$2\xa5\n)Ue!\x81$\x1cj\x1a\xba\x03\t\xd6\u02a0H\xea\xe7\x11\xfe\xd0\xf1\x15z>\x9d\x17\xcdu\xf9\xf9\x90\u04d0t[\xa4\xddN\xe9\xe3RI%n\xc41\xa3\xa3\xa3'\x0e\x98\u007f\xe7;\xdf\x05`\xf3\xe6M\xe7\x1c:th}\xbb\xdd\xc6\x18cD\xeaV}g\x9e\xba\x9e\x95\u02d7\x929\x17\xde\u0132*\x10\xd1\xe3\x80\xf8`A\u078f\x02\u40f2~\xf2P4W\xf9\x00\xc1F\x96(\x8a\x88\xacg\xd1\xf7\x1d<\xcc#[\x1e\u246d\x8f\xf2\u062e\xc7\xd9\xfd\xf8~\x0e\x1c>\xc2\xfc\xdc\x02s\xf3-f\xe6\xe6\x98_h\xe1\xb2\uc60fa|l\x94\x95\u02d7\xb2v\xf5rN;e\x03\x97\x9e\u007f\x16\x97]|.\x8b\xa6&1Y\niB\xb7\xdb\r9\xa9\f\xf0y\x91\x01\xa0\xaeO\x19p\xab\xf3\x02W1\x1b+f\a\x05%\xf2\xa3u\x02\xf2\x14\xe8\xc8\xfc\xbd0(\xab\xe2#,Y4\u01c6\xe6\x016\u03ef`{k)s\xc9P\xe0\xa5]9H\x13\x1d\x9cC\xa9\xd4\U000a628d]\xa9S%9x\x16\xda\x18\x01c\xf3OG~\a@\u0577\xf7\xf9\x81\xe1\xb4F\x91\xe4\xbc-\xc6gk\x9a8\xc2\xc41\xa6\u0440\x18\xe8&\x1e\xc8]\xeaW\xca+A)\xc5;*u7I\xadX\xdcJl\xb0\x9104dH\xdb\x0e\xed(\x9a\b\xa6k\x82[b}8_X\v\x9bJi,e\xc5j\x8d0\xdc\xf49\x9ef<\xf2\x86]\xf9\xb0\x90R6\\\x04\x82x\xb5fy\xd0T\xa8DU%\r\xbe)i\x00\xfa,\xfc\xb7+R\x81\xca*\xdd\u4389\x15\xf9\xa0\x15\xc1\x86\xa7\x91*\xdes\xa5\xe2K\xae\x95\xca\x1d\x85(\xf5\x03\u043c\v\xab\x9a\xf2\xf5\xe8\x9d1b\xe8\xb6\xe7\xe9v\x17JW\u01fcCSel|\x9cU\xabV\x9b\x13\x883\xff\xbe\x00z\xf3\xcd7\xaf\x9c\x99\x99^\x1c^\x8cZ\xf3\x1cG\x96\x8b\xce=\x83\xa9\xc9\t\xda\u0764\xa2\x1a\xad@\xb4\x0e\x8eR\xeb\xd7w<\xb5\xe1\xdf\xf1i\x96R.\x97/\xd84bK\xb3\xd9\xf4i/G\xa6\xb9\xff\x81M\xdc\xfe\xfd{xx\xf3v\xf6\xec\xdd\u03c1\x83\x87i\xb5;\x81:\x89\xe9&yjzV\x02p~\xe1U\u03efp\xa1\xcf\xce\xcd3;7\xcf#[wp\xeb?\u007f\x8f\xcfNNp\xea\xc9\xeb\xb9\xf8\x82gp\xcds~\x8a\xf3\xce<\x99\xb1\xd1Q\xc82\xda\xed6Y\x96URu\xf2g`zh\xa1~\xc5\u0353\xedN\\\x10\x8c\xd6\x01^C;[7Q2uB\xe3G\xe2\xec\x8f\xfd\xb5\x95\x90k\x11\xaf~\x199\xc8\xf2\xe6,\x1b\xda\ayxv\x15\x8f\xb6\x96\xd0u\x91W\xe8\x19W\xb1U8\x96\x8c\xb1\xfeI\x95\x9a\u0322\xb6\xd2^\xd2\xcd\xc1#Dm\x19,\x9e\x9f \x01$\xf3\xed\xd0B\x06n\r&\xb2\xd8\xc8bl\xe4\x95'\x91\xc1:\u023a]\u0124\xe1,\xa8\x9a\xbbU\xcaE\xca\u02bd\xd8\x00\xd5\xd2\xfa\xd64\f\x8d\xa6\xf5\xdcp\"\x98\xc4\x12\xa5\x11\x92\x19\xd2\xc4y\u0146\x84\xf0e*\x19\x00A\xf6\x87\b6\x12\x1aV\xfcf\xe7d\x84\xc4R\x94\xcfN)\xf6+\xea\x81\xe3!\x99\xcb)\x99\x83,\xf5\xa9>\x9d\xf0\xff\x89\u02fd#\x01+Hd\xfa\u0788\xbe,#-\xd55\xce\xf9Ak\xc1<I%*\xaeJs\x8b@\u2434j\xa5+\x05\x90\x17\x18\xe3*\xf4\x15B\xbb5K\xd2Y\xc0\x88\xad\xf9\xd5(\xca\xd4\xd4$'\x9f\xbcAO\x180\xefv\xbb\x02\xb0s\xe7\xce\U000ccc63@*\"Q)&p,^\xb4\x88s\xcf8\x85\x91\xa1\x06\xb3\v\xed\x1e\x05\xaa\xf6\xf0\xd6eK\x97\xa7\x87\xd7(\x9b\xa7\xa9\"\xaf\xc3D\x9e\xf5i\xbc\x1b\xa2\x8d\x18\x1d\x1e\xc5\xe0x|\xef~\xbe\xf9\u077b\xf8\xc7[\xbe\xc5#[vp\xe0\xe0a\x924%\xb2\x968\x8e\x18j6\x18\x1f\x1b\xf5\x96\xae\x87;\xa4iZi\xb3K\xe06\xc6\x10\xc7\xcd\x10,\x9bAeu8O\x05?2=\u00ddw\xdf\u03ddw\xdf\xcf\xdf\xde\xf4U.:\xefL~\xfeg\xae\xe1Y\x17\x9d\u0352\xa9q\xba\xdd.\xadv\xa72@\xd3\x1a\xad\xa0=\xc0\xf7T\xabg\xad\x1cn\xb9\xd9X\xfe;\\\xefZ|\xb8\xc5\u034fH\xf1\x1c\u007fHZr\xae\xf9\xbaA\x860d\x13N\x1d\xdd\u02f2\xc6\f\xdb\x16\x96\xb1ua\x05\xfb:\x93t]\xe4\xad\x12\xc41\xd8f\x98\xbeW\xabp]\xad\xccF\xa4\xa7\xfb)V\xc5T\x8aE,\xe9\xf1j/\x0fo\xa9\x03\x94\x94<r\xfe5\xc6Z\xef\xf2Wx\xa3keyE\xeb!\u0185[\xa4\xabk\x0fs\xbe\xda\x18L\xc3`\x87-Q\xd4 6C0\xaf\xa4\xb3)\u074e#\xcd\xc1?-\xb7@\v\x8aI\x15\x9b\x81\xcd\u007fm'\x14#\xd6[f8z\x17v\xbc\xa4/sy\xf5\xedc\xfe\u04ae\u00e5\xae\x88\a,\x14=\xb9|P\x14\xb1\xd2\xfb\xd0\xcb\xf7\xa6\u2a18'u\x19)m\xb7L\xe82\xb2BLZ\xbe\xf2\x92*$\x1a\xf2N\xb5\xa4XDzb#\xc3\xebe#\xe6f\xf6\xd1^8\x82\x89\xa2\xb0|\xe4_\x80F\xdc`~~~\xcb\xc4\xc4\xf8\x96\x13\x06\xcc7mzD\x00\xbe\xf9\xcdo\xd9\xfd\xfb\xf7\a\xfc\xd2\xe2buN9c\xe3z\u05af]E\x92\xf9\xb5\xe9B\xc1\xa2=&Z=\b\xad\x9800\x19\xec\xcb!O\x114\xaa?\xa1\xa4T\f\xa9\x13L\u0720\u0648\u0679{\x0f\xb7}\xeb{\xdc\xfc\x95[xh\xd3\x16\u069dnQ\x01\x8c\f\x0f\x95\xa9\u9744n2M\x969:\x9d\xa3o\x8by\xd3{C\x96e\xa8s\xc4Q\xeceVY\xea\x97I\"\x83s\xae\xf88|\xf8\b\xb7|\xe3vn\xf9\xc6\xed\\\xf1\xcc\vx\u00eb_\xca\u0557_\xc8\xe8\xe8(\xf3\xf3\v\xf5j\xb8\xd0E\x97+OR\xd3u\xe8\x93\x00r)4\xe9Z\xacs\xb9\xa3P:\x14F`\x90=-C\u0601\xfd\u0660\xac\x11\xa9\x87s/j\xcc3\x16w\xd80r\x90G\x17\x96\xb2ua9\xfb;\xe3\xb4]\x84\xc1a\xc5\r\xbcbt\xa0\x1d\x1a\x83\xe5\xb1\x15]\xb4\f\x98\xdf\xf6=\u007f-\xd7\xeds\xa9\x9d\xe4<o%\xc3\x14\xa9\x1f\xfc9\xa0\xd7\x1f\x8eT\x853\xa4\x81{\xb6&\xf0\xca&\xf0\xd9\x02\xd6\x1a\xe2\xe1\x06\xf1P\x8c\x1b\x03\x9d\x880\xb3)\xeeH\x02\xad<\x0e\xb1\x10\xb1P1%\xf5?\xbf\x9d\xa1\x89\x83\u0620C\x86\xacipVH(\xd5,I\xe6+\xef\xd4\xe5\xef|\xf8c%\xc4\xd2Ul\x03\xf2S8\v\xc3Q-+\xf4>O\xa1\x9e\x04\x8b\\)f\xa4tU\u0334\xbe\xdf\x14\x80\x06\xd3uu\xab\x82\x9a\xbfW-x\xd6\xdf\xf1b\x98\x9f\xdeO{a\x1a\x1b\r\x05\xae\xc7\u007fw\x14\u01d2en\xcb\xe5\u03feb\xdf\t\x01\xe6\xdf\xff\xfe\x1dr\xc9%?\x95\xaa\xaa\xb9\xf0\xc2\v7\x1c<x\xb0\x14\fT\u07a8SOZ\xcf\xf8\xa2\xc5>\x12\xac\xb2uXT/\xaa\x83\x82C\xca\x16_\x9e\xccx\xf4\x89\x83V\x16@\xdc!8\xb1\x8c\x8c\f\xd3\xee$|\xed\xd6\xdb\xf9\xcc\xdf\xfd=\xdf\xff\xc1\xfd$\x897M\xb3\x91er|\x9c\xd1\xd1Q\x8c1\xcc\xcc\xce07\xb7P\xab\xbc\xa3(\n`\xac\xfd\x8fQ\x95\xd6\xc2B\x81\b)e\xfaz\x1e\xde\x1b\xc51.P4Y\x96\x91\x05\xce\xfd\x9f\xff\xe5nn\xbf\xeb~\xae\xff\xf9\x97q\xfd\u03ff\x94\rkV2;7_.;h\x95\n\xd0\x01\xa0\xf5\xc4\x01]B\xc5\xdbk\x1cVMrr\xde\u06f2\xe0\xe7\xf5)\xd2:\xc7;l\a\xbe\xdbR.\xbdHOXF$\x19\x8b\x1bsLD-\u058e\x1c\xe2\u0445%l\x9d_\xc6\xfe\xee8\x9d,\xc6J\x16\xfc\xe7{\x0f\xb9:9\u0557\x88#u\xf8\x96\nG\x9bs\xad\xfdFg\xdaw=;J\xa7\xda\xe2\x00Q\xe7\x15-\xc1\x06 '\x9d\xfb_S)*L\x05Z\x99#s0\u0590bXh\x82\u0574\xb5\x96\xc8z\aFg\x14\x1d1hf\u04594x\xc7K\x90\xeeU\x0e(\xe3W\xf6Q\xa5\x91\x80\xa6\x8at\x95l!#1B\x1aA7\x12\x92HH\fE6kI\x93\xf8\u06575`\x9a\x86,Q\x9cG\xfa\x82K\x0f\xedT\xc0L\x87D\xa67\xcf|\xa0\xc0\xc9\xef\x06H\xed\x8c\x15\xa4\u0432;QL\xaa\x98\xc4\xd5_lWJ&\xa9\x1c^\x85r\u0265\xb4\x17\xa6I\x93\x84(\x1e\xae\xa5\x17\x18#\x9c~\xc6\xe9\x13\xcbW\xac\x1a>!\xc0\xfc\x96[\xben\x80l\u05ee\xc7N5F\xce\f\xa0&\u057d\xc2f#\xe6\xe4\rk\x19\x1b\x1fgvn\xc1'w\xd7\xe0z0<W\xdb\xfc\xa7c\xe0YW\x80\x18\xb2\x00\xe6\x19\u07bc\u007fll\x84\xc7\xf7\x1f\u14df\xf9\x02\x9f\xbb\xf9\xab\x1c:t\xb8,6\xace\xd1\xd4\x14\x93\x93\x93\x8c\x8e\x8e1<<D\xa7\xdb\xe5\xf0\xe12\x80$\xe7\xb3\xe38&\x8e\xbd^ya\xa1U\xff\xddZ\xa7U\xf2C@U\u0272\xccWL\x95\x96Y\x8a\r5G\x9a\xa6|\xf4\u007f}\x91{\x1e|\x84\xdf|\xcb\xeb\xb8\uc8b3i\xb5=\xad\xd3\xeb$\xa7\x03\xba\x97'Z5W\xfdbL\x8dX)\xdf\a\x13\u876c\xf0a\xa7F\x9a\xfd\xd83\xeds\x9a\xc2\xf4\xaf:*\x10\x19\u01d2\xc6,\x13Q\x8b\xd5C\x87x\xb4\xbd\x84-s+\xd8\u05d9 U\x0f\xfa\xd2\x03\xe8}\xae:\x15\"WzC\xa8k\x03\x90J}\x1f\x14\x16n\xc0\xe3-\\&M\x9dKpi\x86\xcb\xd2`o\xab\xf5\xfb\xa2j\xe1[8#zc)T\x19\x8a\f\xa6Fa{@\x8f\xac\xc5Z\x13V\xd5\x05\xd7\xce\u0226S4\xf5\x8a\x95\\\xa1\x97\x03\x9d\x06.]\x05\x92D=\x90K^LK\xa0\x80\xbc\n&\xb2\x824\xc4W\xeb\r\x13l\x90\xebY\x9fV\x80\xd8\x17H\x0e\xe7M\u01ea\x9d\x86S4\r\x87\xa4\x95\u06b6n\xcd\x17I*\aE\xe8\xfdL~\x9c\x06 W\x11\xc4\t&Q$\v\x94\x91\xcb\a\x9f\xc5\xf4\x13\u0514T\x95*\xc6Z\xba\xdd\x05Zs\x87k\x94\x98\x88\u0c8c\x91\x91a\\\x96\xdd\a\xec<!\xc0\xfc\u0421\xc3\x02p\xd3M7\x9ffmtN\xadA5\u07a8f\xe5\xf2\xa5lX\xb7\x8a\xc8\x1a2\x858\xac\xd1\xcb\xd1\x17\x1d\v3+\xa9\x00\xc7\xd33\xec\xf4K\x11\xa9\xda\xe0 \u8946\xa3\xe3cl\u06b6\x93?\xfd\xf3\xff\xc5?|\xf56@\xb1QL\x96\xfa\xaa|\xf5\xeaU<\xf7\xb9\xcf\xe5\xc2\v/`\xfd\xfa\xf5,Z\xb4\x88\x1d;\xb6s\xdbm\xdfd\xe7\xce]\xec\u0673\x9bm\u06f6\xd3n\xb7\xe9v\xbb~N\xb0x\t\x13\x13\x93\xec\u077b\xb7\xafe\xcei\xa827RI\x92\xa4\xf8\\><\xf5U\x96\xc1\x1a\xe3U@\xceq\xc7]\x0f\xf0\x1f\u007f\xfb\xfd\xfc\xb7_\xfdw\xbc\xec\x9a\xcb\x11\x91\xe0\xec&\xb5\x9c\xd2\xfc\u0412\xd2\x11\xfc\t\xe2d\x90\xe1\xf5\x01\\p\x1e\xacU\xe4n@\x16\u04cf\x05\xbb\xfb\xc1\xd1\xe4\x8b \xbdu\x81\a'\x14\"IY\u059ce\xaa\xb1\xc0\xaa\xe64\xdbZ\xcb\u063e\xb0\x94C\u0771`\n\x15\f\u07a4\xbf<\xac\x99\xf9\xf6\xc8l\xfb\x00=\xff_\xa75\x9fm*Fa9\xb5\x12\xca\xe8pP;\\\x9a\xa0YVKw\xea_?\u0329\x1a_\x8d\xa30\x12\x19\x0f\xacR\x89H\x93|\xaeaH\x9c\x90\x89\xd2\xed8\x92\x83\t\xc9l\x8a&Z0\x1e\x1aI}\xdb9\xf3+\xf0>K\xb3r\x00\xd9p\xad\xe6\xcfO\x15\x9b\n\xdaq\xb8\u06106=\xb0k$\xc5p\xba\x1cx\x12R8]\x91\xf8T\xbc\xbeN!\xad\x04k\xf4X\xe0\x1a\xa9\x843\x8a\v\u0665\xe1,\xc8\xc3,\xf2\f\x83T1\x9dJ\x9c\xa4\xcb3\x82\u02d7T(\xa9_\u007foY:\xadYZ\xf3Gz\xcc\u0494,K\x98\x9a\x9ad\u7b9d\xdf\x13\x91\xec\x97\xde\xf4K\x8d\x8f\xfe\xf9G\xbb?\xd1`>==m\x00\xb6m\xdbv\x92\xaa6\x8aN2\xac\xdef\x99c\xdd\xea\x15\xac\\\xb1\x8cV\xe2\xfc\xc6\x18Y\xc9\xd9\x1dc\xd8%\xc5e\xf9\xf4\x01y\xe6c\xa2\xfd\x96[\u042a\x8e\x8e\x8d\xf1\xe0#\xdb\xf9\xfd?\xb9\x81o}\xfb{\u0638A\x1c\x19\xda\v\xf3\x00\xbc\xe4%/\xe2\xcdo~3\x97\\r1\u02d6-#\x8a\x9a\xc5\xcf}\xc5+^\xc1\xcc\xcc,\xd3\xd3G\u0633\xe7q\xbe\xff\xfd\xefs\xe3\x8d\u007f\xc3\xfd\xf7?\xc0\x81\x03\a\x98\x9a\x9a\n\x11V\xd9\xf1\x1f\xa3j\x0fo\x1a\xb6\xd3\xc2.\xa1\x84e\x13\xa7\xca\xce=\xfb\xf8\xad\xf7~\x84\xf9V\xc2\xcf\xfd\xf4\xf3h\x1aC\xa7\xdd\xed\xc9\xc0|r\x15\xf9\xa0?f@\u022f\x14\n\x16\t\x16\xbdZR0OsY~\xac\x1f\xa7\x86\"N/_\xa1\xd7\xca\xd6 \x15*&2\x8e\x95C\xd3,j,\xb0f\xf80;\x16\x96\xb2}~)\xd3\xdd!\xef\xd0(\xae\x9c\xcb\u0220\xf7\xa6w\x96\u04eb\x89\xa9\x1eh=)>\xc5PS\xfa\xaa~M3\x0f\xe6\xce\xd5N$? 5\x18\xebm\xe0\xc8\x14\xe7\xb20T\xf4\x1cyd\xbceK\x8e\xe2\x12~h\x06$\x99\xd2\xe9d$\x89\"\xd3)2\x9b\xf9 \xe3\xf0d\xa4\xfaV\x85\x83\xcf8*1r\x94\xe1\x11\x198[\x1e\x90\xb9\x85\xae(\x98,\xc3v\x85\xac\xad\xb8\x86\xe0\x1a\x824\fD\x94\x9e\x97\xd6?\x17\xe9:4\xed9\xf0\x83O\xbb\x982tBzl0\xf2m\u06b2\xf8\x97\x9aK\xa5(\xd0\r*\x16\xf5\U000c0a85\xb6jM\xf2\x12\xdec\x1f\x1e\xd2\xed\xcc\xd1i\xcf\xd6<\xa4P%MR\x9d\x9a\x9c\x94s\xcf9w\xfc\xe6\x9b\xfe\x1eu\xee'^\xd1\x12}\xe4#\u007f\xde\x05\x98\x9b\x9b=\xe9\xb1\xc7\x1e\x030Z\x88\xf4\xfd\xab\xbdr\xc52\x16OM\x92%\t\xb1\xb8\xa2\xea\xabUY\x03\xd6\x02\r\xfa#i\xa6\xfb\x95\x19\xf8\x19L\xd3\x00\x00 \x00IDAT\x86.Q\xe1i\x8d*c\xa3#l\u06f5\x8f\xf7}\xe8/\xf9\u05b7\xee`dr\x92\xd8Z\xe6g<}r\xfd\xf5\xd7\xf1\xeew\xbf\x8bU\xab\xd6\xe2\x95;-\x16\x16fQ\xf5C\xa6\xc9\xc9I\x16-Z\x02\b\xe7\x9d\a\xd7^{-\xaf~\xf5\xab\xf9\xfd\xdf\xff\x03>\xf6\xb1\x8fs\xf0\xe0\xc1\x10\x97\xd7\xf3\xec\x02\xb7\u9723:,\x1e\x04\xf0\xf9A \x81\xb7\x8f\xc4\xe02\xc7\xec\xdc\x1c\xbf\xfb\xfe\x8f142\u032b^\xfa<27G\x92$E\xcdl\xa4\xf4\x16\u0441U\xfb\x13H\x19\xa2\x8eo}1^\xbd\a\xad<}`=H\xe1\"\xfd\xa7M\xb8\u0524\xcc*\xed1m\xf3]\xa2_\xa6id\tk\x86\x0f\xb1\xb41\xc3\xda\xe1\x83l\x9e]\xc1\xf6\x85e\xb4\xb3\x18+>]\xaag&YqN-\x97k*\xa3\x9e\u0497\xa7w6\xa1u\x85Q\x9e\x1eo\x83\t\xb8:\x87K\x124\xcd\x02\xc0jA\xc5\xd9(\x06c\xc8B\x97G\x14\xa3b\x8bMb[XDT\x06\xf9\xea]\xfe:\x0e\xda*d\x89bgS\xe2\x85\f\x93\xe5+\xed\u07a7\xa4\xf0\x8fq\x83\tN5R\x02b\xbenoKUHMq\xe2 \xea8\xe8\xf8\xe1\xa16\x8c\xff\x88\r.\n\xbe5\xc6K'\x1d\x9e\xc2\xd1\xfc\xec2~1\xce\xd4y\x16\xaa\xf3J\x18\x14\x82\x9eK8\xfd\u01a7\xe9:$+\xccX*\xd66=I\x00A\u032e\xea\xedE:\xadiZs\x870\xc6R\xb5hTU\x19\x1d\x1de\u035a\xd5\x1d\x80\x95+W\x9c\x18j\x16U]\xf5\u05b7\xbe\xf9\x92\xb9\xb9\u067e[\xd1Z\xc3\xdaU\xcbX41J\xbb\xd3%\x92\xca\xd0(\xf8G\f\uea5f\x1e \xa7\x02\xe4\xa5\u046e\xe7\xa1\x1b\x8d\x06\xfb\x8e\xcc\xf1\xc1\x8f}\x86[o\xfdg\u0196.crb\x82\xf9\xc3\aH\u04c4\x97\xbe\xf4%\xfc\xe1\x1f\xfe\x01\x8b\x16-\xa5\xdbm\x15\x83JkK\xb3\v?\x1c-S\xa5\x9a\xcd&\xa7\x9dv\x06\x1f\xfe\xf0\x9f111\xce\xfb\xde\xf7\x81\x822\xa9\xfei4\x1aDQD\xab\xd5\xea\x931V\x01_{\xac9\x9dSo\x05`=\xed277\xcf\xff\xfc\xe0'X\xb3z%\x97\x9ew\x06N\xe7\u0086\xa2T\xb8B\x18\xb0\n\xd3'0<^\xf73H\xc9.O\xcb \xfa\x89\x02\xfaQ\u010bR\x16\x04\xd2[\x1d\x88\x14\x99\xb0b\xbc6\xc7:\x88m\xc6\xc8\xf0\x01V4fX3t\x98\x87\xe7V\x05>\xdd\u0516\x8e\xaa\xd4Sm^_qZ\xef\x9bs\xd7\u01a4\u055f\xe0\x15 \x9a{\x91Wl\x1c\xb4\xc2\a\x18kI\x93\x0e{\x1fy\x80}\x9b\u007f\x88s\x19\x93kOa\xc5\xc6s\x18\x19\x9f \xa4^\xf8\x85\x9c\u04359\x85\xaeS\xda\x0e\x92\xc8\u03c0\x1a\v\x19\xf1\xbc\arq\x81O\xce\xc1\xbc7\xb0\x9b\xba\u04e1\x14\xe9\x12\xfe\xf5\x13\xa5\xf4twR\xd0\x1cyu\ud36c\u00b5\xd1q\xa88L\f\xb6ap\x91\x906<\xbfN\xb8n\xb5\xdaI9\x87\xba\x10\xfa1\xd0)I\v\x17H[\x8dc\f\xef\xbb\xc9\x14\x9b\x849E\x90$\xaa\xcb\xfd\u07a9\xa5~ke\xbadl\xc4\xcc\xe1\xbd\xcc\x1c\u068d1\xd6\xd3>^J\ud187\x87\xcc\xec\xec\u0716\xd1\xd1\xd1;\x00Z\xad\xb6\xfbI\as\x03\xf0\xf8\xe3;\xd3M\x9b\x1eiW;\x11\r\x14\xc1\xe8\xc80\xabW,\xa3\xd9h\xe0\x82\v]\x8d\xa9\xcb#L\xfa\x86w<M@^\xbf\xd7L\x80uk\x04L\xccM_\xf9&7\u007f\xf9\x16\xec\xf0(K\x97\xad\xc0%\x1d\x8e\x1c>\xcc\u06b5k\xf8\xad\xdf\xfaM\x16-ZJ\xbb=\u007f\xcc\xea\xb9\xfa\xa7\xd3\xe9\xd0\xed\xb6\x88\xa2&\xbf\xfd\xdbo\xe79\u03f9r`U\xdel6\xb1\xd6\x1e\xf5\xe7\xd6\x16\x8dz*\xf54s \x86F\xec=#v\xed\xde\xcbG>\xf9\xb7\x1c<2K\xa39\x84\x18\x9f\xb7\xeaz\"\xb7\x8e?\x16>\xfek\xd8o>\xf6\xe4\x17\xfc\x95\xbe\x9c\x80'\xf0(\xe8s\xd7\u0523\xf0\xe8j\x14\xb5\x12\xbcG@\xabv\u06a6\x944\x1aq\x8cEm\xce\x18\xdf\u00d5K\x1e\xe6\u00a9G\x99j,\x90\xa9\x1f\x8eW\x96\"\xfbsV\vu\x86\x14\u04b9\xa3\x0f\xf4\xa5\xc2g{\x83\xa8\x99N\xca|\xab\xeb;)\x97\x15\u065e\xbe\xdd7l\xff\x97o\xf2/7~\x84\a\xff\xf7\x17x\xf8\x1b\xff\xc0\x03_\xb9\x91G\xbf\xff\r\xd2n\v\t\u05cd\xaa\x97\b\xb6Re\xbe\xebXH\x95D\f\xd8\xff\x8f\xbd\xf7\x8e\x92\xe38\xefE\u007f_Uw\xcf\xcc\xeeb\x01,r \x88@\x80A\xccA\x04\xb3$\x9a\x92\xa8@E\u0496D\u0274l_Y\xbe>\x96l_\xf9\xfa\x9e{\xed'\xd9\xef\x1d\x9fg\xeb:\xe9]\xcb\u05b3h_\xcbV\"MR$%J\fb\x00s\x06!0\x00 r\\\x84\xcd\x13\xba\xab\xea{\u007fTuw\xf5L\xcf\xee\"\xf0<\x1f\xc1s\u0392\x8b\xdd\xd9\xd9\xd9\x0e_}\xf5\xfb~A\"l2\u00ba\xca0p2\x16\x13\x87F\u0198!?(\xdd/\xe2\x9c\xd1GrV\x88+\xfa\xa4Sow\xce$\xfa\x94a\xe19\xffR(\x83\xa0i\x10\x8d\x1bD\xe3\x1a\xd5\x11\x85\xeap\x82\xa8i\xdd\f)\xcb\xe8K\xb38\xb9\x80e\x15\xdc=3\x8b\\\u4ed2\x141ag\xaa\xe5\x06\x9e\"\xc3\u033d\xdd\x15\xf9\xc3\xcd\xd4)S\xc0h\x85\x91\u00fb\xd1l\x8c\x822Y\xafe\x17\x85a\x809s\x06\x0e}\xe63\xb7\x1c\x06\x80\x8b.\xba\x90O\x8ab\xfe\xdcs\xcf\u03cb\xa2\xca\x1a\u02ea\xc8\x1f\xda\x18\xcc\ua7c1\xb9s\x06,\x8f\x1f\xc2\xcf\xf1*/\u0669\u007f\xc2\t\xf3\x18\xf1\xec\xb1R~\xb0\u327f\xf6\xc6\x16|\xef\xce\x1f\x81c\x85@\x12\xf6m\u07c2}\xbbl\x00\xf5\xfb\xdf\xff~\\y\xe55\xd0:\x9eV\x11\xef\xbc\xd1\x15f\xce\x1c\xc0M7\xddX\xfa\x9c$I\xd0l6\v\xf8x{1\xef\xf6\xef\x94\xd9\x02BV\u041f|\xe6E\xdc\xf7\xe0:DQ\x05\u0489\x1f\f\x93\a_\xf11\x1f\xd3\xc9\x13\x9b\xe8\x04\xcc2\xa6ge\u0325\xef\x87:\xb9\xe1i\x05\x16%\xbe\xda\u0085P\vd\x1e\xd8\x01\x19\u0329\x8c\xe3\xfc\x99\xdbq\xd5\xc0\x1bX\u077b\x1f\xa1\u0408Y\xc2\x18j\xdb\xc1P\xa9\xc8%\xfb\xbc\x8d\u0398\xef2s\x06\x86\x01c\"N0<\xd1\xc4\xc8D\x82zK\xbb\u0754\x86\x8c*8\xbc\xebMl\xf8\xc9\xed\u063f\xf9g\x98\x18>\x84\xfa\xd0A\x1c\u0679\x05\u06de}\x18\xc3{v@\x86\u0591>a\xa0\xa9\x18-\x05(&\x18!@2@\xd4\x12\x88\\G\x0e\xb8n<\xfd<-\xca\u0736*\xa6\x05\x10\x19\xa4\xecX!\xa978 \x94}\x9dla \xce\x16\u0334\xcdN\x03\x93\xd3\xd7\x13\xca@$\f\x910d\u04e02\xa6Q\x1b\u05c8\x9a\x06B\xe76\xb4E_k\xdbY\u06cf|x)|\xc8%\xe5\xdbk\x06Z\xc6\xce\x03\x98<\x16\v2\x87\xd2\xf6Z@ H\x19\xa211\x84\xc3\xfb\xb7\x80\u074e;}\x8aVZ\x0f\f\f`\xf6\xec\xd9\u06c8h\x1b\x00\\v\xd9e'G1\x1f\x1b\x1b[5s\xe6\x8cU)\x1f\xdb/j\xfd\xfd}\x98=\xab\xbf\xe0Q\xc2\xecd\xf3\xae{4De\xb5\xf7\x84\r\xd0l!\u03e1\x96\xb0RE\xbd\x11\xe3\xbe\a\x1f\u014em\xdbA\x81@k|\x14\xadF\xbd\xd0a7\x1a\xe3\x19m\xf0h\x1f)\u03bdl\xd9)\b\x02Y\xda\xc1+\xa5&\xc5\xca\xcb~o\xfau\xcbC\xb7\x17a\x14\x06\x98\xa87p\u03cf\x1f\xc1\xe0\xfe\x03\xe8\x89B\x80\x04\x98\x84c\xb3\xe4v\xab'\x92i\u00a5{\x9f\xe9/\x0e\x1eWa\xda?\u05feK\x98\xca\u317a\rN]{g\xa5\xf8\xb6\xfb\x8bHci\xed\b.\x1b\u0602\xb7\xcf\u078a\x05\xd5\x11(\x12HXf\x96\x00\u0183\xac\x8a\xd3\x03W\xe8\x8bI%\x056E\xaa\xa2\xd4l\xa0T\x82VKa\xbc\xa50\xd4\xd0\x18j\x1a4b\r\xc8\x10{__\x8f\xfdo\xac\xb7\xf1t\x89\x85b\x92f\x1dC{\xb6a\xe2\xc8 \x88$\x12m\u0408\x8d\xa5(\x12A\x93\xe5\xe5\u0216@Xgk\xdce\\A\u0576\xbb\xee(\xe2\xd3\xd9\xca\"g\x87\b\x9d\x17u\x19[h\x03\x9c{\xbe\xf8\x8b\x85\xff\xb3\xc20\xa4\v\xe3 \xc3\bbFe\u00a0:n\x10\xb5\xac\xc8'\u0760\xa7\xb9\xab\xac\xed\u0417\xb5\xb1\x9fgktq&!\x13\x03R\xce\xee\xd6\x13\\\x11\xfc\u0646g\x94\xee\x16W\x19D\x98\x18=\x8cC{7e\xc3\xcf\xf4\xacj\xa3\xe5\xc0\xc0l\u031f?o=\x00\xfc\xf1\x1f\u007fY._\xbe\xf2\xe4(\xe6\x1b7\xbe\xaa\x06\a\a\xb3A\x9f\xaf`\xeb\xed\xe9A__/\x8cq\xd1j\x1e\xf9\xdf\x0e\xcf\xe9-\xf3~7Y^g\xfe;\x04Y\\{\xeb\xae}\xf8\xe9c\xcf\x00\xac\xc1\xaa\x93q\xf4\xd0C?\xc5#\x8f<\n\xa2\x00\xc7P\xcb\x11\x046\xb7c\u02d67a\f\xa3R\xa9\xa0\xaf\xaf\xaf\xd0]\x9b.\xf1&e]y\xe9\x82BdS\xe3I \f\x02\xbc\xbey+\xd6=\xf5<z\xab\xa1g\x1bzb|\x14\xa9t\xad=\xf6\x84\xa2b\x8eT{\xb6ig\xc7\xde\x1e\xd0L\x1d{\x83\xee\u007fkY\u007f\xc0\xe4D/d\x1d\xf9\x02h\baS\xafz\x83\x16\xce\xea\u06c3+\xe7l\xc2Y\xfd{\x10\x89\x04M-\xa1\x8c\xf0\xc6\xc0m\xbf\x81\x8a\xdb\xf9\u00bb\xa7\x9c7h\xc06\x1cZ[:\"\xd8F)N\xc4\n\xe3J`xx\x04\x83\xdb\xde@\xab>nAA\xa3\xecN,\x8eaT\x02\b\x89\xa6f4b\x03\xc5\x04&\x81\x04\x84\x04\x04\xd1\x12\b\x1b\xce\x02\u0597\x9cr\x8emw\xc2VS\\%\u073ek\xb6\xf0\x8c\x15\xe80d\xcbv\xde\xc4m\x8b\x19\x173VH\x1b\xdb\xd9\x1b\xc7\xccd \x88\r\xaa\xe3\x1a\xb5\t\x8d\xb0e t\t#\x93m\xf7M\x86\xddB\x98\x8f\xf2Y3\xa8e\xa1#\x06\xdbP\x0e\xb7\x80\xb1\xe1\u04acS\"\u01dc\x010zd\x0fF\x8f\xec\xc9\xee7v\xbb\x820\bDT\xa9\x1e\xbel\xed\xda\r\x00\xd0\xdb\xdb\xfbs\x8f\x97g\xc5|\u03de\xbd\x18\x1b\x1b/\x14\xa1\xf4D\xf6\u052a\xe8\xed\xadA\x9b\xd4ZV\x14\xd8%\x9aDi\xfft\"\x18,9\xb4\xe2\xba$X\xdb\xd9$Qx\xe9\x95\u05f0w\u07c1\u049f\r\xc3\x10\xbbv\xed\xc2]w\xfd\x00Z\u01e8\xd5z;\x86\x93\xdd!\x16\xb8N\\\xe2\xe0\xc1}\xb8\xed\xb6\xdba\x8c\xc1\x9c9\x03\xe8\xef\xef/\fO\xfdb]\xc6x\x99\xb2\u023b\xf7$\xa4D%\n14:\x81'_\u0608\x91\xb1\t\bY\xec\x96\u028b\u0431\xc3-8Nw\xc6\xe9\xa0\xf7<\t\xd43\x95?\u03d4E=\xc3\u05d1:2\x81\x04C\n'\x1b\x17\x8c\xf9\x95\x11\\2k+.\x1d\u0602\xf9\x95Qh\x16HXt.\xeel\xfd\xc8\xd9\xd8\x0f\x93\x89\u007f\x00_\xe3\xcf00:\x86\xd61\xb4\u0459\xe5-\x01\x10l\x80\xb0\x8a\xc1]\xdb1\xb8e\xa3\xf5\xc6\xf7\xa6\n\xba\xd5@\xcf\xdc\xc5\b\u7702\xb1F\xcb\xc2* (\"(\x10\x82\x16\xa1\xda$\x04\x9ar\u1303X2\x8eu\xdb\x11\xe2\xc9\x0e&u\xc20\xed\xc5\xd1\x0e\x1e\rdl R\U000bbdc0\x907t\x04\xac\xa7\xb8L\\QO\xb1k\x06\u0096AuB\xa32\xae![^\xb4\x90scd\xc30:\xb7\x83Na\u007f\xa9\fD\xa2s\xe8\xc8\x15\xfd\xfc=\xd87[8\x1fD\x10A\x00\x954ph\xef\x1b\x88\x9b\x13\x10Bf]\xbf1\x86{{{10{`\xd7G>\xfa\xf1\x97\x01\xe0\xf2\xcb/\xc3IS\u0337n\xdd\xdaj4\x1a\xa5\u017c\xb7\xa7\x8a\x9ej\xc5%\xebp\xe6H\u01c0\x1b4\x15\x15\x1f'\x14\x06\xf0H\r\xa9<=\f\x024\x1b\r\xbc\xfc\xcaF$q\xdc\x15\"\x91R\xe2\xb6\xdbn\xc3_\xfe\xe5_\xc1\x98\x04\x95J\x0f\xaa\u055eI\xf1s!\x04\xaa\xd5\x1eHY\xc1\xd0\xd0!\xfc\xe1\x1f\xfe\x1fx\xfa\xe9\xa7\xd1\xd3\xd3\x03\"\x01{\x8cDa\xfb\x97\x0e@}X\xa5\xfd\xff\xdd\x06\xa2\xa9\xe4\xdf\xf2\xcf\xed\"\xb1y\xfbn\xbc\xb1c/(\b\xf3F\x8c\x8e\xbf\xc0v\xde\xcb\xdcm\xea1\xed\xd7,-\xc0\xdc\xd9G\xfbW\aO\xf1zG\x15\x9dG\x80\x91\xf6#\xa5\u03f1d\x904\x10\xc26cU\x19cM\xdf~\\=g\x13\xce\xe8\u06c7\x904\x12\x0e\xb2\xdcX6\nF\u06ee\xd9h\x05\xa3\x95\x15\x00\x19c\x8b\x88\xfb}\xc6\x18(\x1dC\xe9&4+K746\xf4\x02N\xcc\"%\xe1\xc8\xf6\xd71\xb2{+d\x18Z\xbb\xc0\xecZ0\x98\xb9\xe2LD\x8b\x96#Nb\xb0\x14PD\xd0L\b\x9a@\xa5\x01H\x96 \a\xed\xb0+n0%\xf88\nU\u0583\xe1:q+\xa2I\x86\x1c).\xaes\b\x86<\xdf\xf0\x8c^$rA\x15\xa50\x8d\xe79N\xb0\x01\xcbA]\xa3:\xaa\x10\x8d\x1b\x88\x98\x8bR\xfe\x14z\xa1|6!2n9\x1cf\xee`Hmrr\xb9\x9f[\xe7\x86\x01RFhN\x8c`\u07f6\x97aX\x81\x84\u022c\x01\x94R4o\xfe<\xacX\xb1|\x1b\x11\xed\x01\x80\xb5k\xaf8y\\\x13W\xacX1\xe7\xe0\xc1\x83mrg\x06\x91@__\x1f\xaa\xd5j\xb6\xaa\x8a<\xab+\x0fa\xed0\x14:1\x83O\xe6\xdc\u0396\x1d\x8fIJ\x81\xb1\xf1:\xb6l\xdd1i\a\xcc\xcc\x18\x1a\x1a\xc6\xff\xf8\x1f\u007f\x84\xad[\xb7\xe2s\x9f\xfb\x1c\xce>\xfblDQ\u037b\x9a\xd3\xddW\xdem\x8f\x8d\r\xe3\xb9\xe7\x9e\xc3_\xfc\xc5_\xe3\xbe\xfb~\x840\f\xd1\xd7\u05cbF\xa3\x81\x89\x89\t\x18\xa3\n\xf8w\xd9\xe7\xdd\xdeS;,\xc3\xccPJ\xd9E\xa4RA\x14\x86\u0633\xff\x10\xde\xdcy\x00\xabN[\x05\xc3\rOP\xc7G\r\x8a\xe4\xae+\xe4\x05w\x94\xe1\xde\xc7\xd7\xed\x17zF\xe2i\xf5\xed]\xbd<\xa6\xf9\xbe\n\u040c\u021d\x03\xb3\x81.3\xa4+J\x02\x1a\v\xab\u00d8\x1140\xa72\x86\xd7\u01d6\xe0H\\\x03\x99\x040IfA\x9b\xdf\x00\x1e\x01\x9d\xac\xb3\x9f6\t\x14+(\xd2Pnn\xc4Y\aK\x10Q\x15c\a\xf7a\xf7\xf3\x8f i\xd4!+\x95\xac\x90\xeb$F\xcf\xc0B\xcc[y>$\"$\xba\t\x04V{\x1f4\x19QL\b -\xbd.-\xc0&\x87U\xc8cw\xb4w[\xbe\x10\xa7c\x89%\x14B6:\x96N\xff\xe9\xda\u044c\xdb\x17\x03\xedL\xca\xd9[\xfc\x8d\xa5\x87\x8alwdM\xceSk\x01\x91\x18\x04\x82\x90T\b*\xb2\xb35\x18\v\xb7\xb0K\u0720XC45\xa0\r\xc8\x18\x90vN\x93\x86Q\xd0\xf702\x85)9!\x163chp+\x0e\xee}\x1di\f\x94\x15\x9fYk\x8d\xfe\x19\xfd\xb8\u448b_\xec\xb6#\xfe\xb9-\xe6\xcc\\\xf9\xda\xd7\xfe\xfa\xba\xc7\x1f\u007f\xc2Q\xab\xd2H)\x06I\x81J\xb5\n\x11F.\u02638\x1cI\xf3\x1b\xa9M\xa4q\x82Jyf\u07aa!m\a\xe3\x9c\xea\xc7\xc6\xc70661)\x1e-\xa5t\x8e\x88-|\xfd\xeb\u007f\x8f\a\x1ex\x107\xde\xf8q\\~\xf9\x15X\xbat\tf\xce\xecG\xa5R\x05\xb3\xc1\xc4D\x1d\x87\x0e\x1d\u009bo\xbe\x89\a\x1f|\b?\xfe\xf1O088\x88(\x8aP\xadV\xd1j\xb5\xd0t\x1e*\xedB\xb2\xf6\xae<\xc3\xef\xa6\t\u0527\x05\xdd0#\f\x03\x8c\x8cO\xe0\xf0\u0430\x1d\xdcN\xa3\x1b\x9e\xea\x18v\xf1\x13<a\x90J'\xc2\xec'^r\x9bUr'R\u007fBo\xb3BZ\xbd\xfdL8\xf1\x1b3\xa1/h\xe1\x9c\xfe=\x98\x1d\xd6\xf1\xfc\xa1\x85\xd83\xd1\x03\x06#\xa0,t\xceI\xdd\x18\f\r2\x02\x06\n\xac\x15\f)h\x97\xf8c\xfc\u25aa7E\x80\xe1\x9d[px\xf3+\xa0\xc0\xcdi\x8c\xf3\xf3V1\x06N=\x13\v\x96\x9f\x0f\f\xd7!\xc9\xca\xd9E\x02DM@\x92\x04\x85A\xa1\x90\x921\x85\x98\xc3\x0e,\xd0;\xe8L%K \xe73\x00\xe2\u026c\x89\x19\x1d\xb2X\u07fa?\xf5L\xf7\x95\xc9^\x88\xb3I\x8f\x81\x82\x15\x10\x05\x96\xc6\x19\x18\x03\xa1\x00\x11\v$5\x01\x1d\u0602n\x17\a\x03\xd9L \x12\x97\u03ea\x8d+\xea\uc0ba\xd9\xdbY\x14m\x1a\x88$t\x12c\xdf\xf6\xf5h\xd6G\xb2\x9dA\x9a\xe9\xda\xd7\u05cbY\xb3f\xed\xfb\xe0\a\xde\u007f/N\xb2G\x00 \xaa\xd7\xeb\xb3\xe28.x\x8d\xa4\x85\xc6xqN\xec\x05\xf5\xb6[A\xe5\xcf8\x91\x18\x10g^\"v[l\xb1\xb3V+\xcf\xdb,+\xe8\u030c(\n\x11\x86\x01\x9a\xcd\x16\x8c1\u0632\xe5M\xfc\xe9\x9f\xfe\u07e8\xd5jX\xbe\xfcT,]\xba\x14\xfd\xfd\xfd\xd0Z\xe1\u0211!\xec\u0631\x03{\xf7\xeeE\x92(H)2\x1e\xb9R\t\x94\u0496O\u0705\x9d2\u067f\xa7\u04f9k\xadQ\xaf\xd7\x11\x86\x12:\xd1h6[^D\xd6\xe4\u066aS\xb6\xad\xa5\xfd/\x9f\x90\xba\xe9\xef\xfa\xd3\x10\xe7t\xaa\"\xbc\xebc:\xc1\xd5\xc7\xea\"@S\rh\t\x10\x82All\xb2\x0f1\x16\x84\x838\xafo\x04\x15^\x80\x1d\xf5\xb9HL\x88@\x98\xec/I\x17\"m\x94\xc5\xd1YC\a\x8e\xcd\"l\xfa\x0fs\u0389\x11A\x05\xad\x891\x1cX\xff$\x92\x89\x11\xeb\xab\xed\\7Y)\x84\xb5>\xcc_}1j}s\xa1\x1a\x13\b \x00e Y@\xb2\r\xba \x9b\x9e\xec\xe2x\x8c\xc5\xcd\x1d\xae\xdd\xee[\xe3S\x83\xbb\xda\fS\x11\xf2/\x1ea*\xc1\\\xa8}e\xc8\xe1\x96IN\x90\xe0\xb4\xe3v\x87N[\xa6\x11\t\x824\fj\x1aH\u0148k\x02\xa6F\xd0\x06\x10-\x05\xd9T@\xcat\xf1\x84P\xc4%\xa3t\x8f\xf9(\x84D\xdc\x18\xc1\xbe\x1d\xaf@%-\x9b\u0104\xdc\\k\u05acYX\xb6\uc517\x06\xe6\xcc\u007f\x19\x00n\xbd\xf5\x1f\xf0\xd9\xcf\xfe\xdaIS\xcc\xcd\xf8\xf8\x84\xb2\xea\xc8\xf6\xb9\x90\x8d\x87#O\xfak\x9d\xce\u0716\x06o5\x14e\u007fG\x00\xbb\xb5\xd5,\xc0\x9cg1NVP\x93D\u0658\xb8\xcc\xd2\xd6~4\x1a\r\xbc\xf6\xda\xebx\xed\xb5\xd7\xdb:y\x01!l*Q\xfa\xbbS\v\xdb\xd4\x1dq\xb2\x9d\xc0d8<\x11ux\xbb\xb4o\xfd\xb4\xd6v\xf0Jde\xdf\xe9\xce\xe7X\xecR\u0607?\xca\xfb\xe7\x13a\xc1R\x9c\xb1\xe5\x11}HM\x99\xb2!+OVk\xba\x0e@\x8f\xb7\xb8\x17\x1aV\x01\x10\x1b\u8111$\x06\xb3\xc2\x18\xe7\xf6\xc7\xe8\x93-l\xae/\u0138\xa9A\xa6>Bl\xb1p\xc3\x16;gg\xfc\xc5\x04ha\xa09\x8b\x9b\xb2\xc7X\x06\x18\u06ff\x03\a6>\xe3\x1c\xfd,<c\xbb\xf2\x04\xb3\x96\xac\xc1\x92\xb3\xaf\x02\u01c9\u015c\x99!c\x03\n\xa5\xfd \x99moEb\x99&\xe4\x0f\x01y\x92\xa3B\xc7rd\xb8m\f\xde6E\xed\xf6\xfb&3cb\x82\x1f\xf0$`G\x06`@*F\xb5\xae\xa1\x8c@R%PC\x01-\x9dY\x06\x93i_9\x8ao\xbb\xfd\x8a\x1d;\xbc\a#\x87w\x81\xd9@P\x98\xef@\xa4@\xadV\xc3\xc5\x17_\xfc\xec7\xbfy+\x00`\u0252%'Ug.*\x95(\xb0l\x8c\xce!]\xa2\x8dU\u04f9[W;\x0f\xb5\xc0\xf3\xc76\u07ad}\"}\x9aRC\xa8\u020d?\x15B\x18\b\xd4j=\b\u00e0\xf4\nK\xa3\xbe\xe28F\x92(\x10Y\u0225\xb7\xb7\x17J)\xe7\x88\xc8\x1dC\u0254\x8db\xa1\x14\u04f1K)[8\u02be\xef\u007f\xcdz\x9c\aY\xb1n\x1f\x86\xfa\x18\xba\x94\x12\x86\x81J\xb5\x82\x9e\xbe^\xebvF\xf9\xed6\xd5}[\x0e]\xb0\xe7\xfbGm\xde\x18|\xc2\nz\xcem*f\x98\x9a4U\xa6\v^\xde\xfe\xbe\u0465_\xc4\x14\x85\u007f\x1a[<\xc0\x00\x891\x88\x95UkJ\x10ze\x13k\xfa\xf6\xa1\x16\xc4\xd88\xb1\x14\u00ea\u05f9\xbc\x1b\xb7\vt\x1f\xc2\u01b2\x19\x10\x8ct\x16e\xce}\x90\x82\x00\xac\x15\x0e\xbd\xfa\x1c\x1a\x87\x0f8\x9c\xdaR\x16M\x92@\xd6z\xb0\xe4\x82w\xa0\u007f\xee2p+\x86\u0400d\x82 \xe1\x02 \x02\x90\xb4!\x9dB\xdbB\x9e\x8es\njN\x9ad\xeaL\xd3\\\xe1\v\u01efK\x11\xf7\xfcMJ\xa7\xa7\x05\x03\xb2\xe2l\x92)wo$r\x89G\xc6v\xed\xa4\x81\xb0i Z\x06\xa6\xe9\x8c\xc9REQA9\x9a\xbaVr\xdbN\xc45\x92\xcc8\xb8\xefu\v\xb1Pj`g\xdb\u02be\xde^\u031f?\u007f\xec\x82\v.\xfc\xe7\xf4\x1d\xbf\xe7=\u05df4\xc5\\\x000}}}:-\x8e\xed\xc5|\xbc\xde@3V\xae[\xcco=M\x02\x86D1_\xf2-0O%\x17\xa3\x10\xc2 d\x9b\x93\xd2\xdb\u05cb\x81Y3;\xe0\xc3\x0e8\x91M\xc6\x18\x01r\xee\xb8\xefh\xa8\x94\xca>\xfc\xdc\u03f4\x93\x9fL\xe19\x9d\x0e=}\x1d\xff\xb8\x96\rd\x02)\xa1\x12\x85\x19\xfd\xfd\x98;g\x00P\x89\x8b\xdc\xe0\xa3,\xac\xdd\x06\xa1\xd3h\xb0\x8e\x037\xb7;(ktea\x96\xdc-3\u035d\xe4B<F9\x8d\xb1\xb3\x80S)\xc5q\xda\xfc{\xd7\xd9%\x86\xd1T\x06\xb1\xdbm\xba8\x13TD\x8c\xe5=\aq\xc1\xcc\xedXP\x19\x82fF\x02@\x933\x81%\x06\xa4\xf3\x10\x97\x04\xd3\x16\xa2,\xc3\b\xcd\xe1\xc38\xb0\xe1)\xe8\xa4i\xbblv\xbbZ\x15\xa3o\xd1r,\xba\xf8\x9dV\xc3n\x18\xc2\x10\x88\x05\b\x02\x02\x815\x88r\xa6\xdfT\x12T\x92\xc1\xf2\x93\x14g \xa7\xf0r74\x8d;\xa1\x19*\xdb\xc1\xa4\x04s\xf2\u03d4?\x11\xednKIi\u051bv\xac\x17{\xe8\x9c\xf2\u050a\x8f\x82F\x02\xd9H\x80\x96\x8d]$\xce\x191y\x00F\xa7\xae\x98\x9d\xe3\xa8J\x1a\u063f}=\xe2\x86sJL\xf9\xff\x86\xd1\xd3\u04c3s\xce9gd\xed\u06b5\xdb\x00\xe0\xa7\x0f\xff\xf4\xa4\xc2\xcc\x05\x11M\x1c92\xf4Hoo_\xd61\n\x91\x9f\xacCC\xa38<\xdap\x13c\x91\x8d&\x99\x84\x93\xe4\x8a\xc2BM'\xb8X\xa4\x97\x9f-\xe8\x1aP\tzk5\x9cq\xfai\x90R\xba\x01\xc9\u4152\x99Q\xaf\xd7Q\xaf\xd7]\x81FV\xa8\xfd\xe7\xb4\x17\u0763-\xe4e_O\x17\x8a\xc9\x06\xa4B\b\x90\x10PJa\u1885X\xbel1L\x12\x17\xb2\x0e\xdbi}\u0736\x1b)DV\xb6a\xaa\xa5\x93\f>\x91\xe7\xa73\x9b\x91\xdb \x16\xf6\xfe\xc5m\x03\xb72\x04\x17m\xcf/K\x02\xa2)\x8azZ\x9bbePO\x14Z\xda@\xbb\xf0cCl\x8d\xbb\x84A$\x13\x9cR=\x88\v\xfa\xb7\u151eA\x18\xa1m\xd6&\x19\x1b\xf8\xe0B\x1f\x8c\x042\u0751\vxf0\xf6\xbd\xf8(\x86\xb7\xbf\xe6-\x1e\f\xa3\x15\x82J\x0f\x16_p\rj\xa7,G\x12\xa5\xbclw\x8f\x91\x84\x10\x81\x1d\xb8:\xb7E\x169\x13\xa7-\xdf\u00a5u\xf9\xd6\xc0T>\xbc\xf0W\xba\xec \xe5]6u$\xf7\x96X\xb7\xb9\x00\xeaN\x8eZ\xf9\xcc%\xb5\x0f\u0228\x8b\xe9\xa03\xb5\xd9Mi\x96-\rn% \xa5A\x89\x06%\x16b\u024c\xd4R\xa5g\xa7\x8e\xdf\xd5&\x89\xa1\xfd[ph\xdf&h\xadlg\xee.~!\t\xb5Z\rk\u05acy*\xfd\x99w\xbd\xf3]'W1\xb7\x85E\xef\\\xb2d\x91\x17\xb8\x90\xe3\xd1\xfb\xf6\x0fb\xf7\x81C\xa0 \x82f@\xb1\x80rFF\tK/\x1c\xf8\xd8\x13\xe5\xa7[\xd0\x05\x18P-\xf4\xd6B\x9c\u007f\ue648\xa2\bR\n\x04%B\x9e\xb2!c\x8e}s)~\xcd\x05Iq\x97\xf72I!?\u0582/R:\x90\x90Xu\xda*\x9c\xbax\x81\r\u0560\xce>\x94\xa7\xdeEg\xf1_\xed\x16\x00\x85e\xe1-ak\xe5%\xbbH\x85\xccc\u0734o\x8cL\xe5FbT\xc0\xf6\x8b\x85\xbf\x9dr\u074d\x9b\x9e^\x8dqb\u040a5bm\xa0a`\x88\xed \xd3a\xe0\xf6s\x03\"\x8d\xb9\xd10\u039e\xb1\x03\xa7\xf6\f\x02\xd2 \x11\xd6\x02V\t\x82\x16\f\x0e\x8a]\xb2\b+\xa8\x1f\u0687=\xcf=\x00U\x1f\x87\x90\x01RoXf\x83\x9e9\v\xb1\xe0\xbc+Aa\bE\x06p\xf3\x13!$\u0205@\x93\x8b\xe31.~\xad|\x97EE8\xa2D$D\xe8\x92\xcc\xc8\u0716\xe1\xc6m;\x1f\x9e|\x95\u6a7e\x99/<\xa9\x17LZ\xc8ar\x0e<\x19\x9d\xd9\x05\xb31\x99\x0f\xba=^\u02024\xc8[\x10\xb3\xe1gz\x92\x05ap\xf7\xabh\x8c\x1d\xca-o]\xf7\"\x84\xc0\xa9\xa7\x9e\x8a\x8b.\xbeh\f'\xe9C\x00\xc09\xe7\x9cs\xa8V\xeb\x19I;]\xbf0\xed\u067d\x1bol\xd9\x0e\x11F\x0e\xf2\xb0)6\x99y\x0e\u02b9\xe6o\xd5@\x94\xb5BU\x18\x9c\u007f\xe6J\xac\\\xbe\x14Z\x1b\xd4*\x95\fC\x9f\x0e\xe3\xa4\x1d\xf2(\x13\xfc\xb4\x1f\a\xff\xdf\u077a\xff\xa9\x86\xa4\xed\x8bF\xf6\xbaB\xa0\u054a1{\xde|\xac\xbd\xe4\x02T\xa5U$\u0494\v\\\xb7\xed4g[SP;^z\xfcA!\u04c3y\xb8\xa3\"p\xb7\xad{\x17\u0225\xd0\xd9Sw\xbdj\xc7\xf2\xec<TZJ\xa3\x99h\xc4\xca\xc1e\xf0\x9c\x18\x05`\xa4\xfd\xdc\x00\xd0d;\xf6\xd9\xc18\xce\xed\u0749\xd3\xfb\xf6AV\f\x1a2\x80\t\x00\x13\x10L\x90\xd3\xe0HH\x18\xad\xb1\xeb\x89\x1fbd\xfb\xeb\xa0 \xcc0v0C\x86\x11\xe6\x9cq!f,Y\x05h\xab\xb2\x04\b\x90\xd6\xdeV\xc8\xc021&\x9b\x00\xfb\u01e0l\u0562\x1cb\xf1\xff\xdf\xf9ze\xe9\xbcT\x84E\xb9K\xe5\xa6.\xa0V\x99\xa4\x97;M\xbe\xd2,Tc48\x8d\xd6\xf3\xa0\x99\u0531\u04a43\n\xff\x85\n\r\x02A\xab\x18\u00c7\xb6\xa3\xd5\x18\xb7B!\xb2\xac\x190#\x10\x01.\xba\xe8\",Y\xb2tN\u0588\x1e\xd8\u007f\xf2\x15\xf3\x9bn\xfa\u012e\x993g>_\xadV\v\x85\x8b\b0q\v\xeb_y\x15\aG\x1a\x90A\x94y@\v6\x1e\xa6k\xcf\xde[\xc9\xcd'\xef\x06O\x9a\r\xacX4\a\xef\xbf\xf6\n\x1b\xfe`\fz{z\nR\xfb\xfco\xa0)\vy\xd9\xd7\xcb\x16\x81\xe9`\xe4Sa\xe9e~.\x96k\xaeq\xf6\xd9g\xe2\xf2\v\xdf\x06\xd5j8/\xe7\xee\x1b\u074ecCE\xf7C?\u00ac\fu~\xabe\x14\xbe\xea\xb3\xe8\xc7RfH\x8bB\xf9.\xff\x9by\xca\xe3\x90b\xf2\xc6\x10Z\x89F+\xd6HT\n\xa7\u5fd7Hd\xfe\x1e~\x97\x9f\x9a\xc6\xf5\xc9&\xde\u05b3\x1bg\xf6\xecA5J\x90\x04\x12:\x12v!p\xe7KTj8\xf4\xfa\v\xd8\xfd\xf4O\xa0\x93\x96\x1d\xcei\x9d\xe5\x99\xd6f\xcf\u01d2K~\x01\u044c~p\x1cC*\x9b\xedIA\x88 \xeaAX\x9b\x81 \xea\x01U\"\x10\xdb\xfc\xcbT\x00\xd5^c\xb9\xc4\xd1\u0187\xc1}\u031b\b%\xfe\xc4\xed\u017bDB6i\xf1\xc7\xf4a\xba6a\x13ke\xad\x12\xc8\xd9%\xb8\xc5.\xb3Vg\xbbS\xe2\xc22OY~if\xeb/\x04\x9a\x8d\x11\xd4G\x0f\xc3h\x95\xeaNA\xb0\x011Q%\xc2\xd9g\xbf\r\xbd==k\x98\xb9\x17\x00\x16-Xx\xf2\x15s\"\u06bfx\xf1\xe2\x17\x97,Y\f\xad\x15\xa7t\xba\xf4\f\xbf\xf0\xc2\xcb\u0630y'\x82\xde>$\x06.C\xb2\xbd{x\xeb\x1f\xe9\xefH\x92\x04Q(q\xdd\u0557\u0f37\xadF\xbdaC\x97\xa3\x8c\xe125d\u04ad\x90\xfb?\x9f\xb2QR\xdf\xf22\x1c\xbd\xfdg\xa7\xea\xc8\xcb\x16\x0e\xa5\x14f\u03dd\x8b\x8f\xdf\xf0n,\u83e0\x9d{e7Z_g!\xf7\x90f\xf2h\x89\\\x84Zh:p\xcdQ\u0737\u075e\xe7g\xc3\xf8\v\x89\x81\x80\xcaF\x8f\xd41\a \x94\xd1]=\xdf\xf16\x90\x80J\xd0[\u0144\x96\xd2h%\x06\x8960i\xf7\xe7\x89hR\xf4\xc1\xea(\x9c\x96\xc2\xc0:\xfc\xb9\xc56\"\x85\u0555}x[u\x17*Q\x82$\xb4\x8d\x02k\r\x11U\xd1\x1c9\x82]\xeb\xee\xc5\xc4\xe0\x1e\x90\b\\\xa1\xb2\x1d\xa5\x90\x01\x16\x9c{\x05\x06N?\u07fe+e \rA\x86UT\xfaf\x81a0\xb8\xfd\x15\xec\xd9\xf2\f\xc6G\xf7CBXO\x16\xe7\u007f\u4670\xa3\x8c\x8a\x98\r;}\xaf\xf7)\xb8\x9e\xed;\"\uee8b\x9atDZ(\xd4\xe5\xa3j\u01ddJ\x83\xa6\x8d\xa3x:\x98\u02a4\x10Jz\xdc\xe1\xabx\xd3y\x1c\xb2\x9cU\x16\x9cE\xf0%q\x1dq<\x81v\x034f\xc6\xec\u06730\u007f\xfe|0p\xca\xfe\xc1\xc1\x15'%\xcc\xf2w\u007f\xf7\xb7\x11\x00\x9cy\xe6\x19\x8f.[\xb6\f\xb6\x86\x11\xfb\xc6Q\x87\xf6\xec\xc4\xf3\u03ff\x88Fl d\xd0&\b\xe1\xd2\xc1\xd4[]\xd0\x1b\xf5:V\x9f\xba\x18\x9f\xb9\xf1}\xe8\xed\xa9adtlZ\x01\x14~\x10s\x19\x9c\xe2\x17\xdd\xf6\xe7v{\xbd\xa9:\xf5n\x98\xbaOO\xfc\xf0\a\u078dw\xad=\x17\xbaU\u03feF\xd3=\x9e\x85y\x11\xb51\x02\x90m[\t\xc7&\xe3'\x0f~\x9d\xee\xf3E\xa1\x88\x13\xb4#\xfc\t/\x98\xba\x9b\x11\x17M\x8d\xd2vl\xfc\xc9)\x00\x13\xa5\xd0J4b\xcdP\xa6\xe8]\x9e{\x85pV\u0233L\x06\a\a\xe4\x9f\x03\x01i\x9c\x1a\x1c\u009ap?\"RPL \x19\x02`\xecy\xea>\fn|\x06$\x84\x83\x0f8\vH\u8677\x18K.{/*3\xe7@\xc7-\x04\bP\xad\xf5\xa3\xda7\x80\xc3{7c\xddm\u007f\x8cG\xbe\xf3\xdf\xf1\xcc=\u007f\x81W\x1e\xf8&\x0e\xee\u0610\xf9\x9bP\xfa\x9e:'\u02c5\x93g\u00d7)\xd3\xf90g\xc1B\xc5\xed\x19O\x1f\x18+37\u0387\xa2\xed\xab\x03\x95\xee\xf4\n\xbf\xde0\xd8\x1bhZh\xcb\x0e\x94\xdd\x06\x06B\n\xab\x1b\x91\x00\x02\xfb|f\x86\xce1\x00\xb7x1\x94N\xa0un\xbf\xe0\xdfS\xbd\xbd}\b\x82\x10\u0198\xc0\x18\xb3\xf8d,\xe6\xc1\x19g\x9c\xae\x00\xe0\u04df\xfe\xe5\xd7\u05ed{\xfc\x95\x81\x81\x81s\x87\x86\x86\xb5\x10\"\xf0\xb9\u044f>\xfa\x04\xae{\u05d58g\xc5B\x98\xc6h\xc7\xcdM\xc7\xd1\xd1\x1d\xed\xf6=\xbd\xd0T\x92\xe0\xfaw^\x867\xb6\xec\xc07\xfe\xe5.\u0109\xea\x1a\xbe\xdc\xceR)+\xc8e\xf0J{`\xc7d]\xf7d\xde\xe6e\xbc\xf5\xf4\xf3+\xaf\xb8\x14\xb7\xfc\xe2\a\xd0\x1b0\x92\x89\xb8\xa4\x93\x9a^\x05M3\x1f\xb9\xacu\xe5Ni\xfdQ/\xba\u053dw+{rj1\xe2\x17x\xea\u03a1(_@\u0494\x9f6V\x8f(\xbc\x0f\xb2\xb1kJ#I4\x942\xce\xf8/}\xae\xb7#1\xb9\xb29+\xdc\u0739\xb0\xd8\xee\x92Q\x81\xc22\x1c\xc6(j\xd8&\x17\x82\xc3\x1a\x06\xd7?\x86\xed\x0f\xdf\x01\xd5\x18w\xb0\x8e\xce~JFU,\xbd\xecz\xccY}\x1e\x8cJ\x10\xf5\xf4\xa3\xda\n\x11\x1f\x1e\u0096\xf5wc\xfdO\xbe\x81\x83\xdb\xd7C\x06\x15\x04\x95\x1e\x1c\u067b\tZ\u0168\xbd{.f\xcd\\\x04\xc4-\x97\r\xea\x18\xd7e\x91vm\xc3\xc2\x02\x15\x9b\vzN\x97\xf9Yf\xa4\xc0%M\xb9\xe7\x8cE\xed{8*\xc1\xdcK5\xa4\xb9\xf5\x1e\u5db4\fc\xbf!\xad\xb9\x98\x88\b\x81\x04\xa4\x84\v\x87\xb7_W*\x8fLd'\xf6\x82\xb1\xdd:\t\x99\xbd\x96/\xf3\x17\x82\xd0l6\xa1\xb4\x02\x119\u01d8\x93\xb03\u007f\xc7;\xae5\x1f\xfa\xd0\r\x92\x88\xde\\\xbdz\xf5\xed\xa7\x9d\xb6\nJ%$\x84(\u063an}\xf5U<\xfe\xf4Kh*\x03\xc8\xc8c\xb1\x14/$?\xaa\xeb-\x85\\\xc8\x0eDf\xd4*\xf8\xfc/\u007f\f\x1f\xff\xe0\xb5\x16\xcb\x14\x02A \u074a/\xa6\xd5A\x97\r?\xa7\xd3}\xd34(\x91\x9d\x03R*\x18\xef_|\xd1\xf9\xf8\x83/\xfc\x1aV.\x99\x87\xa41\x81\"1\xef(\x8e\x87o2W\xb2\x9aNWV?9\xccB]Xp\xd4uqH\xa5\xfd\xb2\x00\xa1\x14\xa5\xfe<\xe5\xfa\xc1\x93\xa2\xea\x86\x19\xb1\xeb\xc8\x13m\xd0NXJ\xc3s\xb4\xb1\xf4S\x93u\xe6^A7\\$\x8b8\x88J\u00a0\x0f-\xac\xa0\x83X\x1c51\xba\xf3\rl\xba\xf7\u007fcl\xdf\x0e\x80lt\x19\u0600\x84]\xac\xe7\x9f{9N\xbd\xe6C\bzf \xa8\xf6@\xd7'\xb0\xfd\x99\x1f\xe2\xfe\xff\xf5y<\xf0w\xbf\x89\xfd[\x9e\x87V\tT\xdc@c\xf4\x10\x86\xf7m\xc1\xf6\x97\xeeG\xfd\xf0V\xf4\xf5VQ\r\x05\xa2\xc0\xfa\x10Q\x19w\\x\xe1q\x18\xd1\xfd\x00\x00 \x00IDAT\xd7\x1eP\x9e\x96\xaf\xa2-.\x95br\xdc\xd1aSgW\xc0m\u06c2\x12\xdf\x00\x9f\x19\xd6\x16\x14W0@3\x86\xf3\xfd\x18\x01\x14\x10\xc2~\x89hN\x80\xb0*\x10HB \ba@\x90!\x15h9\xe9.\x93\x89\x11\xd5z\x11Vz\xbc\xeb\xdc\xcd!H`hx\b\x87\x0f\x1d\x063\u01f5j\xed\xe0IY\xcc\x01\xe0\xf3\x9f\xff\x1c\x03\xc0\x97\xbe\xf4\xfb\xff\xb0`\xc1\x82W\xe7\u035b'\xb5\xd6I\x8e\x9d\x03\x80\xc6c\x8f?\x8d\xbdG&\x10Uk@\u6652\u039a\xf3<A\xf1\x16\x16\xf4\xc2n\x93\b\x8dF\x03\xf3\xe7\xcc\u0117~\xe3S\xf8\xc8{\xdf\x01\xad5\x946\b\x83\xa0\xc0\x97'J\x83e\xb9k!\xf7\xc39\xa6b\xaf\xf8,\x98\xc9\xf0\xf9\x82\x0f\xb3\x10\b\x03\x99\xdd\x17\x97^|\x1e\xfe\xe8\xf7~\x1dg\xaf>\x05\x8d\xd1!\x90N\n\f\xa1\xa39\x16>\x83\x8f\xa9[\x97{l\xbb t \xa3\x9dI\xa2\x9d\xc8k9y\x90\xc0^h\fO\xb9\xab+[\u0532m<\x01\x86\xad\xaa3Nlr\x931~\xa2\xbbU\x19\xb2a\xe8\xf4\x1c\xc1\xb3d5\x1e\u0312\x0eA}\xa6Qv\xae\x193\"\u00bc\xe6~\xec{\xe4v\x1c\u07ba\x11B\x06\xd9@\u03fe\x9e\xc6\xccS\xd6`\xcd\an\xc1\xc0i\xe7\u00a8\x18\a6<\x85\x17n\xfd\n\x1e\xfb\xdb\xdf\u014e\xf5\x0f\xd9\u008f\xd4w[g\u007feR\x1fA%\x14\xa8\xd4$d\b\x04\x92\x10\n\x82\xf4\xb8\xe4)\xcf=KXB.2b?j\u03c3\xda\v;\x9aB\xc25M\r\xb3L\x87\xcd@\xe5\v.\xda\x03\xa6\xdd{c\x00$\t\x95\x9aD83\x80\x9c\x19@\xce\b@\x91\xb5\xb0\x15\x82 #\x01\x11\n\x8b\x97S\xbe\xa34l\x10\xf5\xf5\xa3\xda7\xdb\t\x8cR3\x17\xfbs\xf5\x89:\xb6n\u074aV\xb3\u065a=k\xd6\xf0I[\xcc\xdf\xfb\xde\xf7\x9b\x83\a\u007f\x06\"\xdaw\xdey\xe7\u07fex\xf1\"\x18c\x82\xf6\u0405W_Y\x8f\xf5ol\x87\x96\x15\xb0\x88\n\x81\xc3\xfe\xb0\xe6X\xb1\xd9c\x83]\x18\x13\xe3\x138e\xe1\\\xfc\xe1\x17o\xc1-7}\x00RJ\xb4Z\xb1\u06c2\t\xaf\xf0\xa2t\x88\x99\x16\xe70\f2FL\xfa\x9ct\bZV\u0627\x1b\x19GD\b\x83\x00Q\x18@\x1b\x03!%\xde\xf7\vW\xe2\xff\xfa\xaf\xff\t\x17\x9d\xb9\x1c\xad\xf1\x11\xc0$v\x11\xe4\xe3\xc8\xfa,\xe1\x06w\u04d3L\a\x06\xa3\xd2\r\xb6/\xe4!\xb4k4\u065b\xa4\x14\xf0m?K\x93\xa6\u0388\x9d\f\xbeI_[k\xb6<\xf2\xc4@i\xce\nq\xdaYg\x10\x8a\xd7yk\xb6\u0676\x99\xba7\xed\u0239\x04KwEM\xca\x00\xa1\x14\xd8\xf9\xe2:\xecy\xfe\xe1\u0316\u0546Y\x18\xb0V\xe8_r\x1aN\xff\xe8\xe70c\xc9*\xecy\xe6\x01\xfc\xec{\u007f\x8d\xe7\xfe\xd7\x1f\xe0\u0347nCc\xfcH\xe1\x04\xa5@Xz\x8d\xac\xb9\xecz,Ys!\x94j\x81\xa2\x00\x14\x10\x84\x04Bi=\xd2\xf3\x18{r\xd0\x05\x15l\xe3\v\xd4\xe0\x8ek\xa0\x18p\u0285E\xb6\u06d5Q\xb6\x9cs\xd7U\x9e\xdb!3\x8f\xff\x9d\x82\xf9i4]\x10\x10\xc2^\tY\xb5\xa1\xe5\xa8\x12\xb8O\x80+\xb6\xe0KA\bB\x02\x059L\xc3l\a\xcf2\xac\xa0\u007f\xeeR\x04Q\u0545h\xa7\xf5\\@%\t6l\u0600\xf1\xf1\xf1\x04@|Rb\xe6\xe9'\xf3\xe6\x9d\r\x00\xf8\xf2\x97\xbf\xfc\u05ef\xbc\xb2\xfeS\a\x0e\f\xae:x\xf0 \v!\xc8hmW\xc7\xc68\xee\u007f\xe0a\\\xfe\xf6\xf30/\xea\x81i$n\xa8E\xe0\xe3\b\x1cFigw\x14\xc396\x18\x9b\x98\xc0\xbc9\xb3\xf0\a\xff\xf9f,\\0\a\xff\xf8\xdd{\xb1\xef\x80\xddm\x05\x81\x8df\xb3\xa2!\xd3u\x90g;;sTC\xce2\x1c<\xe5\x8e\vB\xb6c\xd1Z#I\f\xe6\x0e\xcc\u00a7>\xfa^\xdcr\xd3\xf5X0w6\xc6\xc7\xc6\xf2N\x9c\x8fn\xd2\xd0\xce5\xeff\x99;\tb\xda\xf5\x98\x97\ty\xca;u?\x8a-\x85[\x8aT\xc0b\x01\xe0i_\v(\x81s\x00\x02\x1b\x03e\f\x942P\x86a<\xd6\n\xbcn.\xeb\xb8\xfd\xee\u06dd\u007fk\x16\x87,t\xc5\xd7\xe5\x14\xf1X\x81J%\xc4+?\u06c8\xc7~\xf2c4\xc6G\x10\xc8 \v\xb0\x00\x00Y\xa9\"\xac\xf5\xe1\xf0\x1b/a\xd7\x13?\u0111-\xaf`\xfc\xc0.\xdb};\xac\x97\xbd\xa0\x8a\xec\x98\n\x81\xb7\xff\xe2o\xe0\x1d\x9f\xfdo\xe8\ud7cf\xf8\xd0((\xb0v\xbd\xc4\x02\x82\r\x02\x10X[H(u\xea\xed\xd8y\x15`\x95\xfco\xf1=\xc8;\x05}\xdcf`E\xe8n\xb0P\xb6?\x9b\xfc\xdc1\xe5\xda\x02\xe3\xbar!\x80\xa0B\x10\xbd\x02$`=\xd0\xc1@\x85l\xe6\xed\x98\x01\x9a\x1a$\x800 \xb0\x01\xb4rW\x98]\x851g\xe9\x19\xa8\xf6\xcd\xc2\xd8\u1f50A\xba\xc6\x11\x12f\xbc\xfe\xc6&l\u07f1#:\xf3\x8c\u04eb'mg\x0e\x00_\xfb\xda\u07e4\xc5\xe9\xc85\xd7\\\xfd\xed\u014b\x17AJA\x00Xx\xfc\xedg\x1f_\x87\xa7\x9e\xdf\b\x84\x15@\xe4~-\xbe\a>\xb7\xc1q\xddvle\xf9\x90|\f\x85,\x1d\xfc\x8d\x8fO \x8a$>\xfb\x89\x1b\xf0\xe7_\xfe\x1d\\\u007f\ud568U+PJ\xbb\xf0e{s\n\xf7\xc6|(\x85\x99\v\xde,\x19\xde\xea\xbc]\xcaR\x84\xca:\xf0J\x14\xa1\xbf\xaf\x17\xbd\xb5\n\xc2@\xba\"n\xe5\xfc\x97_|\x0e\xfe\xe7\x1f\xfd\x16~\xe7\xd7n\xc4\xfc\x81\x99\x18\x1f\x9b\xc8\xfbL\xe6)C\xe1\u0288\r\x93A)e\x86\xa74\xc5\xebLVP\xc9y\xae\xf8\xc3\u0254f\xa8\xb3\xef\x14\u007f\xdex\xbd4\x1d\x85B\xb8\xdb{\xd3\xc6 q\xb0J\xa2\f\x8c6\xc5\x01&\x17\xab\x9d\x0f\xa1\xd8N\x1c\x9e\xab\xa4\xbdn\r\xdb@\x04\xbf\x90\xb3[\x84\xab\x95\n\xb6\xed\u0703{\xee\xfd\t\xf6\xee\u0647@\x06 \xe3(\x8c\x9c\xefE\x87\xb7\xbf\x8a\xd7\xef\xfa\u007f\xb1\u3c7b1\xbe\u007f\xa7\xed\xf0\xb5\x82Nb\xdbE\xb6\x1d\u0359\x8b\x97\xe1#\u007f\xf2\x0fx\xdf\u007f\xfd\v\xcc^\xb1\x14\xaa\xd2\x02\u034d\x80>\t\xaaJ\xa0\"\xc0!A\b@z\xd3^r\x01\xcb\xd4n\x8a\xc5\xc5\x15=\x8dQ\xeb6-\xe1\u0098\xbam\xc8\xc9\u076e\x94\xac\x15n[`\u06f6\x87\xd9\xf6\x8b\xac\r\x88\xf3\x9e\t#B8#\x80\xac\x884\xe7)_mC\x02f\x04\xe0\x1e\t!\tR\x10\"ae\xfaLn\x80\xaa\rf/X\x89\xdeY\v\xb3\xc8>\x9b\xf4d\x11\x84\x83\a\x0f\xe2\xd1G\xd7U\x01\xcc:\x19\x8byV\xa5\xef\xbb\xef>\xe4\x9f\xff\xf8\xf9\xe7\x9e{\xe6\x86\u077b\xf7\u032f\xd7\xeb$\x84p\x1d\f\xa0[M(\n\xb0\xf6\xca\xcb\xd1\x1b\x00\xa4Z\xb9\n\x14>\u038cLxT&\xbb\xe6)\x98\f\u01c2\xa5\x83\bIb\xb7_+O]\x8aK/>\x0f+\x97/\xc5\xd8\xc4\x04\x8e\f\x8d\xa2\x15\xe7\x9e\xe4\x16O\x9fj/P\xc4\xcdE\xdaq\xb7a\xf1A \x11\x06!zjUDA\x00\xa5\x12L\u051bH\x94F\x10\x068c\xf5r\xfc\xc6\xcd\x1f\xc1o\xdd\xf21\\|\xee\xe9H\x92\x04\x8df\xcb.*<=4{2\xfa\xe7\xd1`\xe2t\x14\xc7sRn{\x16\xea-\x9c\x8f9\n\xe5\xc1\xef\u0667\xc2\u01e7\xfb~\x94f$Z#Ql\x87\x99\x1e\x94\xd2\x0eo\x15\x98*\xe9\x87\xc9\v\b\x15v]T0\xb8\xb1\x9d%\xa1Z\xaba\xff\xc1#\xf8\xce]\x0f`\xc3k\x9b!\x88\xa0\xb3\xc5\u0763X\x1a\r\xa3\x14H\xda\x0e\xd3\x18\x03\x9d\xb42\x8c\xdc\u007f\x84=3p\xce{o\xc2\xf5\xbf\xff\xe78\xfb\xba\x0f\x00\x14 \x9e\xa8[Ec(l!\x0f\xc8S\xef\xe7<x\xe3\xe9\x06:\xf4><\xd5\xe2\xdc\u05a3\xfb\xdc\xc7\u051d\xd3\xf7\x04\xa0N\x02{\xd1\x1e\x82\u0684`y1OU\xb6 \x82\x11\xd6\a'\f\x80\xea\x8c\x00\xb2O\u61da\xdbV[\x01P(l=\xf1h\x9a\xc6\xc1a BT\xe9\xc1\xc8\xc1\x9d8\xb8s\xa3\x1b<\xcbl\b\x9a$1\x94J\xa25kV?}\xeb7\xbf\xf9\x12\x00l\xf8\xd9\x06|\xfdo\xbf~r\xc1,\x00\xf0\xcf\xff\xfcO\xf8\xccgn\x01\x11\x8d\xddy\xe7\xbf\xfd\xfe\xe6\xcd[~8::\x9aejjG\xd3{\xee\xc9'p\xff\xe3\xd7\xe2c\xef\xba\x18\x11\xd5!\xd8\xf6h\x06V\x19\xea|z\\\u03ae\xbd\xf0L!\xee\xc4\xcfvo\xff\na\xf2h\x94\x92A\x98\xf7oA\x04\xc3\x1a\xcdF\x1ds\x06f\xe1c7\xbc\x1b\xef\xb8\xe2b<\xfd\xdc+\xf8\xc1\x8f\x1f\xc1\xc67\xb6\xe0\u0411a\xc4q\x92]\xb3\xa1sS\u0303c\xadC\x9b\x10\x02\x81\xb47g\x10H\x84a\x80@J0\x03\xcdV\vq\xac\x1c\xceg\xcbV\xa3i\vx\x14\x04X\xb0`.N_u*\xde}\xf5\xa5x\xe7\x15\x17\xe2\xd4Es!\x880Qo@+\x93\xfb\xb1\x1cC!o\xff&s\xf7\x8ez\xba\f\x16\x9e\xe2y\x9d\xb4F\xfb_\xe1\x06\xe1\x94\xf5\xe5m\xbcc\x86\xe7\x13s\xec\xf0\x9b\xd2\x06q\xa2\xa1t^\u023b\xc17&c\xaaX\x9a[\xb6\x8bB\x1e\x05\xc7\xce>\x04\x19\x14\x96\xe3\u0646\x81 \xac`\xdf\xc1a|\xef\xae\a\xf0\xcc\v\xafd\xe5Li\x95u\x85\xfe\xae\x10\xb0\xbb\x04\xad\x94\xe5B\x1b\xed\"\xcel\xd1\uf67b\x10\xa7]\xf5>\\\xf8\xe1_\u018a\xf3\u05e2\xda\x17A55t\x12\u06f8\xb8\xb4\xb0I\x003$\x10\x11DU\xc0\x8c\v\u0206\x06\xb7\fLb\xe07\xf9T\xb0\x9f\xf5\x8ac[t\\\xc7}\xd7\xee\x91\xce\x00\x89\xa2V\x81\xb8\x1b\x82\xce\x1e1\xb1Dn\xeae\x86\u06a2.\x11\bF\xb5F\x90\xfdd\xff>\xc3\x1d\x17Zv<%\x03}\xd2>o\x9c\xed.\x84\xac2\xd7h\x8d \x8c\xb0\xec\xac+\xb1m\xfd\x03\x18\x1f\xda\x0f\xe9\u03ad\x10\x84\xb8\xd9\xc4\xe0\xe0 ~\xfa\xf0#\xb7\x00\xb8\x15\x00\xce9\xfb\x9c\x93\xaf3\a\x80;\xef\xbc\v\x9f\xfe\xf4\xa7\xe8\x95W6\xe0{\xdf\xfb\xfe\xe6\x9bo\xbe\xf9\x8a\u077bw\xaf\x1a\x19\x19q~\xdb\xf6\xc0\xa9V\x03\a\x87\xc7q\xee\x05\x17`\xc1\xac^h\x15\xbb\x9c\x14\xeeX\xa9\x05\xa5\x1d\x1c\xa6t\x05i/\x06G\xd3E\x16;t\xbb\x93h%\x1a-C\xe8\xef\xeb\xc1\x19\xabW\xe0\x1dW\\\x84\v\xcf9\x03\xf3\xe7\x0e\xa0Z\x89\x10\x86\x12Ji4\\\x1a\x11{\x16\x9c\x96yb\v\xba\x10\xd6\x14\x89\xd9 I\x14Zq\x828I\x10+\x05\xad\xed\xb6?\b\x02,\x987\ag\xaeY\x89\xf7\\{\x05>\xfb\xc9\x0f\xe3W?\xf9!\\v\u1658\xdd\u07cb$\x8e\xd1h\xb62\x95 xz\x1e\xe54I\xa7\u0716\xa8\xd5q\x1c\u02e4\xf1S\x15\xf1\xc9\xf6+T\xb6s\xc9(\x87T\n\xed\x1co!\x87W\xc8\x13e\xa0M\x1b\xe5\xd3\xfd7E\xc7\xd8%c\xa5\xb4C\x98\u0734)\x03\fr\x97\xd7\xccy/\x9f\xd9\x11\u00a8\x82##u\xdcv\xefCX\xf7\xf4K0ly\xeb\x16.3\x05\xb0\"\xdd\xea\x1b\xada\x8c\v\x84v\x1d#\xb3\x81\x88jX~\xcd\rx\xe7o\xfd\x9f\xb8\xf23_\xc0\xa2\xd3V@\n\x89\xa4\x11\xdb\u039d\xa8\xb0\x9b\xc9p\xfb\x80@U\x01\x11\u065d\xa0$B@\x94u\xab\x85\xf9J!\u05c1\xa6X\xa2i\x92\xe99O~\xc6\u067f\u6f06\u0311\xde-\u00db`\xd2\u0667$ \x14\xa8V\tQ\xbf\x00E\xe4\xe6\f>\xc3\xc6g\x0f9\xf5\xa8 P(\xec\xb9L\x87\xdb\xee7J)Q\xe9\xed\u01e1]\x1b12h\xe9\xa1\xf6\xfc\x11 \x88\xe3V\x93\x84\x103\u05ad{\u426f\xfe\xf9Ww\x01\xc0\x0f\xee\xbe\v\xdf\xfd\xcewO\xae\xce\x1c\x00n\xbe\xf9f\xfe\u05b7\xfe\x15\x00\xf0\xb1\x8f}\xf4\xcb\x1b7\xbe\xbafdd\xe8\xd4\xf1\xf1\t\x04\x1c qE\xef\x8dg\x9f\xc4\xf7\xff\xed^,\xfd\xf5\x9b03\xac\xa2\x157\xec\x1c\x03\xbap\xf2\r\x8a'?s='\x1f\x9f+\u0798\xdd:\xef)\a\xa1)\xae\u02e9\u007f\x8cF\xac\x12\x8c\xaa\x04\x95\x00\xa8U+\xb8\xf4\xa2sq\xe9\xc5\xe7ahx\x14[\xb7\xed\u0126\xad;\xb1u\xc7\x1e\xec\u06bd\x17\x87\x8e\fatl\x02\x13\x13\r\x8c\xd7\x1bh\xb5b(\x13g7\x8f\x10\x840\fQ\xabV1\xaf\xaf\a\x03\xb3\xfb10{&\x16\u039b\x8b\xe5\xcb\x16\xe3\xf4\xd5+p\xc6i\xcb1o\xee\x1c\xabtT1Z\xcd&\x94\xd6Y 62\xc6\xca\xe4\u007fYG!\xa7\xa2\xa0\x8f<:0g\xdd9\x15\xc6ZeCOB\x99@f\xfa\n\xcc\xf4sS\xe2\f\x95E{u\x89\x8c;\x96G\xa2\r\xe2\xd8 q\x1dy\n\x9f\x14\xf8\x17\xe9V\xdc}O\xbb\xffS\xc7\xd5d\x1c\xd4R\x98\x9e\x17p\xdeZ\xa5\x82\xfd\x87\x86q\xfb\xbd\x0fc\u0753/d3\x14\xed\xf2_\x89\xf2\xe0\u2b10\x1b\r\xa3\x13h\xad\xb2\xfc\\f\x8d\xfeS\xcf\xc4\x19\xef\xff4.\xfd\xc5\xff\x84\x85K\xe7\x80\x12 \x99HlxEV\xccr;Z\xff\x9cQ\u0298\xe9\x11\x90\x01A\x84\x04\x1eW \xc1\xe0\x86\x81J\xf2\xa2^\x9e\xa1\xcd%g\x87\xbao\xb9\fw\xf2\x89\xdb\x02*\x88\xbaL\xb5\xc8\x19\x1e\x93o\xfae\xb3R\xab\x91@\xb5_\x02\xbd\x1aF%\x85\xd0\xed,!(C}(\xd7K\x10@\xbd\x02\xa4%\xc8%/13\x14+D\xb5~\x9cr\u0595\u063b\xe99\xa8\xb8\t#\x04\x041\xa4\x1046>\x81}\xfb\xf6\r|\xeb[\xffz#\x80'\x00\xe0C7|\xf8\xe4\x83Y\x00\x9b\xccq\xc7\x1d\xb7\xd3G?\xfaq\xbe\xf0\u008b\x9f\xfc\xf6\xb7\xff\xe5\xbf\x11\xe1\xdb\x0f=\xf4S0\xc0ZI2\x8e_\xfb\xc0=\xf7\xe0\xfc\xb3V\xe1\x86k\xd7\xc2\xc4\t\x88\x15$\x11B\xe8L\x05\xa6\u0754\xd5\x06t\v7\xec\xcb?'\U000b71b9Y\x97\x1f\x14>]@\"\u007f\xbeM\x92\xb1#\xb9\x04\t$\x94\x02\x8cj\"q\x98w\u007fo\x15\x97\x9cw&.\xbf\xf8\x1c\x18\xadq\xe4\xc80\x06\x0f\x1d\xc6\xd0\xe8\x18\xc6\xc6\xea\x18\x19\x9f\xc0D\xbd\x89X\x99lK/\xa5@\xb5RAoo/f\xce\xe8\xc1\xbc\x81Y\x98;g\x16\xe6\xce\x19@\x14\x06H\x94\x82N\x12$\x8d:\xd8(\x10k\x14\x93QyJ\u018ao\xc91\x15\xbd\x93K\t0\x9d\x0e\xe8\x93\xcd*\xcaX-\x8c\xa9\xb8\xdf\xe5\xfb\xa9N\\\x9f\x8f\xbf\x90;\x8f\x15m\x8a>+\xec\r\u074d[\xe5\xd8+\xea\xe9n\xb0 i\u271cW`\u007f\x00\x10$Q\xadF844\x8a\xef\xde\xfd \x1e}\xeaE\x18c \x85\xcc\x02\xb7\x995X\xbbW%\x8b\xe3\x1a60*\xb1\x96\xc5\xeeQ\u96cde\xef\xfc(N\u007f\u07e7p\xfa\u06ef\u00ac\xde\x00z\"v\x05\x93;\xa9(\xdc~ls:!\x81\x80\x88\x80Y\x04\x84\x04!\x05$'\xd0F\x83U\xdb\u0312\xf3\xb3GGA'`\ued01!t\xb2f\xca2~\xb3\xb6\xc4\x17$\x11\xa0% #BuF\x88\xa0\x9f\xa0\xc1\x80&KQ4\\\xec\u0385\xb0Ceo\xdea\xbb'\x80\xfa\x02H\xc3\xc0\xb8\x86\xd66\xf4\x03\x82\xb0`\xf9y\x985\u007f\xb9\xc3\u0383\xec\xaf\x0ed\xc0\a\x0e\f\xd2\xd3\xcf<\xb3\x96\x99{\x89h\xe2\x99g\x9f\xa6K\u07fe\x96O\xbab\x0e\x00g\x9ey\x06\xff\xcd\xdf\xfc5\xfd\xf6o\u007f\x81?\xf1\x89O}w\u01ce\x9d\x17\xef\u0739\xf3w7m\xdaDA\x18\x98VK\v\x00\x18?\xbc\x1fw\xdc\xf9C\x9c}\xe6j\x9c\xbeh&Z\xe3#\b\x1c\x13 \xed\x92\xd1\xfd\xb0\x02\xd7\xd1\x11\x15\xbe\u05ae+$.z\xefOBq\x9d\xa4(rF[\x94\x99\xb0\u0240\xb4\xb6)\xe0\u0292Qc\x87\xeb\xcf\xec\x8d00s)\x02!!\xa4\x04\x89<\x88\u00f8\xad\xb0v]\x85a\x86QvK\xad\xb5F\u0728\xa3U\xf7L\xf5\xc9\xce\x0e:\xcb\xeb\u051d*\x95\u031e|\xeb\x84BG\x0e\x94\xda'\x91\x1757\x99\a\x13w\xe9\u0727\xc2\xdd\xfd\b\xba\xf6\xe1X\x99\xac\xe8\x98\n\xb9J\v\xb9v\x18\xb9\x1d`\x1a\x8f\a\x9e\x0f=\xdb\xff>G\x93\xe4\x1c\n!/\u07cc\xbc\x95\x92a\x17\xf7j-\xc2\xc8\xe8\x04n\xff\xe1#x\xf4\xa9\x97\xa0\x8d\x81\x14\"\u03cf\xd5\x1a:i\x02\xb0.\x9d\xa9\xb1\x96\x0f\xf9\xf4\r,\xc6)\xe7\xbf\vK\xdf\xf9a,\xb9\xfc]\x98?o6\xaa&\x86i6\x8b\u0175=z\x8d\\0\xb2\xdf\u9ca7\xaa5\xb0.\x8f}\x01H\n\xbb;PV\xcd*\xdc\\\xa0]\xb2\xcf\x05\x1f\xfb\x829BfN\xc5\x1e\xbe\xcep\\u\xea\f\x15\xa4.\xe6l\xedWX\xca}\u05c2@\x01\xa1\xd6\x1b \x9c\x19\x80\xab\x04\x8auf[\x9b*XS\xc8\xd1\xe6'pg\xdc)\xac\xc8H\xf6\x05\x16F\x9b\xb0;\\\x9d\xc4\u86fd\x10\xa7\x9c\xb1\x16\awnty\xad\x96\xa5&\x03I\x83\x83\ay||\xe2\u04bb~p\xe7\xaf\x03\xf8\xab\x17^xA\xb8\xbe\xf2d,\xe6g\x03\x00\xdf\u007f\xff\x8f\x03\"R\xcc\xfc\x95#\x87\x0f_1>>~\xe9\xb6\xed; \x85\x84v\x93\xfa\xf5\xcf=\x8b\xbb\xef{\x18\x9f\xbf\xe5\xe3\xa8Uj\xe0\xa4\x01C\x021\v\b\x17#f\x03~\xdd\xc5I\x1e\xc7\xd7\x15wIE\x8e:Q\x9b\xf7\x86\xe7?Q\xec:i\x12\x94\x8f3\x9f\x88\x00\xda+7E\xdd;9\v\xce8\xd6\xe08\xbf6\v\xc6U~\a\x82b\xcee\xfb\xfb\x05\xf9\xdd :n\x88\xc9\x17\x1f*\xfa\x17qI\xa2\x10\x8a\x05\x9e\x8fb\x91\xa3i\xfe\x9bJn_.-\xf2\\p\xe9\xa0c\u0499vB>\x896\x88U\u0691[\u06a0q\xaaM\xe31\x1c&\x03\x17l\x01\xf7<N\xb2_\"\n\u007f\xb7 B\xb5Z\xc1\xb0+\xe4\x8f<\xf5R\u0591\xb31PJ#\x8ec\x18\xd5\xc4\u0095g\xa1w\xe1\x1aL\x1c>\x80\xe6\xd8\x11\xb4&\x86\xc1\u0318\xbf\xf2\x02,\xbf\xf0:\xcc=\xedB\xd4V\xaeFu\xe9<\xf4\x06\t*I\u074a\xee|\xba\t\xba\x0f&\x88\xda\xf6T\xfe\xf7\x8d\xbb\xcej\x02\x8c\x10\xb1b\x18V\x88Z61\x89\x8d\xdf\xf8\xe4\x05\xddU\xe9\x8e3\x96z\xb6p\xfb\xc04+\xe8e\x9e\x94\x93\xc08\xc2\xdel\xc6\xf9\xafT{\x02Tf\x05@\xcd\xd2\x10!\x03\x18\xa1@Bg0hv\x93\t\xb8@haC+\x90[2\b\x02(\x12P}\x12Z\x19\xc8&\u00e8\x18Q\xb5\aKN\xbf\x04\x9b\x9f\xbb\x17c\xc3\a]XE\xba\x83\x96j\u01ce\x9d\xe1\x03\x0f<x9\x80\xbf\xfa\xcd\xcf\xffg\xbdc\xc76:\xf5\xd4\x15|\xd2\x15\xf3\xf4\xf1\xeew\xbfW}\xf1\x8b_\x14D4\xbai\xd3\ubffcs\u05ee\xfb\xeb\xf5\u01b2\xfd\xfb\x0f\xc0\x187\xa9W-\xdcu\xfb\x1dX\xb5r9>x\xedZ\b0t\xdct|]\x01\x05'*r\u05f2L\v;Q\xdeC\xb1\x87\xc0\x12Cp\xfb\x8d\u0649\x01s\x89/7\x95l\xffDv;{\x8aEo\xf9/\xa4\xbb\xa4-qfND\x9eq\x90\xc9::fn\xebx\x8a\xfd,\xe5\xab\xd64q\xfe|P\xdcm\aRz{\x95\xee\xa4;\x91\xe2\u92b1:\xed\x93\xfc$ .,h\xd4\xf1\f\x00\xc7\u0451\xa73\x81D\x19\xb4\x94\xe3\x90;h\xc5/\xe4\xc6\xf8\xe8\xbc\xd7\x15\xb2\xb7\x843w:*\xb6\x1fC7\x03\xa9U+8ph\x18\xb7\xff\xe8\x11<\xf2\xf4\xcbH\x94\x82\x94\xc2v\x80Z#Nb4\xeb#Xu\xd6y\xb8\xe2\x96?Fm\xfeyH&F\xa1\x9bu$\xad&d\xb5\x8a\xdeY\xf3Q\x9d5\x0fI_\x15I\xb5\x85\xaa\x19C\x0fKH):\xad\xc2\u0476\xe5\xccL\xc9\v\x88\xb9-\xec%\x97\x97\x00 #\x01\xd5\x1fX\x99\u3a36A\xc9\xcc\u0747\"]\xc06.1\xcf\u028b?\xb5\xdda\u0735=`\u07cf\\\x10\u00aa@uf\x00\u0457\x97\x17!\x05D\x108K\\\x06\x9c\x101SQ\v\x91N5\x1c>\x96\x0f7\x04\x11\u008aD2\x03`N`Z\f\x95\u0118\xb3h5\x96\xae\xb9\x04?{\xf2N\x04a\x04\xc3\fA\x84\xa8R\tv\xee\u0705\u077b\xf7\\\xf7\u063aG\xae\xbe\xfa\xaaw<v\xff\xfd\xf7\a\x00\x92\x93\x86\xcdR\xf6\xf8\xc1\x0f\xee\xc0W\xbf\xfa?\xf1\xb5\xaf\xfd?\x87\xff\xe6o\xfe\xaa\xbee\u02d6\xf7\xee\u06f7O\x1a \x9bL\xb7&\xc6\xf0\xfa\xe6\xadX\xb6b%N[~\x8a\x1bt\xe4C\x1e\x86\r\u007fV$2 NQ\bC\x12\x02\x8c\x904\xa4\xbbx\x05\x8a\"#\xe6\xe9a\xe5\xd3\x19\u06b5s<hj\xba\x06:\x8d>\x8b\xa51\xeb\xbb\t\x8e\xdf\xd5\xd9\xc1\x94\xe1\x91\xe9\xff\x05\xca\xe7N\xfe\x93\x89<\u04e5\xb6\xf8\xb4no\xfehL\xb5\xa8\u02f3}\xc0\xc4x\x8e1\xe5\xf5\xe2\xf8#\x03\x13\xa5\xd1Lre\xa7\xf6?<c\xac|~\xde\xe9r\xc9m\uad54\xa1\x92fEZGB;\x9b\x89\xa2\bo\xee\u0707o\xddy?\x1ey\xe6e$\x89r\xbe)\xec<~4\xe2f\x1d+W\x9d\x86w\xfd\xca\x1fa\xf6\xeak!\x83>\xf4\xf4\xccF\xdf\xecE\x981\xef\x14\u0318\xb3\b\xb2\u0683\x96L\xa0k1\xfa\xfb$\xfa#im]SC\xac\xec\xb2)\x02\\\xcc\xe4\xe5^\xe6\uf66d\xd9K\xa6\x1a%\xf8\x0ev\fA@\x18\n\xe8\x90\x10K\xb7P\xb9\xc0d\U000b72e5K:\xa1d\x85ikh\xdaH.\x1d@Z\xfagP\xceZqL\x96\xa0G\xa26\x10\"\x9a\x1dX\xde}\xda\x12e\x17\xb1\xcf\x1a\xa3\x9csN\u0536fPA\xab\"\x84\x1d\x02+\x02X1\x90$\xe8\x9b9\a\x9c\xb4\xb0\xeb\x8dg\xa1\xb5r|s\xfbbI\xa2LT\tjc\xa3\xa3;\x9e}\xf6\xb9G\xef\xb9\xe7\xde\x13\x1dO\xfc\xef\xee1\xa5U\xe4\x82\x05\x8b\xf9\v_\xf8m\x00\xc0\xb5\xd7^\xf7\x8d\xab\xaf\xbe\xfa\xcf\xce?\xff<\xf4\xf6\xd4 \x82 +]\xfb\xb6m\xc1\u05fe\xf6u\xac\u007fu3\xaa\xbd}\x99:\x94\x19\x99BP\xb1\x84\xce\xf0\x03\xcbK\x97\x04\x04\x94\x1761\x9d\x81_\xc9\xd73\x93\xfb6\x1de\xc1\\n\x92B\xceSa\xf3T\x9c\xcar\xbb\xae\xb1\x8de\xd1\u0669\x96'\xe8LZ\xc81\xb9\u043a\x13\xbb\xc41\x16T.\xed\xda\xd2\x1c\xa9\xf4\xfc\xa5\xf9\x9d\xa6C.\xc2\xc7\xc5\\a\xb0\xed\u0213\x9cKn\x8c\u0166\xd3b\x9e\x1ac\x15\xfe>\xceY<\u0145Ix\xb517\x81\xb3X\xb1\xd5\x15T\xa2\n~\xb6i\x1bn\xfd\u07bdx\xf2\xf9\r0\xc6r\x95\xad(\u0206p\xb7\x1a\x13X\xb1r9\xae\xff\xe5/a\xe6\xcak\xd0\x18m\xc04&\xa0\x9a\x13\x88\x1bc\x88\xeb\xa3h\x8c\x0f\xa3a\xea\xe0\x19\x84\xfe\xfe\x10\xbd\x01\xb9\x05\x9a\xba\x9c\xb0\x9cuC~\xd7\xec\xfcGT\x12C\xb5\x9aH\x9a\r\xa8f\x03\xaa\u0544\x8acOIj_\xbb&\tsj\x12\xfd\x03\x01\xc4\xfc\x10jV\x80$\x12.\u06d4\u06ba\x1c.\x1d\xccPG\xf4\x1by\x83T*\xccl(\x03\x17\xdb\x1ayw\xc32\u0641g43@8+\x00\x82\xb4\xb1\xf1\x82\u02e5\x84\fB\x9b\x89 \x05\x84\x14\x16/\xf7S\xc1<\xf1\x96\xbf\xf4\t\x00\x91 \u022a\x04\xf5I\x84U\t21\x16/?\a\x8b\x96\x9f\x03\x157\xad%\x80\xdb\xc1\x85QH;\xb6\xef\u0091\xc3C\x1fd\x93\xac8\x190s9\x9d'\xfd\x97\xff\xf2\xbb\xb8\xed\xb6\xdb\x01\x00\x0f>\xf8\xe0\xcb\xeb_~\xe9\xc2\xc1\xc1\x03\xab\xea\xf5\x06i\xe6\xec\";28\x88m;\xf7b\xd5i+\xb1t\xd1B$Jyy\xb2\xe4\x8a5#\x00[\xb8\x85\x01I\x8c\x88L\xae\xb0\xf4\v3O\xd98w\xed\u01cfv\x19\x9e\nO\xee\xfe[\xf2\xff\v\x0f\x1b\x17(Wm\n\xbf\x1b\xa7\xeeE\x1c]\n\xbd\xefg\xcdDG\xfd\xbe\xa7*\xe59D+\xa1!]G.Jv64\xfd\xf0\x8ci\f;[\xb1\x85V\xb4\xb6]\xb86)[\xa4\xcd>\x98\xba\x9f\x8d\\L\xe8\r\xff2\x15\xb2\x84!\x89\u0219\xa9=\xf9\xc2\xcf\xf0/w\u070f\u05f7\xec\x00\t{\xedi'\xfaI\x92\x04q\xab\x893\xcf:\x03\xd7\xdf\xfc\x05TW^\x87\u047a\x85\b\xadq\x9b],\f\x01\xaa\"@\xb3\x03\u0318i\v\xb9\xec8 y\x81J'\v\xa9}\x80Q\n&I\xa0\xe2\x16T\u0482j5\xa1\xe3\x16t\x12\xc3\x18eM\xbc\xd2\x04#cR\x16v\xa6\xb4\x0e\x88P\x91\x02\x95H@\xf6Hk\u0125\xd3E\x8f<7[\xcam\n\xfcXXj\u03dc+\x0e\xec\v$\x84\x0e\x88\x8f<\x81\x90\xb5\xae\x8df\x85\b\xe7\x86\b\xaa\xd2m6J\xae,!,W\xde\xcb\a-t\\\xdc%\r\x89r\xd6M\x10\tDB\x00\xcd\x043\xfa\xe6b\xec\xf0^\xec\xda\xf2\x02\xac\xed\x88=\x97B\x10)\x95`\u018c\xbe\u015b7o\xfe\xfe\xbau\x8f\xef\xfa\xbb\xbf\xff:\xdd{\xef\x0fO\xeeb~\xdbm\xb7\xe3\x87?\xbc\a\xdf\xfe\xf6w\xf0\x95\xaf|\xa5\xf1\x8do|\xe3\xf9\xd1\xd1\xd1_\u077bgOPo4\x9cT\u06b27\xf6\xed\u074b\xdd{\a\xf1\xb6\xb3\xd6`\xe1\x82yH\xe2\x96\xdf+\xd9\x13\ufdb9\x12\x8c\xc0\xf5\xea\xe9\x10\x91\xdb:m\xfawx\u040aB\x1e\xbf\xb8qG\xca\x0eP\x12\x14C\xdd!$\x97\x96\xd5Q\xb4:\xdc\x0e3\t\xf6\x89)\xaa\xdc\xe6\xc3\xd1D\x88:\"\x00\u0085x\x1b\b\xb2;)ry\xaf\xe2x\xcd\xd5\x18\x88\xb5\xed\xc8\x13\xa53.y\x86\x8f\xa7|\xee.\u02d4\u007f\xdb3S\x86\u074a\\q`U\x88$`H\"\xa8\xf4@\x1b\x8d\a\x1f{\x16\u07fe\xf3\x01\xec\xde\u007f\xd0F\x02\xc2\xc2*Zk4\x1a\r\x04\x02X{\xd5\x15\xb8\xf6\x93\xbf\x05Zr\x15\x86\xc6\x00i\xf2\x80m\x12\x02,\x04tU@\f\x84\xe8\xeb\x0f\xd1\x13\tH'+\xb7]\xa6/\xcfLC\x8d\x9dB4\x8eaZM\xa8f\xd3v\u07ad\x16t\u0702Q\xca\x1ax\xb1K\xaf\xef\xf0\xff\xc9}|\xb2\x8e\x16\x80$B\x14\nD5\x89 \xb2\v\xaf-\xea\x1e\xba\xc3\u014e\x82D\x97\xe5\x9f\U000ee768[\xa3\x90c\xe5F\x10\xa4\x04\xc2\xfe\x10r^\x84\xa07\xc8]\x1c\xcb`Ka\x8fQjM\x9c\xedR\xb2\x85\x9a\xbc\xd0\xe7\xce\xf4\x1bA\x96\"L\x02\xe0\x86F\xc0\x01X+\xec\u0776\x1e\xf5\xb1#\b\x82\xa8x\xcf\x18\x8dE\v\x17\xecz\xfe\xf9\x17\x1e\xfdy.\xe4\u04c2Y\xd2\xc7\xfb\xdf\xffA|\xff\xfbVEu\xdey\xe7\xbfv\xd3M7~\xee\xdak\u07e5\xe7\u03db\x870\x8a \x830{\xee\xd3O=\x83\xbf\xfc\xdb\u007f\u00ae\xfd\x87\xd17\xa3/\xe3\x05\xa7%\u0278N/e\xb6h&hCP\u01a9\xc9\x18\xa5,\n\xfewV\xd0\xf3\xa8Fg@E\x9c\xdd\u0113\xc1F\xecc\xbfmE|\xda\xed3\xbb\x1b\x86\xf9h\xeb\xe7\xa4\xff\xb69\x9d\x12\x89]r\x11@!\x84\x86$\x03\xc9\xda\x0e\xab\x19 6\xc7~F\xdc)\x8e\x95F3Vh\xc5\x1a\xb1b\xa8\x94On\xfc\xce\xcd+\xd8\xe9U\x94\x168\xcf \xcb\xc0\xa7(z\x16\xb1L`\x92\x88\xaa5\x8c\x8eM\xe0\xce\x1f=\x8c\xef\xfe\xe0\x01\x1c8x\x04R\bh\xa3\xa1\x12\x858NP\x1f\x1f\u01cc\x9e*>x\xe3\xc7\xf0\v\xb7\xfc\x1e\x9as\u07ceC\xc3\x1a\x82\xb5\x85\x05\xb5\x82V1\x94\x8e\xa1B\x86\x98\x15\xa2\xb7?@o(\xac\x02\x9a\x19\xac\xadD_\xb5b\u8e05\xa4\xd9@\u04a8#\x99\x98@k|\x1c\xf1\xf8\x18T}\x1c\xaaY\x87\x8a\x9b\xd0q\fVy\x17n\xb4\xe5\xb2\x1bcla\xd7\x1aFi\x98D\xd9N\xde}h\xa5\xdc1r\u05c2\x01d@\xa8\xcc\x0eP\x9b\x1f!\x9a\x1d\x02\xa1\xc8!\x17\x81\x82\x01\x96\x95\u06a7\x17\x9d\xef\xf5\xc2\x05\b\xab\xfc\xbe\xe3\fb\t\x04\x10\xf5H\x88\x81\x10\xb2W\xe6\v\xfc$X)\ta\xe3\xf7\x84\xc8\xe1\x15\xe1\xcf6\x04r\xae\xbc{\xa1t`\xe2\u4ed6o\xceh\xc5u\xcc]z\x06\xe6-Y\x03\xad\x93\x82\xbf\x0e\x91\xc0\xe1#C\x18\x1b\x1b\xff\u022e]\xdb\x17\xd8\x19\xe0\x9d\xf4\xf3Z\xcc\xe5\xd1<\xf9\xb6\xdbn\xc7K/=/\xfe\xfe\xef\xbf\xc1\xb7\xde\xfa\x8f\xeb\xbf\xfa\xd5?\xdf\xd6l6o\u063f\u007f\xbfh4\x9a\f\"2\x8e_\xbdm\xdbv\x8c\x8eO\xe0\xfc\xf3\xcfE\u007f\u007f\x1ft\x12\x03\xccH\xed\x99S\x87\xba\xb4\x935>\u036e,\f\xe2\x18\xa0\x92\xff_Z\xf5\x927CS-\bS\x04y\xf04\x82\xd2\t\xc7\xfe\x1cn\x1bz\x02V\xf8UC\xe2\x86\u04e6\xb0(\x1d\xaf9\x9a1\x8c8\xd1h\u0116K\xae\x8d\x1bxj\x93+e\u0459\xf1\x99\xee\xb5\xdb\xf9\xd1\xfe\xf85\xb7(qL$!\x11Vj\u0631\xef \xee\xb8\xfb~\xfct\u0773\xa87\x1a\x10B Q\x89\xa5\x1e&\t\x92V\x8cU\u02d7\xe0\xc37\u007f\x1a\xa7]w3\xf6%K0>\x9c \x022X\x85a`\xc00! g\n\xd4z\tU\x18\xc0\xc9\xf8-d\x92v\xda1t\xabe!\x948\xceqo\xa5l\xc16\xd6\x05\xd0?\xb9\xd46%\xf1\xfd_\n,\x1dW\xf4\x84\xb4\xc6o\xe9,\x87\xdc\n&\"\x01\x8a\x84\x9d;\xb4l\x97O\xa9\xaa\xd2?\xd7\xee\xa4R\x99\u027d\xc7\xc7\xcf\u0198~(6\x01R\x12\u008a\x00\xe6D\bf\x87\b\x04\xd96\x8d&\xbf\x963\xa7R\xe3\\\x0f\xb9\xe8\x1a\u019eL\x97\n\x9d\\\xaa\xae5@\u02c0[\f\u048c0\xea\xc5\xc8\xe1\xbd\x18\u0739\xd1y\xe2Pv\xce\xc0\x06Q\x14.\u073dg\xf7\xb7\x9ez\xf2\xa9\x03\xdf\xfd\xee\xf7~n;\xf3\xe0h\u007f\xe0\x82\v.6\xeb\xd6=*\xae\xba\xea\x1a\xf3\xcew^\xfb\xad\u035b^o\xd5j\xd5[\xef\xbc\xeb\xee\u0783\x87\x0e\x19\x80\x85R6\xe0\xf6\xae\x1f\xfc\b$\x04\xbe\xf8\x9b\xbf\x82\xc5sf\xa0>6j\xbb)\x12`\x92\bX\xb7\t`\xc8R\x00\x8b\xd6\r\xedb\xbd\xa3\xc2~\x8f\xb7\bN\xf5\xbb\xd2 a\xff\x8by\xacW\xc1\x9e\xaf V!tr\xc6A\x1d\xf7S\xe9\x0e\xe5h\ni7\x1e~Y\ta\"\xc8\xcc{$\xdd\xd2\u04d4\u01fe\x9d>\u0775\x88\xb3\xed\xbe\x13e}s,Fn\xa9j\xda\xf0\xa4|\x98\xe2\x0e\xadS\xaa\xc8m\xef\xc00C:\xc8\xe3\u535b\xf0o\xf7>\x84\xd7\xde\u0612m\xd5ce\xdd5\x8d\xb1\xa6gW\\\xf1v\\w\xe3/\xc1\xac\xb8\f\xdbFfB\x0f7QqT70\xdb\x10b6@@\x88f\x00\x95\x9aB\xa04r\xe1g\x8aU;\xf8\xc0\x15\xaa4\x9e\xae}H\x9c\xa6!Y& e\x05\xb2\xa8\x9c\xf5\xf8\xfe\xc6\xd22\x89\xb5\x15\x10\xb1\x00sP\x84\xb9\U0008a360W\xa2**\x80\x00\xf4\x91\x04\x1csg\u01b7A!g3\xe3\x98\x17|_\xa835\x0e\x80\f\x04\u0090@\xb3\x02\xd0@\x88 @F)\xb6?\xc3]\x9b\x12\x9b8$ \x03\xeb\vo\x8c\xce\x17*a\xbfg\xb7[\x94\xef\xc0\xd8W\xde\x02H\f\xb4\xd2H\f\xc0\x94`\xe9\ua2f1\xe9\xc5\xfbph\xef\x16\x84Q\xd5\xd5\x0f\x01\xa5\x15\xf6\xee\u06c7\xbe\u07be\xcf\x02\xf8\xe2\xcf3\xcc\x12\x1c\xcb\x0f]u\xd55\xe6\x81\a~\"\xae\xbb\xee=f\xf5\x9a3\xbe\xff\u66dbxdx\xf4\xd6\x1f\xddw_\u07e81LD\x94$\t\xc0\x8c;\xef\xbc\x17:\x89\xf1{\xbf\xf5+8u\xf1|\x8c\x8f\x8eBi\xe3\x06E\xe4.t\x82\xc86\xd3%\xfa\xc4\x13\xd8rwS=\x1eMa/\n\x88r\xbc\x9c=\f\xc1\xff\x1c\x9e\xe5g7\x1b$\u007f\xc03\xd9`\xf4X\xf8U\x93\xcb\xf49\xf3\u02a1\xb6R\x99)t\xb9\xfb\xb1\xe2i.\xa6\u06b0\r\\N\x95\x9d\xcaX\xba\xa1\xc9\u04c1\xdam\x1b\n\xdcv\xcfG\x9e2\xbadn!L\x9e\xaa\x98\x19\bC\x89@\x06x\xf2\x85\x9f\xe1\xbbw?\x84\x9d\xbb\xf6@Jk\xbf\x1a+;TTI\x82(\fp\xf5UW\xe2\u068f\u0708#\x8b/\xc4\xd6\xe1*\u0091\x16*\xdad;G\x8b\xef\x02\x88\x80\xa0_ \xecaH\x9d\xc0$\xbeN\x92\xb3\xf3\xc7\xe9\xcfx\xe2\xa6\xe2\xf1\xe2\xdc\u007f\xa4M\xb0S\xb0,\u02c4c\x9c\x15Hr\x8b\x06\x1b\xdb\u0676\ufb6dw\x8c}\x05Y\x13\xa8\u032f\xc0\x04\x02\xeaP\f\u0772\xb1z\xd4\x01\x99g\xb9\x9d\x00\x00 \x00IDAT\u0561\xcde\x8d\v\x17\x1f\u00df\xd83d@\b\x02\x82\xe8\x91\x10s\"\u020a\x00\x99\xa2\x81\\g!\xf7--\x9c\x96CJ\xabh\u054e+C\x9c\rk\x99\xa8p\xbc\bN0\x06\x02\fC\u0156\xc2j\x1b$\x8d\x81E+\xd1?\xb0\x04\x87\xf6lvL\xa1\xdc\x1a\xa0>\xd1\xc0\xc1\x83\a\xdf\xcd\xccU\"j\x1e8\xb0\a\v\x16,9y1\xf3\xf6\xc7u\u05fd\u01e4\xf8\u04eaUkn\xbb\xfe}\xef\xf9\xb5\xcb/\xbb\f\xfd3fP\x14\x85\x1c\x85Qv\x83\xdd}\xef\xfd\xf8\xef\u007f\xf2Wx\xfc\xb9\r\x88\xaaU\xf4\xd4*\x8e\xb5b\xe9n\x86rq\x05\xa1\x18\x12\x9c}\xdd\x13\xa6\xd1\t(\xe8e]*\x97\f\x1a\xdb?LF\u0663\u008e\xc2d\xd1\xc5\xc5!g\xa1\"S\x113o\x87\xbc\xa9=\n\xac\xa3H\xf21\x17r`\n\xf9\a\xb3g\x00V\xb2s\x98\xe45'\x9bg\xa4B\xa0fK\xa1\xd92h\xc6\x06J[\\<\vW\xce\f\br\xd6\au\x8c\u073c\u063e<E\xad\xe0:\x98\x0e\xa0\xab\x95\x10\x81\x94x\xec\x99\xf5\xf8\u07f7\xfd\b\xdbw\xee\x86p\xdd^\x92X\x98#\x89c\xf4\xf5\xf6\xe2C7\xbc\x0f\xd7\u007f\xf2\x16\x1c\\p\x016\x8dU \xea\n\x91\xd6)\xa5\xdb\x05ZXH\x84\x04CT\ud015\x95\xce1m\xf7\u007f\xa4\xec\x13\xe73\xa23\x9e\xb8W\xac9\x0f\xd3((\xfa\xb9\x9d\x86\xea\xd9C\xf8\x17J\x1b\x90]\u0282\xca\n5\x10V\x05\u00b9!*\xf3\"D5\t!QN\xdd\x12(\x1a\x03!\xa7\xfa\x16\xba?I\b%AT\x04\u011c\x10\xb27p\xb5\xdf\xebV\n\xd7>\xb9B\xdef\x95\xcb6\x81I\x04\x01HH\xbb\xdb\xc8\xdc+\xc9\xfe\x1bT\\W\xd86}F\x19$\xb1\x816\xe9\xa2n \xa2\bs\x97\xacAT\xed\xb5\xe7\xcb->\x82$\x12\xa5\xb1c\u01ce\xd5/\xbf\xfc\xdc\"\x00?\x97\x85\xfc\xa81\xf3\xf6\xc7'?\xf9\t\x9cw\xdey\xf4\xe0\x83\x0f\xe1\xb6\xdb\xfem\xe3\xef\xfc\xce\x17\x16\f\r\r_\xb2\xff\xc0~\xeb\a'\x88\x8c\x1b\\\xec\u0735\a\u03fd\xf8\n\xa4\x108u\xd9R\xf4\xf7\xcf@\xa2\x8d-\x8c\f\xebW\xec\xd6\x16Q\xd8bR\xe6'\x91]s\xe2\xc4LC\xb9C\xe5V\xd6\x05\xe7\xfc\x94n\xae#^)j+?m\xc0\x01\xb5\xd5\xf5\x92\xd0\xf3n\x90E\x19\x0419\x0e>\x05V\x8f\x12\x96\r&g\xc5\xf0tw\b\xee\x05\x94\xb6\xd8x3\xd1V\xd5\xe9\xe4\xf9:\xf3W!\xcf\x12*\xff\x1c(\b\x11\xbb,b\xe4\xf9k;j\xa8 \xd4j\x154[1\x1eX\xf7,n\xbb\xe7\xa7\xd8?x\x18a\x10\x80\rCi\r\xa3\r\x1a\x8d&\xe6\x0e\xcc\xc6\xc7o\xfc\x10.\xb9\xfe\xa3\xd8Z[\x89\xad\xcd\x1a\x82\x98QmiH\xe5e4\xa4\xb9\xa0\xc4@\x00\x04\x11l\x82|\xa1\x9b\xf6\xbc*\t\x85\xecQ\x12T`\xc1\b\x19 \x8c\xaa\b\xabU\x04a\x04\x19F\x96\x1dS\x18(S\xc7\xf5\x05\xe1\x162a}\x83D !\x82\x10\xe4\xaa3y'\xc5\x17\xf8\b\x06D@\xa0\xaa-\x98B\xc3v\xd1\xde.\x87\xfc\v\x92\xca\aA$l7\x1e\x04\x02\"\"\xc89!\x829\x91\vW\xf6\xad\b\xbc\x06\xbc\xf4J\xf1\x96\xea,\xe5\u02e4\xc9!E]\x95?\xe8N\xcf\x03\x80V]C7\x8c\xfd\xdb\\\ve\xed\x174v\xbd\xf1,\x9a\x8dQ\u0220\x92\x89\x88\xb4V`c\xc4\xf2\xe5\u02df\xbd\xfb\xee{6\xfc\xc7\x00\xb4\xe4\xf1\x9d\xef|\x17\xd7_\xff^\xdc\u007f\xff\x03\x00\x80{\xee\xb9\xf7\u01fbw\xef8\xad\xd1h\x9es\xe8\xd0!J/n\xe3\x9c\xe6FG\xc7\xf0\xcc\v\x1bp`\xf0\b\x16\u031f\x83\xc5\v\xe6B\b\x91\x19)\x19\x88\xccMQ\x90_j\u06f8\xcd\x1e'\xfdXg\x95\u9ad9l\xfe\u07aet\xa4\x82\xea\u0447W|\x9b0\xf2\x1c@\xba{\xb1{e\x8a&\xe7\xb4w\xe88\xa6\xb9\xbb8\xdeY\xc0t\x06\xa8\xd3}h\xc3H\xb4\xb1~\xf2\x0e\x1b\xb7B\xa04\x00\xa4\u0762\x81\vXr\x0e\xd9z\xdb|\xcf\x11\u0424\xb0K\xba \b\x81 \bP\xadD\u0635\xef \xfe\xed\x87\x0f\xe3\xc7\x0f?\x8d\x91\xd1q\b\x17.\x92R\x0f\x9b\xcd&\x96.\x9c\x87O\xdc\xfcK8\xe3\x1d\x1f\xc0\xab\xb4\x14{\x92*\xa4aT\x9a\x1aA\x92\x17\x12\xf2\xa8\x91l)<\b\x88!B\x80d\xfe\x9e\n\xe7/\x1d\xbe\x91\xb0\"\x19ae\xecA\x18BV\xaa0I\x82\x03\x9b7`\xcb\x13\xf7c\ubccf\xe2\xe0\xd67 \x84D\xcf\xcc\x01\x17f\xe1\x0f3\xbdE\x9e,\xbf\u074aml!\x97A\xe0\f\xe1<\x0e\xbe\xa7\xbb\xf1wz\x90\x04\xaaX\x93.\xa1\x1c^\x9e\x0e5\x99\x8ab8\xa2B\x87.$\x10\x84\xc2\x05-\x13\xc4\xec\x10r~\x04\n(\x17\x06Q\xb7\x96\xa1\xfb\xfe\x90<og6\xa6\xc3\x054\v\xd8\xd66\xf8Ci\x83FK\xc344(\xb1\xb3\t\xc1v\b*E\x88\xa8Z\xc3\xf6W\x9f\xc0\xf8\xd0\x01\x04\x0e\x19 AH\x94B%\nq\xca\u04a5\xad\xc7\u05ad\xbb\x03\x00\x1e\u007fb\x1dn\xbd\xf5\x1f\xff\x033\xf7\x1f\xbf\xf7{_\xcaC\x8c\x8943\xffZ\x18F\xa8\xd5j\x9f|\xfe\xf9\xe7\xc1c\xe3\xa0\n\xa1\u0572\xc1\f\xcdf\x13w\xdc\xf3\x13l\u067a\x1d\x9f\xfa\xd8\xfb\xf0\x9e_\xb8\x123f\xf4\xa11Q\xb7\xb44\x10b\x04\b\u0666JJX\xf9rf\xa2\xe2\x17\xf1\x94\x9b~\f\x86S\xbe;E\xea\f\x93 @\x02\x89\xc0\xc9e\u049e\xdc\u03ef\xcc\x13\xae\x18y\x881w\xc0\x12T\xc2\x12\xc9\xddh\xca;\\\xc6$\x98\xfa4\x8b/\x9f\xa0\x01\xef\xb1\x14q\xc3\f\xe5(\x86\x896\u06403\x93\xe2\x1b/&\x82\xbcA\x1fS\xc6\x15G\xc1\b\n\xb9!\x13\xc1\xfb~>\xf8\f\x02\x89(\n\xa1\x94\xc2\xd3/\xbd\x8a{\x1f|\x12\xafm\xden}\u0209\xa0\x13e\xe9\x87J#n\xb5\xb0r\xd9R\xfc\xe2\xa7n\u0092\xb5\xef\xc1\xcb\xf1\\\x1cIB\x04\xc2 L\fd\xe2\x16\x88\x94\x1dbl\x04\x0fI\x17\x15(\x81\x80%\x02- \xc2 \xf7\x01/`\u0294\xc1G\xa9\xc531P\x1f>\x82]\xaf<\x8b\x9d/=\x81\xc17_\xc5\xf8\xa1\xfdPI\x82JO\x1f\xf6\x9ey>.\xb8\xe1\xd3Xx\xfa\xb9\x801\xd9\f\x83\xdb|\xe13\x1e\xb6\x83%\v\x1eZ\x93\f\x1e\x01\x17\x06\x1d\x10\u012c\xd0r\xf1\x87\x12P\xc3\xc2]>|\xc5\xf6D\xe6l \xb2\x85<\f\\JP_\x00\x9a\x17\x82B\xe1<T\xba\x14r/0\x8cPf\u06563[D\x10\xc0h\r2\xc6QI\x1d\x1f]\b04\f\xacG}#6\xd0-\x8dP1\xda\x13\xbb\xb5\xd2\b\xc3>\xcc]\xb4\x1a\x83\xbb^\x831\x1a\"\xc8\u0565q\x92`\u04d6\xcd\x17\xa6\xbf\xfb\xca+\xae\xfa\x8f\x01h\xe9\x85\xe2,5\x17/^@D\xd4`\xe6\xdf\xe8\xef\x9f1X\xadV\xbf\xb8a\xc3\xcfp\xe0\xc0~03\xc7IB\xa9\xb8\u854do`\xcf\xde\x03x\xed\xcd\x1d\xf8\u055b?\x86\x95K\x17\"N\x12\xa8$\xb1\x1d\x1d\vH6\b\xa1\x10\xb0C\xa9\xa9\x8bS\"\x15g/G\xa7\xfcd'V\"w@4\x82\f\x15/w;\x14\x05.H\xb1|\xa6\xaf\xa3=\x01t*d)\xf3\xf6\xe7\xe3(\x9e'\xa2\x90\xd3q\x16\xf0\x94q\x94\xe6s*\u05c5\xa7TC\u035c\x8bC\u041ejFY\xdef\x16\x15\xe4\vm\xbcX7\xf6\xd8\x1a\xe4\xb6\xfd\xd5J\x15a \xb1{\xffA<\xf8\xf8\xf3x\xf8\u0257p\xe8\xc8\b\x84 \x186P\x89\u016e\x95\xd2\xd0*\xc6Y\xa7\xaf\xc0G~\xf1&\xcc=\xf7j\xbc\u051c\x8b!\x15 $\x03\xd2\f\xd1\xd46\x04\x81$d\xe4\xe0\x8bD\x83\xb5\x82\x86\x82Jb\x98\xa6\x027\x19aS@$\x01\xa8\" D`c\xcd\x18PI\x8c\xe6\xd8\b&\x0e\x0f\xa21z\x18I\xa3\x0e\x93\xc4\x18\x1d\u070b}o\xac\xc7\xd0\xeemh\x8d\x8dX\xf5fda\x16\xad\x12\xec}\xf5E\xcc^\xba\x02\xb3\x97\x9d\x86\xb0R\xb5\xf8\xbb\u020b\xac\xf0\xf8\xd7$\x85\x85K\xb2,Z\xce\x06\x9fS^\x1bi\x82\xd1\xcc\xc0\xed<\x14$3\xa0\xbdE6\ud4055\xf4\xaa\x046\\\x19\x82\x80\x1eKCDE\xe4\ucb2eq'\xf0\x98F\xdc\xe5\x9at\u0443BB\x04\x12l\xa4m\f\xd9X\xdfsw\\\x8d\x01Z\x9a\xd1L\f\xa2\x84!\r{a\x16\xee}$\n$\x02,8\xe5llz\xe9'\xd0*\x81\x91\x01\x04\bA\x10bl\xbc\x8e]\xbbv/x\xe1\xc5\u7bbb\xe8\xc2K\x1e\xf8\x0f6\xcb\x14\x05\xfd\u99df\xc4\u06b5\x97\x83\x88\xc6\x00\xfc\xce\x13O\xac{\xfa\xae\xbb~\xf0\u01cf=\xf6\u061am\u06f6\xd3\xe8\u0628VJ\t\xa54\x81\x19\x87\x87\x86\xf1O\xffz\a\xf6\xef?\x88\xcf\xfc\xd2\r8e\xf1\x02\xf4\xd5\"\xf4\xf7\xf7C1\xa1^o 1\x12\xa1\x90\b\x9chEd\xae\x8a\xdc\xe9}\\t\x01\x9a\xd4\u00ef\xddN\x97\x00DPmE\x9a\xba\xbe\xceT_K\x87\xa2>\xf7\xc5\xef,i\x1a\x05\xf2h\xa0\x93N\xaa!\x1f\xd7b0mHE[HE)\xceB$\xb4\xb6\x18\xa7f.\xaa\\\x19\x1dA\x1d\xa9\xbc\x9d<\u0715\xbc\x00\x8aB2\x8d\u00e1\x830@%\n161\x81\x176l\xc2=\x0f<\x81\u05f6\xec\x80R\x1a\x950\xb0\xbf\u07e4\u0656\x84\x9e\x9e\x1a\xce9\xfbB\xbc\xf7\xa3\x1fEe\xf5Z\xac\xaf\xcf\xc0\xa8\x91\b\xa5\x01\x91@\x90\x00\x91\b!\x03\x03\xd5j\xa01v\x18qs\x1c\xcd\xd1\u00d8\x18:\x80\x89\xe1\xfd\x98\x18\u068f\xb81\x0e\u05b1\x85*\"\x89\xa0VEP\xadA\b\x89\xa4\xd5Bsl\b\x8d\x91!\xc4\xf51$-+\b2*\xb1>+I\vB\x06\x90a\xe82?M\x16\x1bW\x1f>\x8c\xc3;\u07c4j5\x11\xd5zm\x97\x9aA6\xb6C\xb5B\x1b\x99\xbfF\x10\xa2`\x14=\xcd\u055b\x00\x88P\x00\xb3C@\x10\xe4\b\x01\xb1\x014\xa0\x13\x83\xc4\x15_)\b\x95\x80\x10Fv\xf1@E\x80g\x05@U\x80\x8d\v\x9dqP(qq\xb8\xef\x13\x00\xda=8\x19E\xd6V\x16\xb0.\x03\x18\xa9AZ[k\xect7G\x84D\x03\xf5\x96\x86I\f(6Y\x8a\x14\f@\x9a\x9dy\x9a\x86\x90U,Xr&z\xfa\x060:\xb4/\xfbE6\xec9\xc1\xf0\xf0\xe8\xec\x87\x1e|\xe8b\x00\xffQ\u0327z\xac]{9\xa7\x90\v\x00\\q\xc5U\xdfc\xe6\x9f\xfc\u065f\xfd\xd9o>\xf1\xc4\xe3_\u06bcy\u04ec]\xbb\xf7\xa2\xd5j\xb1J\x12JO\xe4\x8f\x1fZ\x87\x177\xbc\x8a\xd3V,\xc3\xcae\x8b\xf1\xb6\xd3O\xc3\x05\u7781\xd3O[\x0e&\x89\xb1\x89:\x9aZC\x92\x84dv>\xe9\xd6\xf6\x899\x87.\x98\x8f\x06\x05\xee\xe2nX\xb2%\u0325\xc9\xdc\x01\x9f\xf8\xd1\x1aT\xd2\xf5\x17Cp\xbb\x8f\x0e\xdb\xe1\x96\xf6\xa7uc\xa2\xd0q\x94d\x9a\xe6B\u046d\xf0+m\xe9a6\x9b\xb3]\x8a\xdf)\x1fL-\x00\f\xa7v\r\x94\xb3=\x88\x8b\x8e\x87(H\x80\x00\x00Q\x18 \x8aB\x8c\xd5\x1bxu\xe3\x0e<\xb8\xee9<\xf1\xfc\x06\f\x8f\x8c#\f$\x82@Bi\xe3\x15\x16F$\x03,;u\x19.\xba\xe6]\x98\x98\xb3\x06/\xef\xd7ha\x02\xa1\xb4\xe97Fk\xa8\x91\t4\x8e\x8c\xa21<\x88\xe1}\xdbpx\xcf\xeb\x988\xb2\x17qs\fI\xab\x01\x1d7]\xb6g\xee\xfc\x92\x05-\xa4CP\xa3-\x17\u06a7\xf39\xae9\tB\xa5\xa77\x17\x1f\xa52}\xad\x11\xd7\xc7\x11\xf5\xf4a\xf1\x19\xe7\xa3\xda7#\x17\xbdd3I\xab\xd4$\x87\x93\xa7CSHYlb&=Y\xd4I1\n\bbf`\v\uaa06hh\u02002\xbb\xe1\x00\xb0\t?\x86\x81\x8a\x80\x98\x19\x80{\x84\x9bW\xf8\xd7iz\x8fP\x9b\xf6\"]\x9c\xfd\xa6\xc8\u3d34[\x05\basS\xc9*\u008d\a\u02754#\xd1@\xa8l\xf1\xce\xe6\x02i4`F\xff4\xe8\x9d1\a\x03\vWal\xf8@\xfeur\r\x03\t\xec\u077b\xef\x1d\x00\xfe\x14\x00\x9e|\xf2q\\~\xf9\x95?7\xc5\xfc-\x13M\xfe\xe3?~\x93~\xe5W~5\xbb\x95\x1fz\xe8\xfew\xdf}\xf7\xbd\u007f\xf2\xf4\xd3O\xbf}\u02d67111\x818I`t\xb9\x99\xd9Y\xa7\xaf\xc4\a\xdes\r.\xbd\xe8<\xbcm\xcdJT\xab\x15\x8c7Z\x88\x95-\xe6\x15$n\\\n\x87zs\xdb\x14\xfd\xc4<\xba\xb1(\xb8\xf0yqPk\xda\xfa\xe2\xb43\x17\x05^\xcc4@}/v\xfd\xe8f\x01\u04c7X\x8eZh\u0140r\xcc\x10\xad<{Z\xe7p\x98\xfa\xa9\x982\x17\xc9\xff\x8f\xbd7\r\xb2\xe4:\xaf\xc4\xcew\xef\xcd\u0337\xd5^\xd5\xfb\n\x10\r\x80X\tb\xe3Np\x81(B\x94DK3\x1cihY\xd684\xe3P\xcc(\x183\xa6\x15\xe1\t\u02e3\x88\xb14\x94\x1c\x1e\x85\xc2\x16I{,\xc7\f\x15Z\fQ\x96\xc4m8\xdcA\x90\x00\xb1/\xc4\xd6@7\xba\xbb\x1a\xbd\xd6\xfa\xf6\x97y\xef\xe7\x1f\xf7f\xe6\xcd|\xf9\xaa\x01nSptF\x14\xd0\xdd\xf5\xeaU\xe6{\xf9\xce\xfd\xee\xf9\xcew\x0e{\xbe;\xec\xf1\xe1\xa9\xee\x98\xf3+M\xfd\u0749\b\x81\xab\xc4/\xaem\xe2\xd9\x17O\xe2\u0467\x9f\u01c3\x8f=\x83\xd3g.\xd8\u079ep\u0563\xbfj\xc0r\xf4J\n\xcc\xce\u0361\xb9\xb4\a\xa3\xda,X\xd5\x10F\x01\x94\x8a\xec4\xe7h\x04\xbd\xb9\x81\xde\xca9\xf4\xdb\x17\x10\x0f{\xcez\x16\xde|@\xb1kM$\n\x00U\x90(ybs.v\xf3\n/z\x12[/\x96\x1dW\xbe\x11o\xfa\xb9\xff\x12ox\xeb\xddPa\xe4\x85<\xbb\xa6\xa7\x14\x10A\x00\x15FPadm3\x84\u020a\x18P\x15oWV\xb5\xe7Z}\u00dc\u075f\xd6\xdf\xc4\x00\xe7G\xa0N\x02c\x80\x81{OC\x82\x95 \x06\x04\x9a\r@\x8b\x01\x8c\xb4 \xcdd\xfd`R\n_PQ\x1b\x9fs\xe5<\xa6h\xc9*\xf3\n\x9fw\xa3\x13\xc4\xfd>\xe2Q\xec\xb2U\r\xfa\xfd\x18\xeb\xed>\x92~\x8c\xa8\x1bC%\x06R\x00\x01\x03\xd0\fh\xeb\xf1\x04\x86\xf5e!\xc2c\xdf\xfaS<\xf2\xf5\xff\xdb6\xa0\x9dm\x80N\x124\xeb\x11n\xb9\xf9\xfa\xa7?\xff\xf9\u03fd\x87\x88.\\\xae\xcc_\xe5q\xe3\x8d7\xf2'>\xf1\t|\xfc\xe3\x1f\a\x00\xbc\xf7\xbdw\u007f\x99\x99\x1f\xfaW\xff\xea\xb7\xff\xf1\x13O<\xf9?>\xf3\xcc3\xf5W^9\x83~\xbf\x8f$I\xc6~\xfe\x99\xe7\x8f\xe1\x99\xe7\x8f\xe1\xe0\xbe\xddx\xe7[\u078c\xf7\xbe\xebN\xdcz\xf3u\x98m\xd6m3U\v\xd8E\u06a0\x18%Q\xb2\xea|\x95\x15ieu\xea\u0758E\xba\xa3z\u0528l\xc0UUR\xb3\x9f\xc4\x0e\x14$e\x93\x90\xb6*\xddh\xf2>\xa2z\xff\xf0\x83\xae\xe2)\x96\x19cUI\xb1\xb1\x94J\x9e\xfaS\xde\x15\xe5\xd5P\nt\xe9LA*\xee\x14p\xd9S\xe9\xf5\trS\x90\xec\xf9WK\x04JA\t\x81\xf3+kx\xfa\xe8q|\xef\xf1g\xf1\u0533\xc7pqu\xc3\u01baI\x91\x9f\xa5\x1f>\xec\xe8\x1d\"@\x1b\x83\v\x17.\xe0\uc673\x16tDn\x86\x95\xf3Y\xe9X\xbc\xafe\xa7|]\xa0\xf1\xb6t\xae\xc4\xf0\x16\x0f6\u067f1\xe7u({\x1aq\xa3\rt<\x04\x18\xb8\xf6\xae\x0f\u1dbf\xff\x8f\xb1\xf3\xaa\xeb0\xec\xf5`t\xe2\x940\xf6<\x85R\x10A\b\x15\xd6 \xc3\u0426\xe9\x00\x85\xa1\x98R:u\xc5\xddIYsZ\xfb*\"\x01 \xb6\u065aF3X\x10\x928o\x88j\x00\xd2]\x9b\x88\rL[\x83[\xd2:\xade}\u07f42v\xaa\x17b\bL\x8a*\xc9\xefI\xa2\xea\x1b\xdd\xf7/O\xafch\x18\x9a\tR3\x843\x0fK\xef=2\u0206\xaa\x88\x81$\x89Q\xab\xb7\xb0\xb0\xebJ\xc80\x82\x8ec\xa4\x01DRH\xf4z=\xac\xae\xaf\xed\xfa\xd2\x17?\u007f+\x80/^\x06\xf3Wy\xbc\xf9\u0377e\u007f>}\xfa$\xfd\xcb\u007f\xf9\xdbDDk\x00~\xef\xcb_\xfe\u04a3_\xfa\xd2\u007f\xfc\u0503\x0f>x\xe8\xc9'\x9fB\xbb\xdd\x1e\xc3\u04f4\xa9zb\xf9\f\xfe\xc3\xff\xf39|\xe9k\xf7\xe3\x9doy3>\xfc3\xef\u016d7_\x8fz\xad\x05\x13\x0f\xc0\xc9(\xe3\x18\xdd~\xad0\xfcR\x05\xdaU\x80\xe6\x9b\u0231\xa7\xbc\xc8e^T\x01\x9d\x19\xc1\xe3Et\xf9|~9\x93\x87\xe1\xe7\u07fd:@\xe5B\xd5\u03d7`\u0479:\x93h\xe2b\xb6\x15o\x0f\xd8\x0fN\xa2\xd9\xd2)n\xd8'\xd5P\xe7\xa6W>WJn\xea\x913\x8b\x00O\xba\xe0\xc9\xf9(\v\xa7'\xa4@.\x10H\t\xa9$\x8ca,\x9f9\x8f\x87\x9fx\x0eO<\xfb\x12^<\xb1\x8c\x15\xd7\xdc\x14\x04h\u0599\xd5+\t\xdfm\xdbQ7^\xfe\x9e\x90.\xd75G\xe1\u0716\x96\xb7&\x99R|\xce\x1f\xe2\xe0*\x05{\x81\x82\x97}6z\xee\x82[\u04a1\xa3\xf4\xcb\x18\x83Zk\no|\xef\x87q\xe7/\xff\x06\x9a\x8b;1\xe8lzT\x83\x93\x1f\x06!d\x18Y\x10\x972\xcb\xed\u012b\\\xa2\x99\xd2\xe0t\xbbK\xd2\xc6s\x97\x14\f3b`-\x01w\x12g;\xec\xde\v\a\xa4\x9a\x80>\x01\x92\x19r` \xe2\xd8V\xc2S\n\xc25@\xb3\r\x87\u06cd\x18\xa6\xcc{\x89\xb2\xa6lu\xd1A\x15\xea\x17\xc0\x99\u007f\xb9k\x1d&\x8c\xa1\xf3\x99W\xda)r\xd2\xdfk\xac\xda\u021ff5l\xef\xcf\xd6\xccN\xb4fvb\xfd\xe2\xc9\xcc7\x9e\x04!\x1eiN\x12\xbd\xf8\xf0\xa3\x8f\xdc\n\xe0\x8b\x9d\xce:\xb5Z\xb3|\x19\xcc_\u00f1w\xef\x01\x86\xcd\x14\x95w\xdf\xfd\x01}\xf7\xdd\x1f\xf823\xdf\xfe;\xbf\xf3?\xfd\xdbVk\xea\x97\x1f{\xec1\x9c?\u007f\xbeP\xeaf\x8d\x11a\xb7v\x17V\xd6\xf0W\x9f\xfb\n\x1ex\xe4)\xfc\xd4{\u078a{\xde\xf7v\\s\xd5!\xcc4\x1aP\xd0H\xe2\x18q\x1c\xdb\u02a2\x94\x95I|\u9294\\\xf52r\x9e\u06a9\x97v\x9ar\"\x05Y\xf5@aK\xe9\u03f9S\u05bc\xf3\x15\x18\x94i\xa3\xd9Q-T\x92t\xf0\xe4\x91\xd4Kp\xecE.\x1e\x85\xdd\u026b\xa9\xc6'%-1[\xad\xb8\u05b9\xccP\xeb\xbc\n\u03f7\xec\xe3K\xce\x18h\xbb\xd2^P\x1e\x1dB\u0796\x842><@\x10(\f\x86#\x1c;\xb6\x8c\xef<\xf24\x9e\xf8\xfeQ\x9cx\xe5,\xba\xdd~\x0e\xe2I\x82D\ub8bf\xb9\x11\x05\xc4\xf5\xdb\xd0i)\xca^\xf6l\x1a\x98\x9cJ\xfc\xf2\n3\x05B*\xc4\"g3\xaa~@\xab%\xc5\xf3\x85_k\x18\x13\xc3$\xb1\xa5J`w\x17\xb6i\x19@\xd6\xea\b\xeaM,]\xf1F\\\xf3\x9e\x0f\xe1\xd0-oG\u051c\u00b0\xb3\xe9\x02\xc4\xedh\xbb\x90\xca\xd1*5\b\xa5<\x83\xa2\x92-\x00\x97\x03\x1cx\x8c\x0e3\u0657U\x17%\x06\x88\x8d]\x98i3\x81\xe8h\v\xd0\xc6\x01%\x91}I\x8c\xb5j\xd1\x00\xe2\u0600b\xb7\xe0\x8e\fxh\x80\xf9\x00A \x10\xc0R,\x82r\xd2)\xad\xfe%\xa5!\x1d\xbc\xe5}\xc7^\xd4\"\x93\xe5\xcdA\x02\x9a\r\x06\t\u00f0\x05r\x99x\xbd\x14\xc3 \xe3\xf9\xb6\xe7\xac\x17\xb4\x8eQk\xcc`a\xe7a\xac_8Q\xbc\xaf\rF\xbdn7z\xf9\xf8\x89\xeb\x00\xa0\u055a-\xf4\xf8.\x83\xf9k8\xee\xbe\xfb\x03\xfa\xbb\u07fd\x9f\xde\U00096df1\xe3\xac\xfe\xe1\xe7?\xffw\xf7\xdd{\xef_\xfd\x0f_\xff\xfa7\xf6-//S\x92$\f \x91R\x06\xc6\xe4\u06fe\xac\xca?s\x0e\xff\u05df\xfe5\xbe\xfe\xed\xef\xe1\xe6\x1b\xae\xc5\xfb\xde\xf5\x16\xdct\xf5\x01,M\xd5\xd1j\xd4 \xa5@\x1c'\x8e\x8fw\xa6Q\x82\xbcq\xf0q\x0e8\x05\xf2\xcc\xfc\u0254\x12\xe0\xe19\x1c\n\xca\x00*\xad\xd8\r\x17\xedk\xa9d\xfa\x9fZ\xfd\xda\xf1\xe2\x1c\xf0S\xb9b>\x04UE\x10\xf1\x96\x11p\x97\x8c\x87\u02ea\u03ca\xb0\vWys\xf6\xe1\xe7\f\xc8\x13\xe34\xe2\x06^\xcc\x172\x10-\x0fx\x8c)\xd2\xc0\xce\x10*\xd7\x031\xfb\xe7\x04h\xb6UW\x14\x86H\x92\x04/\x1e}\x19_\xfd\u03a3x\xe4\xc9\xe7q\xe6\xdc\x05\f\x86C+\x8bc\x83\xd10\x81N\x9b\x9b$2\x8d\xb2T\xcaM\x0fR\x9e)Ib\x82\x1c\xa88\x11@.\x8f\x16\xe5k\x01g\v2\x15\xc6b\u0269P,`\xb3\xb1v\xb5iU-\x82\b\xd1\xcc4\xc2\xe6\f\xa2\x99\x05D\xd3s\x88\x9aS\x98\x9a[\xc0\xec\xee\xfdX8t5\xe6\xf6]\x81\xe6\xec\x02\x88\xadU\xae\b#\x90T\x80\xfb\x12nQ\x13\x82<>~<\x9d\xb5j\xbf\xc9^\v\x82a\x81{\xa4\x19\xa3\x84m\x13\x91\xad\u03f9\xeaj\x04\x1d\x9d\xb9)J\r\x906\x85\xf4:2VVn\x90F\xf4\xb8\x02G\xc7\xd0\xcc\xe8N\xdbs\f\b\b%\xd91\u007fApsD\u064e\xc06\x8bKq\x90^hz\x96\xb1\x9b\xaa_\xa4\x02d\x82x\x94`\xe8V#\x19[\xa5\xa6I\xcd\u0334\xef\x83\u3337\u049d\xa4\x8eQo\xce`i\xf7\x11\x1c\xfb\xfe7=\xd5\x14\x10\x06R\x9d>\xfd\n\xd6\xd77\xaeg\xe6\xeb\x88\xe8\xfb\x0f<\xf0\x1d\xe1.\xf32\x98\xbf\xd6\xe3-oy\x1b\xff\xe1\x1f\xfe\xaf\xf8\xcd\xdf\xfc\x18\x01\xe0{\xee\xf9\xd0'\x99\xf9O\u007f\xeb\xb7>\xfe\xb1\xaf}\xed\xeb\x1f=}\xfa\xf4U\xe7\xcf_\b\x1c\xa8kr\xdd&\xe6bK\xfe\xf8\x89\xd38y\xfa,\xbe\xf5\x9dGp\xf8\xc0\x1e\xbc\xf9\xfa#\xb8\xe5\xfa+q\xc5\xfe\xdd\u06390\x87\xd9\xe9&\xea\xad\x10Z\x1b\f\x86\xb1\xe3\xe5\xd3j\xae\x18Ll\x00\x9b\x06\x9f\u4cba\x8cB\xf0@/\x05\xea\\vE\x13Io\xf6V\x8e\xbc\x92/\xf2\xeb\xc2q\xc4R\x90\xb5\x0f\x15y|\x96\xc8\x02}\xe9\x92\x16\xb9\x97\xecX\x96\xcf0\xab\xd6,\xff\xad\xbd\xf1\xf3\xb4\x92\xcb\r\xb0\u0704\x1e\xa8\x04\x1b\x18k&\xa4\u065a)\xd02\x8a\xf9\x9c\xe9K\x92\xaa9\xeaQ\b\"\x81S\xaf\x9c\u00f7\xbe\xf7\x04\xee\xfb\u07938\xb9|\x16q\x1c\x83\x88!\x05C'\xf6\xbd3F\x17\x16\xf6\x99f\x1d,\x02\f\x13\r\b\xe5Y\xa9::\x82\xca\xd1\xd4\xee}O+u\xe1\xde\xff\xf2\xf6\x82<\xbb]r`\xa15\x92d\b\x9d\xc460\xb8>\x8d\xc6\xd4\x02\x82Z\v\xaa\u0442l4\x10\xcd.\xa2\xb1\xb8\x1bS{\x0ecz\u07d5\xa8\xcf\uf08cj\x10B\"\f\x15\x1aQ\x80Z\x18@\bB\x92\xc4\xf6\xfd\xaf\xd5\x1d\x85\"\xa1\x85Mv\"a\x13\x84\xc0\xe3\xadl\x86\xbf\xeb\xc9{/\xc6\x03q\xe3\x16\xc9X\x03\x83\u0120\x9f\u062a<\x9d\xef\t{\x1aa\xcf\x05\x002 \x13\x06\xa5#\xfe\xeey9\r I\xe1Rx\x8e\x8e\x9a!:\x1a,\bqCbD@?a(\x01\x04\x82\x10)\x81@\x00J\x10\xa4p\xe7lJ\n-\"+/vU\xbd\u007f\xee ;\xa0\xdfO\xec\xdfe\u0090\t\x17\xed\x1e\xb8L\xdb\xe4K\xb4\xd1\tT\xd0\xc2\xcc\xc2>\x04a\x03:\x199+\x04{K\x8c\x86C\xac\xac\xac\xcc}\xfd\xeb_Y\x04\x80\x87\x1ez\xe82g\xfe\xc3\x1c\xbf\xf9\x9b\x1f\u00d7\xbe\xf4y\xfe\xdd\xdf\xfd=\xfa\xe67\xefc\xa7K\xff\x1df\xfe\xc3\u007f\xf6\xcf\xfe\xe9o>\xf2\xc8#\xbf\xb0\xb6\xb6~\xe3\u0253'U\xb7\u06ddH\xb5\xe9Dceu\r+\xabkx\xfc\xe9\xe7\xf1\xe7\x8d:\xf6\xeeZ\xc4\rG\x0e\xe1\xea+\xf6\xe3\xf0\x81=8\xb0g\t\xbb\x97\x160?;\x05%\x05\x86q\x828N\x9c\t\x12\x9c\u0740\vFH\xb5\xd1i\x13\r\xc5(\xb2t\x84\\x\xe8J\x9c\xfbqT\xc1:gU\x15gF]\xec*>\xe1l\v\x04\x11F\xe9vU\xb8x=7\x8aL\xd9v\xd5U\xf0\x8en \xaf;[H.\xe2\x94#-\xce\xcc[_\v\xf6>8\x9c\aA\xf86\xa7\xde8\xbdU.\xe4\\0{WS\xf8l2\x8fy\x8f\xb3\xdf\xe0\xf4\x99\xa8tj3\b\xb0\xb2\xbe\x89\a\x1e\xfd>\xbe\xfc\xad\x87p\xf4\xe5eG\x93i\b\xc1\xd0:A<\xb2\x15\xb0\xf1\x16\x84\x85\xd9)\\q\xcb[\xb1\xef\x8e\xf7\xe3\xc2\xc55<\xf1\xf9?E\xfb\x95\x97\x01\x00A\x10\x81\\t\x98]L\xc8k\xc62\xd8P\u039b39\x90\x19\xb7\x84\xe4Tj\xa8ch\x1d#\b\xeah\xcd\xed\xc1\xd4\xfc>\xcc\xee\xb9\x023{\xaeDki?jsK\x90K\x8b\xc0L\x13*j@H\xe5M(\xe7J\x8e\x04@;\x06\x86\x9c\xa0\x11)\xd4ju[\xc9;\x15\x0e\x91\xb0\xc0\n\x86\x12\xbe\xa3\xa2\x0fU\xa5\x05\x94\xfc\xc57\u007fOc\xc3\xe8'\x8c~l{\x1d\xbeY\x9d\x1a\x18\x84=\x03\xa1\x1d\x90\u01f6\xa9\x98V\xe8>\xedg_\"\xceR\x83X\xe44\"%\x8c\xa8\xab!\b\x186\xa4\x93\xa9\x02\xb1f\xf4\x13\r%\b\xa1\x00\x02%\xec\xff\x85\xf3\x96\xe1\\\x0f\xa6\x992/\x11.\x15\tCc\x1b\x9fd\x00\x15\xc36>\xc9\x1a\xf2\x11\x93]xD\xfax\xe1\x8a\x1d\xce\xc4\x05\xcc\x1a\xf5\xd6\x02\x9a3;\xb0~\xf1$dn@I\xa3\xd1\bI\x92\xecy\xf2\u0267\xae\x05\xf0\xcd3g\xce\xd07\xbe\xf15\xbc\xfb\xdd\xef\xb9\f\xe6?\xe8\xf1\x81\x0f\xdc\x03\x00\xfc\x89O\xfc\x1b|\xfc\xe3\xff}\xfa\xa1\xdbp\xa0\xfe\xe9O|\xe2\xdf\xfc\xbd\xaf|\xe5+w\\\xbcx\xf1M\xab\xabko\xbcp\xe1\x02\xf5{\xbd\x89M\xbb$I\xb0\xb1\xd9\xc6\xe6f\a\u03fft\x12\x81\x92\x98j5qx\xff.\\}\xc5~\xbc\xe1\xe0^\\}\xc5~\x1c\u063b\x03{v,`\xba\xd5p\xcd=\r\x8c\x12\b\x9d'\xe80\xa7\xe3\u031c\r\xb5\x14\xa8\x89\u031b\x9c\xf3\x06\x90\xc3\b]\xb007\xd9$\x93\xd7\x12t\x80n}2\xa4I}hr\xd0\xcf\x03+<\xbf\rg\xf3*D\x1aO\xe6\xd1\xf6\x94\x876hf\x1b\x19\xc6~\x02\x0fg\x95O1\u0601=f\xd8\a\xe2\xb1\xfek\xc1\x1f\xc5\xdf\u044cW\u0754\ro1\xb8\xe0\x86)\x85D-\f\xd1\x1f\x8d\xf0\xc0c\xcf\xe0\xab\xf7?\x82\x87\x9ex\x16\xdd^?\xb3\x9a5\xdaN[\xdaj<\xff\r;\x17fp\xed\xdb\u07cf\xddw\xfe4\xe6n\xbe\vb\xf7aLo\xf4\x10\x1c\xba\x01\u01fe\xfcg\xb8\xf8\xec#\xe8\x9e_\x06b\xbf\xa9M\xb9#\x1f\u0195*Y3\x8d\xf2\xf7\x8b@PA\x1d\xad\x99]\x98Z\u060b\x99\x85\x83\x98\xdbq\x18s;\xaf\xc0\xcc\xce+P\u07f9\a\\\x0fa\x8c\x86\x96\x8c$\x12\xd0!Y\xd9\x1e\xb4\xf5\xe0\xce$M\x02p`\xcdB`(m\xfa- Q\x93n'\xe6^#\xe9^?\u9a38K\x1a;0C\x1b{\xbf\xc1Q\x1a\xfd\x84\xd1\x1d\x19\x8c\xb4?=k\xd5Bj`,\x00k\v\x922f\x88\x84=\x0f\x1a\x14\xf5/\x8e\xff \xb2\x14\x87\xf6\xfb\x94\f\x88\x04\bz\x06F\x10t]\x14*\xad\xc40b\x03\x90\x03\xf6H\x12j\xca\xfe_\n\xbf7\xe1\x9e\xd7S\xe4\x8c\f\xa3\x97\u061d\x84t\u7656\xf0\x04\x80\xd2\x05\xb9\x9c\x15\xea\xed\xb6\xb5NPk\xcc`jv\x17\xd6\xce\x1fw\xbb\r\x06\x98\x89\x99\x93n\xb7\xa3\x9e|\xf2\xa9E\x00\xe8\xf7\a\xe2\xdd\xef~\x8f\xbe\\\x99\xff\b\x8e\x14\xc8\xff\xe0\x0f>\x81\u007f\xf1/>\x8e\xcf~\xf6^\x10\xd1Y\x00\u007f\x04\xe0\x8f\x1e}\xf4\xa1k\xfe\u077f\xfb\x93[VVV\xff\xeb\x97^|\xf1}'N\x9e@\xbb\xdd\xc1p8,|\xd8\xfdJ\xd8\x18\x838f\xacmlbmc\x13\x8f<\xf5<\xa20\u011e\x9d\v8\xb8g'\x0e\xee\u06c9\xc3\xfbw\xe3\r\a\xf7\xe2\xe0\xbe]X\x9c\x9bA-\x8a \x94\x1d\x821\u0330\x8e\xa6\u059c\xc9h.\x86\x9dL\u0a33\x01!\xbf\xd8c\xf6\x92\x93\xbc!$\xa4`\v\x8c\x91\xb6Y\x95h\xe0u\x87\\Ui\n\xbc\u007f\xce\xf2\xf8q_9H\x1b\xe61\xe0\xf2\u01f0\xfd\x19W\xeb#\x9d7\xa4*\x98\x94L\xf7c|\xf6\x99\xf2\x9d\a\xf9i\xf0d\xd5\rRZ\x99\xa11\x8cg_:\x81\xaf?\xf08\xee{\xf0\t\x9c\xbd\xb8\x9a-L\x16\xc4c\x8cFCho\xf6`\xef\ue778\xfe\xdd\x1f\xc0\x9e\xb7\xfc4\xa6\xae{\aF3{\xd0\x19&\xc0z\a2Px\xc3\xdd\x1f\xc1\xee7\xbd\x13\x1b'\x9e\xc3\xe6\xf21l\x9ez\x01\x1b'\x8f\xa2{~\x19\xfd\x95\xb3\x88\xbbm\xb0N\xf2IJ\x12\u05a4*\x8clcR\x86\b\x82\x1a\xa2\xda4\x9a\xd3;05\xbb\v3K\x870=\xbf\x0f\xcd\xd9\x1dh\xcd\xecFT\x9f\x86\xd61\xb4\xb1v\x13\tb\x98@\u0606]\fH&P `j\xcaV\xd9BZw*\x91Rz\xf9\xffcfl\x0e5FZ\xa0\x19\nD\n\x10\\}Gqe\x13\xdcI\a\r\xb2]d\xac\x81^l\u040dm\x8f\xa3\xb0^\t@\xc4\x06AGC\xc6v7*c\x03\x11{\xd5xy\xf5N\xdf>c\xf9n\x86\x05t\xf6\x82V\xc0\x96\x9e\x89\xba\x1a#\x02\u26a8\xf4\xeb\x1fiF\xac\x19\x83\x84\x10J\xa0\x11\b\u02f1\v.\xb65\x98\x900\xd0\x191\x86\xc6\u07a3rd\xad\x16\xb2\u05cfE6\x8b\x90G\x8cZ\xc2\\x\xeb\xb3\xd11\x1aS3\x98\x99\u07d5\xd1o\xe9g3PJ\x9cy\xe5,\xae\xbbnxC\xeao\xfe\xd0C\x0f\x8a\xdbn\xbb\xe3u\u03dbo\xcb6\xeeW\xbf\xfae\xfa\u02ff\xbcW|\xeaS\x9f\xd6y!\u008dO~\xf2\u007f\xffo\x9fy\xe6\xd9\xdf~\xf8\u11e7\x8e\x1d;\x86N\xbb\x8d$\xd1\xd6\xf3e\x82)\xbe\x10\x02J\xda-\xad1\x96Z\x10\x044\x1bu\xecX\x98\xc3\u03a59\xec\\\x9a\xc7\u03a5%,-\xcec\xc7\xc2<v\xeeX\xc4\xc2\xc2\x1cj\xb5\xc8\x03\x1b\x03\xad\x1dw\xab=\xadqa\x94\x99K\x1bl\xca8\xc8LaNEK]\xf2*g\xce'M\n\xc3(\xfeg\x88*\x02E)\x9b\xd2\xf0\x831\x8a\xfa\xef\x8a\x10\xf6\xac\xfa\xcf\x15{4a\x81*\xac\a\xde\xf3PQO\xef\xfdQ\b;\xec#\xa5\xc4(Np\xec\xe4\x19|\xeb\xc1'\xf0\xd0\x13\xcf\xe1\xc4\xe9s\x88\xe3\xc4z\xa8\x18\x8dD'HF#\x8cF\xc3\xec5\\X\x98\xc7\xcd\x1f\xfc\xfb8\xfc\x9e_D\xe3\x8a\x1b1\xaa/\xa07J\xc0\xa3A\xeec\xeev#2\xacAE\x11d\x00\x8c:C\xf4\xd7/b\xd4Y\u01e8\xbb\t\xdd\xefB\x0f\x870I\xec\x02#L>\x16o$\x1a}B\xa4\x15\x94\x88\x10\x85M\x84\xb5&T\xd8t\u05a91L2\xb2\x8dNA`!\x00%\xc1\x81\x80\x8e\x14L(\x01!,\xff+\x04\x92\x86B\u04b0N\x86\u9389\xb9\x1a\x96\x99m\xe3\xb0\x11\x10\xeaJ \x90\xf9\xa0\x94\xf4$\x82\xe5\x06\xa7\xa3\xa0\xed\xbd\xec\xaa\xf1\xde\xc8`\xa8y\xec\xfe`\x02\x84\x01\x82\xb6F\xd0\xd3\x10\x86!b\x86\x1cq\xd6\x14\xcf\\\x0f1i8\x8d`\x84\x15\f\xb1\xeb5\xd8F\xa5\x00\xa4\x95\x12&\x11a\u0612\xd05Q\x88\xa1\xe3\n\x95\xad\x10@\xa4\b\r%\x10\xcaT\x82lw\xbb\xbd\x98\xd1M,\u0146\xde\x00\xc1\xc5.\xe4\xc0\xa6\x96q\x92\x80\xfa\t\x10\x8f\xc0.\x93\x95\xbc`\x12\x99\ue01d\u02a81=\x83'\xbe\xf3\x17x\xe0\U000df10a\x1a A0\xf1\x10\xa3a\xd7H)\xc4\xdd\xef\u007f\uf4df\xfe\xf4\xa7>\xb2\xb4\xb4\xeb\xb9O\u007f\xfa\x93\xf2\xd7\u007f\xfd\x9f\xbc\xee\xabs\xb5\x1dO\xea\xbd\uff5b\x01\xe8\xe7\x9e\xfb>}\xf5\xab_\x13\xbf\xf1\x1b\xff\x94\x88\xa8\a\xe0\u007f9w\xee\x95{\xbf\xf0\x85/\xfe\xd2\x03\x0f<\xf8\x8bO>\xf1\xe4-O>\xf5\x14\rG\xa3L\x97^>\x8c1\x18\xb9h0!\x05\xa4\x14\x90B`0\x1c\xe1\xf8\xa93x\xe9\xe4i\b!Q\xaf7\xd0l6\xd0l4\xd0j6\xb1\xb40\x8bC\a\xf7\u16ab\x0e\xe3\x8a};13\u0570\xe3\xe4A\bU\x93\xaeq\xe3\x05,\xa4\xd5\n\x91\xa7\x84\xb0\x1a\xe71\xb5\x01\x8a\x86\xec\xecM\u0325\xdbg\xe3\xbaF\xb9\xe6\xdd\xe4A\xbf\xde'%\xeb\xe6\xbb'2\x15\nG\x1aSux\xe9=\xe4\x19&\xf9\xfc{\x19\xd0K\x95\xba`\x1fp\xf2\u6c12\x12\xb5(\x80\x10\x02\xbd\xfe\x00\u03fft\n_\u007f\xe01<\xfa\xf4Q,\x9f\xb9\x88\xd1h\x04\xa5\xec$G\x1c\xc7\xd0\xfbeH\x9c\x00\x00 \x00IDATI\x8c8\xb1\xd4\n\x00\x04a\x88\xeb\xde\xf7a\\\xff\v\xbf\x8e\xa9\xabo\x83\x0e[X\x1b\f\xc0\xed6\xa4\x80\xf3\xfe\xf6w\x04\xf6C:\x1c\xf6\xc1`\b\xa1Ps\xcdH!E\xee!oRE\x86\x81a\x031\x18!\u060c\x11n&\x90#\r\xc4\x1a&\x89\xc1&A2\xec95\x8etS\x98\n\x10\x04\x0e%X);\xd0\x04\x01M\x02Z\u0694\x1db@\x0e\b\xa4\x80\xa4\x96\x0e\xeeVF!g\u02a8\xd806\x87VO\xdd\f\x05j\n\x19\u0152\xca\xfa\xd2j7\x9f\x9a\xb5\x80\x15\x1bF7\xb6\xb4J\xb9\x1a\xf7\x95\xb3AW#\x18\u0608<J\x182\xe6<]\b[[Ld\x16\xb8\u0300\xd3\x0f0\xa5\xd7f@F\x80%C\xb9\na(\x00\x1d\x89\xb1J\u045fk\xd2\f\xf4b\xc60\u046eB\xa7l\xaax\xe4\x94UBH\x90\xb6\xde\xeb\xf6\x17\xb2\xdd%\xe4\x19w\u07adL\xdeN\xd1\xf5\xaf\x8c\xa52\xebS\v\b\xeb-h\x1dCP`\xfb\x14$\u0120\u07c71|\xe3\u007f\xfa\xf2\u007f:\x02\xe0\xb9N\xa7#3\xd1\xcee0\xff\xf1\x1c\xd7\\s\x1d\xa7/\xf2g>\xf3\xef\xe9\xa3\x1f\xfd\x15\u07b9s\xcf\t\x00\xbf\xc7\xcc\u007f\xfc\xb1\x8f}\xec\u007f{\xea\xe9\xa7\xff\xe1V\xcf!\x84\xa3\x05\xd8\xc0$\x1a\x80\xcen\x80T9\xc2\xcc\x18\f\x87\x18\x8eb\xac\xacm\x82aG\xc4\x1f|\xec\xfb\x98\x9aja~f\n{v-\xe2\xf0\xfe=8\xb0g\av,\xcea\xbaUG=\x8a2O\x10)\\\xb5m\xf2\x84y\xadmXp\xa2\r\xe28A\u007f\x14\xa3?J\x9c\x0e\xb7\xa8\xb2\b\x02\x850P\b\x02\x05\xa5\x94\x9dnt\x93\x8aRX\x90\xac\x87\x01\x820\xb0\xbf+\xf5zv\xc6V\x89\xfb\xbd\x99\tQ!\xb9)\x9b\xe2(*<\u01a2\xb9\xa8z\x14\xc97\xbeB.+3\xae\x19,%!P\n\x81R\x88\x93\x04\xe7V\xd6pr\xf9\x1c\xee\xfb\u0793\xf8\xde\x13\xcf\xe1\xec\x85\x15\x8c\\%N`\xf4\a#\x9b0o\\\u2f63Uv\\\xf3&\xdc\U000abfc5\x83\xef\xfcyP=D\xbf\xaf\xa1\xbb]\xab\xec\x91b|w\xe0\xb7\\\u04e1 \x00&\x19\xc1$\xa3\x12(\xe5CJrd\xa06c\xa8n\x02\x8c4Lb\xab<;\xb4\x13B\xaa\xa8\xc0\xa5\xdb\x12\x97\xc0\x81\xb0\xf4\x89\x93\xee\x89! \xb4q\x13\xac\x96X\x16\x9b\xdaR\v\rY\b#\x99$+e\xd8\x11\xfa\x91\u05b6b\r\xac\"D\x11\xf9Im\x19\u0367\r0L\x18\xdd\xd8*U\xc0\x13\xbcY\x18\b\xfa\x06AO\xdbsM\x81\xdcp\x85\xe72\x8f-\xe0e\xf5Sj~\x96%~ymK\xe9\xaaa\x100$\x82\ti\uceb3\"\xc0\xbb\x8e\xbe\x19\xb7#H\U000e90c4@i\xff!\xfd\xbd\xec\x87\xc5\x18\x17\xe8\x01/!>\xed\x93\x10t\x12\xa31\xb5\x84\xe6\xec\x0e\xac\x9d?\x01\xa9\xac\xc1\x99\r\xf3`>w\xfe<=\xf6\xf8\x13\xfb\x00\xe0\xaf\xff\xfao\xfe\u007f18\xa4^/'\xfa\u044f\xfe\n\xdf\u007f\xff}\xf8\x9b\xbf\xf9\x1b\xfa\xc4'\xfe\x80\x01t\x1ez\xe8{\xbb\xfa\xfd\xfe\u011f\x99\x9e\x9e\x02k\x8dv\xb7Wb&\xb8\xf0\u007f\xfbB\x10\xa4\x12\x90\xceG\x99@H\x92\x04\xab\xab\xebX]]\u01f1\x13\xa7\xf1\xe0\xa3\xdfG\x18(\xd4k\x11\xa6\x9a\r\xcc\u037405\xd5\xc4T\xa3\x8e(\n-\u007f9\x8am\xda{\xac\xd1\x1b\f\xb0\xd9\xeeb0\x8c\x11'1\x86\xa3\x04q\xa2\xb3\xada\xda\xe0\xb4\x15\xad\x82\n$jQ\x88F\xbd\x8eZ-\xb4\xff\xa6\x14j\xb5\x10\xcdz\rK\v\xb3X\x98\x9b\xc6T\xa3\x810\f\x10\x86\x01\xea\xb5\b\xf5Z\r\xf5z\x1dQ\xad\x06%\t\xacu\x9a\xae\xe2\x00\x1e\x05c\xbd\xdcn G|\xe6\x92\x05\t{4\x8e\x10P\xca\xeehD\xf6\xe5*\"\xad1\x18\x8epqm\x13\x17.\xae\xe1\xf9c\xa7\xf0\u0413\xcf\xe1\u0157\x97qqm#\xa3S\u0229\x8f\xb4\v\xf05i\x90/\x80\xe6\xd2^\\\xf5\xc1\x8f\xe2\xda\xff\xe2\x9f`\xe6\xe0!$CF\xbc\xd9\x05\x1c%\xe6\xef\x0e\xaajH\x1f\x93\xb8\u0510\xcd\xf6/\xcc\x0e\xc8\x19\xe1\xa6F\xd03\x10\x9a `\xabo\b?\xb0\xb0b$K\xd8\xecI\x03\xdb\x03a0(a(M0\x92a\xdc\x00\x81\x18\x01\xa1\xd1 \x06\xe2\xba\x04\xcb\xf1V\xc8\x18\xa0{\x15\xebH3\x1a\x01\xd0\f,\xf5B\xb9t\x05\xb1\x01:#\x83^l\x90TU\xe3\xfe\a{`\x10t\xb5\x95\x1d\xb2mv\nS\xe2\xcch\xf29\x15-\x12\xca\n\x1dW\x1c\v7|\xa5\x19$\x009d\x84\xd0\x18MI\xe8\x80&)XK\xef\u0378\x92V&\f\xa9\tl\\\xe4\x9csG\xf4\xcf'\x9d\xfcMUVy7\u01e9\xc6t\x82fs\x1e\x8d\xd6<V\xcf\x1esiM\x96^S2\xa0S'O\xa2\xd3\xe9\\\xcf\u0312\x88\xe2\xcb`\xfe\x13>\xdef\r\xe5\x19\x00~\xeb\xb7>\xfe\xae\v\x17.\xbcWO0\xea\xaa\xd5j\xf8\xd5_\xfdU\\\xf7\xc6k\xf1\xf0\xf7\x1e\xc0K\xcf?\x8bg\x9f?\x8as+\xeb0\\\xad\x86a\xa7~\x10d\a^\x04\x89L\x1eH\x00\xe28\xc6h4B\xa7\xdb\u00c5\x95\xb5,\xce\xcb\xdfG\x8e\xd1\x14\x9eJ\x80\xbd\u01a7?\xafY\u04a2\xb9\x9b]\xe4zv\xb2\u0a64\x95\xf4EQ\x88Z\x14\xa2^\xafaz\xaa\x85\xb9\xb9\x19,\xcc\xce`nv\x1a\v\xf3\xb3X\x98\x9d\xc6\xecT\x1d\xf5(D\xb3Qszn\x02\xb3\x86N,U\x834\xa6\x8cr\x85G\xea\x13nw\x03\x02\x81\xb2\xe9\xef\x83\xe1\b\xdd^\x1f\xbd\xfe\x00\xfdA\x8ca\x1c\xa3\xd3\xed\xa3\xd3\xeda\xb3\xd3\xc3\xca\xea\x06\x96\u03dc\xc7K'^\xc1\xb9\x8b\xab\x18\x8eF\x96\x0er\u05db\xc4\xec\x00<\xc9\x00\x1c\x00\xa6\x16\xf6\xe2\xd0-?\x85\xab>\xf8+X\xbc\xfd\x1d0\x01a\xd0\xeeY?\xefR)KeN7\x1d<)\xb9\x8b\xf9\xfd\x85\x02R\t\xeb1\x12\xb65T\xdf5\x005;\x90Hw&[E\x9c\xb8II7\x1e\u039e4U\xb8\x0e!K\xcb/\x8b\x84\x10\xb65(f\xc4M\x01\x1d\x8a\u0262\x94\x12\xb8i\x03l\x8e\fb\xc3h\x05\xc2\xd2\x10\xb0Z\xf1nl\xa7\"\xb94\xa4VFb94\b;\xda59\xd9\xc6\xe0\x19.\xc8E\xfd\xac\xd9\t\xb0\x8a\x02\xda\x13\x17\xe5K\x94N\x8d\xda\xc5M\x18\x00\x9a\xa1\x86\xaeBoI\x98\x90^\xd58N\xf6\x16\xa6\xebfl\x157\xec\xe5\xa3\xda\xe7\x19\xf7\xd5e\xce\xfbE\u067bG\u05b4\xab\u079cC\xb35o)!JS\x93\x14HJ\xb4\xdb\x1dlll\xdc\u011c\xec\x06\xb0|\x19\xcc\u007f\xc2\xc7\xfd\xf7\u07d7\x02:\x9e~\xfa\xfbw\x9e8qb\xac\xdaN\x8f\xa5\xa5E\xfc\xf2/\xff\x03\xdcq\xc7[\xf1K\xbf\xf4\x0f\xb0|\xfc(\x8e>\xf90\x8e>\xf74\x9e;z\x1cO=\u007f\f\xc7N\x9d\xc5z\xbb\x9bU\xcb:\u06da\v@*[\x89\xa6C<eg\xbe\xd4Od\xcc^\x94\u01abBx\n\x13_\x93]\xd0g\xa7#\xe6\xb6\xda0.F=\xdd\xd2j\r\xc4q\x82\xc1p\x04\xd1\xed\xe5\x8bM\x16~k\xa9\xa1f\xb3\x81\xb99\v\xe8\xadF\rs\xb3S\x98\x9b\x99\xc6T\xb3\x8e\x1d\x8b\xb3\u0635\xb4\x88\xa5\xf9\x19DQ\u0359`q\x81\xe3g6\xe8\xf5\a8\xb7\xbe\x86S\xa7\xcf\xe1\u0339\x15\xac\xaeobec\x13\xedv\x0f\xdd\xfe\x00\xfd\xc1\b\xedn\x0f\xbd~\x1f\u00e1\u0749h\xadsN\u05f8?\x1bc\x17\x10\xb7KH\x8f\xf9=Gp\xe4\xb6\x0f\xe1\xf0\x9b>\x80]\xd7\xdc\x01j\xd4\u047b\xd8A\xd2\x008\x14\x95\x01\xa8\\\n\xf7\xf0m\x1a\x88\xb6n\xef3Y\x05G\xd8\xd6\b\x06\xc6R\r&\xa5\x1c\xb82\a\xb6 F\xa5\xbc\xca$O\xd2d\x03\x14\\\xc3\u05cd-\xb0\x15]\x804\xdb\xe1\x1c\u0348\x9b@R\x13\u0145g\x12\xa8\xb9\xe7\x1b$\x96:S\"w\xaa\xd4\x06y\x90\xf5\x84'\x101#\xec\x1a\x88\xc4%\x1d9\x90%FiBw\xb2\x1d\x1d\x97UR\xec\xe3\xa8'ka\xef\xe5q[;b\v\xe8L\xc0\x88$\x8c\x12\x93\xb8\xb1jZ\xd4 W\u06a4U\xb8\xc9\x04]\xee\xf3\xe6\xfc[L\xeeL\x99\xf6\x15\x84\xa3MM\xa2Qk\xb4\xd0h\u0343\x84t\x16\xb8\x80\x94\nB(\x8cF#\xc4q|\xeb\xbd\xf7\xfe\xd5.\x00\xcb\x0f<p?\xee\xbc\xf3m\x97\xc1\xfc'u\xfc\xee\xef\xfe^\n\xdc\xc1;\xdf\xf9\x8e_6f\xf2M\xb2w\xef>\xec\u0673\a\x80A\xbd\xd9\u01357\u074ekoz\x13\xd0>\x83\xd5S\xc7\xf0\u02a9\x97qj\xf9\fN\x9e>\x8b\x93\xa7\xcf\xe2\x85\x13\xa7q|\xf9\x1cN\x9f\xbb\x88\xf5v\x0f\x83\xe1\bZ\x97\xbb\"\"K\x11\u03ebv\u1188\x8a9\x9fy%>>*N\xbe\u0169\xd7\xc8\u02dd\xf8\xbcf.\xe5\xfc6g\x9ab\x064\xd94\x18!A\x8c\x8c\xf6\xb0\x15\xc7&\x8eq>d\xa4\x94\xa5\x86\xe6fZX\x9a\x9f\xc3\xd2\xc2\x1c\xe6f\xa71;\xddD\xa3Q\x87\x92\x12Zklv:XYY\xc7\xea\xfa\x06V\xd77q\xe6\xfc\n\xd66\xdav\x98G\x97\x06\x83\x1c\xf0\xa7\x15x^\x89\xbb\u01b0\xb6C7\xa9|4\xa8\xb5\xb0c\xffux\xc3-\x1f\u0121\x1b\u07c3\x9d\x87o\x86\x10\x11F\xbdM\xe8n\x0fA @#\x81\xe1L\x80$\x12\x05Kc\xc03(D\x01G&\x03c>(\t\x91\xc0\x02y\xca\x1fk\x86(\f\u028c\xc1wE\xa9_\xfd;\n\x14\x90\xb6*r\x1d\xe4\u0563\x1c\x1a\xfb\xbb\x12\x89\xa4!ad\x81\xa6\x1e\xe3\xd3\xfd_\x17;\xbdv\n\x98[Y\x880Yz\"\xecj\u0221\x9b\x99H\xc3\x1bR\u0547\xd74\xa7\n\u0687\x8a\xec\x9a#\xaa\U000f26483O\x98\xbc\x9bO)]\x9e\xff\xb0a\xa8\x81\xfd\xe4\x8cZ\x04\xad&\xa6\u064d\xbd\xb4\"a+I4\xb0\rh$\xb6\xd9\xee/=\\\xbc\x0fQ\xb8'\xf2\xc9U\"Bsf\x11a\u0530>\xf1R\x82\xa4\x82T\nF\x8fp\xfe\xfc\xf9\xf0\xa1\x87\x1e\x9e\x02\x80\u007f\xfd\xaf\xff\xe7\u02d5\xf9O\xf2\xf8\xdc\xe7>\x0f\x00\xf8\xecg\xef\xbd\xee\u0739s\xd7UY\xe7\xa6\u01d5W^\x81\xfd\xfb\x0f!I\x86H\x92\x04\xf1h\x04&\x01\x11.b\xf6\xcaY\xcc\x1f\xbe\x16\xd7\x0f7\x80\xf6\x1az\xed\rln\xaec\xb3\xdd\u0145\x8b\xabx\xf1\xc4+x\xe2\xf9\xe3x\xf4\xfbGq\xf4\xc4+X\xddhc0\x8cm\xcf\xdc\x15\x98\xaf\xad\xf5M\x90*\xb4\xcd\u0634\xbc\xa4t@Hd\xa1\x05 \u06f0e\xa7\x90I?\x18\xbe\xb9\xb6o}k\xaba\x032n\x98HHk\n\xa6\x14\x94G/23z\xfd>:\x9d.N\x9c:\x03\x80\x10\x84\n\xb50D\x10(\xeb:\xa8\r\x06\xc3!z\xfd\x01\x92D[e\x88\xa3w|\x90N\x17\x14\xceF\xfeM\xd1C$=m\x93@\x06\x11\xa6\xa6\x96\xb0t\xf0z\\u\xf3O\xe3\xc0\xd5o\xc3\xf4\x8eC\x10*D\xdc\xeb\xc1\xe8v\xb6\x18R\xcc\x10Z\x83\fa0\xa7,\xa0W,\x86\x85`\x03.J\xec\xc7\x12\x9a\x04\x814#\xea$\x19\x90\x93\v\x00\xceS\x16\x8a\xae\u06d5\u068e2\x8ap\x1emG\xc6n\xe4\xf2\tIKk\x18IY$'%@\xd8\xd5\x10\t#n:\xfa\xe1U\x98\xc9\xd3%\xf8e\x1f\u0205\x01\x82\x9e\x86r;\x8fl\xf4^\x97\x16\xad\x94&\xc2d'\xd1\xfc5\xa6bk\xb420%\ud193\xa5FR`w\xe7C\f\f\x1d\x87Nf\xeb\x8b%\xb6\xcdi\xa1\xd9J \xd3\xe9X/\xac\x84\xbd\xc72\x8d\xf9D{V\x04V\xc9Vo. \xacO\xa1\xdfY\xb3\x86f$ U\x88x4\xc0\u0253\xa7\xf0\xfe\xf7\xbf\xef=\x00\xbe\xfew\u007f\xf7\xf9\xcb`\xfe\x93:\u039cY\xc6\xee\xdd\xfb\x00\x00_\xf8\xc2\x17\u007f\xf6\u0739s\x98\xa4-\x97R\xe2\x96[nq\xa0b2\u054a\xbd#\x04b\xaa\x01\x14\x81\xeaS\x90\xd1\x0e4\xe6zh$\x1d\xec2#\x1cIb\xbcm\xd4G\xd2\xedb\xb3\xd3\xc1\xcb\xcbg\xf1\xcc\u0457\xf1\xe4s\xc7\xf0\xfc\xf1e\x9c\xbd\xb0\x86\x8dN\x17\x1d\xc7!\x0f\xe3\x04\xc3QR\x18t\x91B@)\t%\x05jQ\x00\x90@\xb77\xc4(\x1e\u066a^\b\b!a`\xacQ\x94Kf1\u01a0\x16E\u0635k\x17\xc2(\x82q\x89\xf2\xc3\xd1\x00\xf1(vZ\xec\x11\xe2$\xc1h\x94 \xd6\x1aqb\x15%a\x10@\x90\x80\xd6\x15\xed&_\u0152\u068b\x0e\x86\xe8\xf5\xfa.\x1d\a\x19\r\xe2\xeb\x9b\r\x19\x90.\u058by\xb2\x0eg\xbax\u03a6f5\x8c1\x88\xea\xd3\xd8s\xe8&\xec\xbd\xeav\x1c\xba\xe1=\xd8u\xc5\u03687\x16@\x10\x88G\x03\xc4\xfd\xae\xe5\x9aS\xe3a7\x80\x02M\xa0\x8e\xd5\x15\xf7\x17\x03\vz\xba\xbaI\xe61\x1f\x05\xed6\xf2\u034d\x05\xf2v\x82\xc0M>\xc2\x01\x1c\x95\x00\x8e\xb6\xe4h\xb6\xc0\x1f\x93\x03\vL>\x1cF\xda-\xb9\xa9d\xc9X^=\xe8k\u02041jJK\xbb\x88\x92\xf1&.\xf9+'\x82`\xd03P}\xe3\x1a\x9e\x94/Zi\xdf\u0420\xb0k\x9c4\x92\xe4\xb3+9\xb7QvQ+\xe6\u0125\xf4\x910\x06\xd0\xc5\xd7M&\tH3\x06\xb3\nZ\u0456\x15\xbaH\xac\xdd.\x19\x17\x00\x92\x1a\xaa\x15\xe6\xde\xdct\x03\t\x10\xe5\xc5U\xc1\x84\u051d\x93I\x124\xa6\x16\x10\u0567\xd0k\xafdW*\xa4\x02\t\x85\xcd\xcdM\x1c?\xfe\xf2\xad\xcc<OD\xab\x97\xc1\xfc't\xa4@\xce\u0335\x9f\xfb\xb9\x0f}tk\x8ae/n\xbb\xed\xcd\x13\xca\x18\x93\x99T\xb3\x90HD\x1d\x89\xac\x03\xc1\f\x90\x8c@j\x04\x19\x0e\xa1Z\t\xe6wj\xcc\x1f:\x82[n\xbb\x15<\xe8\xa1\xdf\xed\xe2\xfc\xca\x1a\xce^\\\xc1\x85\xd5\r\\\\\xdb\xc0F\xbb\x8b\xcdN\x0f\x83\xc1\b\x86\x19JI\xb4\x9aM\xabYo6\xb1w\xef^|\xf7\xb1g\xf0\xc7\u007f\xf2\xe78\u007fq\xc5\x15\x8c\xd2\xd9u\xdaiN!\xed\xec\xf4p0\xc0\xee\u077b\xf0\x8f\xfe\u046f\xe1\x9ak\xae\xc9\u0498666\xd0i\xb7\xd1\xdeX\xc7\xfa\xfa\n\xba\x1b\x1b\xe8t\xdahon\xe0\xc2\xca*\xce^X\xc5\u064bk\xd0\xed6(\b\x10\x86\xa1m^f\x82\x04\xbf\xb2v\x9bQ\xe3{p\x97'\xdc\xf3*\u06ceG\x9b\x8c\xf7\x96R@\xa9\xc0N\xda:E\n{\xde\xdd\xe9\x13\xde\xf13\xff\f\xd7\xde\xf9\v0\x89\x8dS\xd3#;\xb8\u00de%\xad\x1f\x94\x93\xc9#\x13 \xechp@\xe8-\x04[\xf2\xccU \xe4\xf3\xe9AO[EG*\xe1c[\xc1\x96\xcb\xee2\xddP\xf0\xa2'\x94\x02 \xbc\xfeFy\xcd\xcc\xc7\x12!\x98a `T\x9e\xbeC\x06 c\x10i\x86H$\u2980\x91Te\xbb\xf2\x9a\x00]\r\fT\xcf5<\xb3\x99z\xce\x16\x9b\xaa\xe9\x9d*\x9a\xc5_,\x8btU\xeeMO\xbe\xe9Z\xbai\x14\xf9\xe2&\xd2\xd73}\x0e\rD\x1d\r\x10a0+\v\xd7[\xb8V\xaf*O\xe9\x1b\"\x012\xf9 \x1c\x15,,+\x9a\xa1\xeewKw\x83i6\b\x1bM\x84a\u0765G\x190\v\x80\xac\xaa%N\x12\xb4\xdb\xed\xbb?\xfd\xa9?n\x02X\xfd\xf6\xb7\xbfEo\u007f\xfb;\xf92\x98\xff\x18\x8f\xbf\xfd\xdb\xff\x17?\xfb\xb3?\x0f\x00\xf8\u02ff\xfc\xf3_|\xf1\u0157\x0e\xb9@\x8b\xca\xe3\x8a+\x0e\u399bn\xb6\xb4\u0224\xe6\v3\xc0:\xbfcI\x02A\x03\x1c6\x900\x90\xc0~_\x98\x04\xb2\x16CL\xc7h\x98\x11\x0e\x1dHpH'\xf9\xa8}V*\n\xe7\xc5!\x01\x15\x022\x00d\b\x84\xf3\xe8\x87\u007f\x01\xf3'\u007f\x91=\xdc\xd2\x13:\xa3I\xa4R\x102\x00L\x02%%\xae\xbd\xf6j\xbc\xfd\xedo\xc3\xe6\xe6f\xe6p\u021cs\n\xc9p\x80Q\xaf\x8d^{\x1d\xab\x17\xcf\xe3\xe4\xa9e<\xf5\xcc\xf3x\xf6\x85\x17q\xf2\xf4\x19\x9c=w\x01k\xab\xeb%2\u0213tIi\x1b\xba\xe4\xbb4\xe6\x8dd6V}B\x04\u0522\b;w,b\u03de=\u0635s'@\xc0\x13\x8f?\x89\u04e7\x97\x01\u0599w\xb7\xcfs\f\xba\x1bX?\u007f\u00a93b$\x83\xbeK|O\xb7\xda\xc5Qr.\xd9\x13\x90f\x84\xed\x04IH\x18N\xa9\xca\u0127*\xd4\xf3\x15\x11\xaao2j#\xf3Ow\xf6\xa9\xe4\x05*\\r\x04\x9a_\xdd\xf7}\x95H\x8aod\f\x88\x05t\xe0Q.\xec,];\x8eviI\xe8\x90\n\xd2\xcbW\v\xe8L\x80\x1a\xda\ub509\x17rlR*\xc9\x1ad\tT\x84A\x8ceUy\xe2C\xf2c\xde\xc6\xe5/\x05j+\xe36\xec\xa0Nz\r\xe9N\x80\u025eO\xd0\xd30\x02\x18MI\xbb\xc0q\xf1=\x13\xb1\xab\xcau\xdep\x15d+\xf0\x14\xc8\xd9\u07ce\x19\xd7\xc4u;\u0374\x1a'o'J`\xa8Z\x13A\xbd\x99\xdd\u06e9m\x85\x94\x01t\x1c\xe3\xc5\x17_\x14w\xdcq\xfb\x8d\x00N\xbd\x9e\x81\xfcu\x03\xe6)\x90\x03\xc0\x97\xbe\xf4\x1f\xefn\xb7;A\u0784\x1b\u007f\xfdo\xbf\xfdvLO\xcf\u0098W)\x1fe.\xf2\xa1\x19\xaaH\x18)aT-\xff\x00\xb0\xb1_\xde\u0523eo\xdcG\x86\x84\xdbn\xdbG(\x002\xaaCJY\xa9\xbca\xb6\u0737\xfd\xbd\xc6\xf9\x93\x8c\x9c/L\xe2Gdf\x8a\x93\xb0\xdeD\xd4hazi7\xf6\\q\rn\xba\xdd\xe0\u00ff\xa8\xb1\xb2r\x11/\xbet\x1c'N\x9c\u00a9\xe5\u04f8\xb8\xb2\x82N\xa7k\a\xa2\x9c\xbcpc\xb3\x9d\xfd{\xa2mU\x9dv\xf9\xc30@\xa3\xd9\xc0\xd2\xe2\"\x0e\x1d<\x88\xab\xaf\xbe\n{\xf7\xee\u016e]\xbb\xb0o\xdf^\x1c<x\x00'N\x9c\xc4?\xff\xe7\xff\x1d\x8e\x1f\u007f\t\u02bb\xa6\xf2q\xfc\u026f\xe1\u01bb\xfe+\x84\xf5i\x18A\x99\xde:\xdb\xees\xa9\xa2\x857\xdc\x04\xfb\xe1\x8e\xda\x1aF\n$u1\x06\xe8\xe5\x14x\xdf\xdeD\x0es \xf7\x91\xd1R\x10\xe3,Ju\x95:)\xabjkb\x86\r\xe7\xd6\b\xce\a\x05,`\x82\u0519\xd2\x01\x9ea\x04}K\xff\x8cZrL\xed\xf2j\x80\\$\x8c\xa0ke\x96\x80\x1b\xb5w\x1c=\x00\x98t\x98\xa9\x1c\u00baU*I%A\xef5\x1f\xfd\u0184\x93\xefd\x03E\"\xc3\xf5\xfc'\xdd\xf7D\xc2\bz\x06L\x84x\xca\u06d1P^\x95\u02c4\u11feP\xea\xf4\u51842\xd5JV\xa9\vgfWm_\xcf`\x04Q\x1dA\xd8\xf0z,\xf63\xa4\x82\x10I\xdc\u01c5\v\x17p\xee\u0739w\x00x\u0753\xe6\xaf\v0\xbf\xef\xbeo\xe2\x1d\xefx\x17\x98\xf9\xc0\xddw\xbf\xff\xb6\xf5\xf5uL\xda%\xce\xcf\xcf\xe1\xfd\xef\u007f\x9f\xad\n\xf5\x0f2\xa1;f\x13X\xaa\xfel\xb50\xf6\xab\xb3F\xa5.\xfc\xac\x8a\xecdd\x10\x04c\x1d\xf8L\xf2\xe84\xe0\x96\x05J\x10H\x81(\n\x11\x85\xa1\u05ec\xca\x03t\v\x897\xee\x1f\r\t,\xee\x9f\xc1\x9e\xc3G\x10*i\xf5\xe1\x83>\xfa\xfd\x81\xd5\xc7\xc71\xfa\xfd!\xda\xdd\x0e\xd6V\xd7\xd0\xed\xf6p\xe1\xe2\nVVWQ\x8bB4\x9b-LOOaff\x06\v\xf3\xf3\u063d{\x17v\xef\xd9\r%\xc3\x02\xa0\x19\xc3h6\x1b\u0540\xe3}\xa2.,?\x83ao\x13\xb5\xd6|\xb6) S\xad\xe4\xe0B\x022y\x1fpv[tk\xe4\xe4\x03\x05U\x045A\xb8E\xa0\xe3\x14\x1d&'L\x84\x01D\x89\x9a+\xe7\xbf\xfaCU<\x11\xecP$\xec\xbd+\xc8\xdf%\x9fr\"\xc8\u0100\x98\xa0\xc3<S-\xa5\"\xe4\xc8 j;\u06a5\xe1@\xae\xd4s\xac\x02rb \xec\x19(w\x9d\x9c. Nv\xc9\xce\r\x91\fg=\x99\xccLml;\xe3]\u05eb$xrz$\x9f\x91H\xa7`\xcb\v,9u\x960l\xad\x05\b\x18\xb5\xf2k\x15\x86\xa1F%Z({\xaf\xc9\u04d0{\u0397\xbe\xcf(;\xa5\r\n\xf9\xe0v\xd7+\x03\x84\xb5&\x84\x90\xd9k\x04\x10\x84P \x92\x88\xe3\x04\xedv\xfb\xed\xccLD\xc4\x0f>\xf8]\xdcq\xc7[.\x83\xf9\x8f\xeb\xd8\xd8\xd8 \x00\xfc\x99\xcf\xfc\xfb\x0ft\xbb\xddk:\x9d\x0e\xa4\x94\x94s\xc0\xf9q\xfd\xf5\xd7\xe3\xb6\xdbn\u035a\x9f?\xfa\x83\xc7\xcc\xf1/u\xa4\xe1\n\x85f\x8f\x1b\xc9O\xc1<=W\x02\xa3^\x8fP\v#\x98V=W\xba\x94T\r\x13\xf3\x9f\x19\x18%\x89\x05\x91\xb0\x8e\xe9Z3\xf7A\x17\x02\xb9\x8b4\xd0\xedn\xa2\xd3\xe9bn~\x0ea\x10\x00\xf0+m\x8d8\x1ea\x10ws\xafv\"\xec\u0631\v\xd7\\s\r\xbe\xf0\x85\xad\xf3p\x87\xbd\r\xac_8\x8e\xe9\x1d\a\x1c\xf7\xa9\x8bzB\xaeh\xf9yZ?\"\xbb.\u02a1A\xe8\xc0/\xad\xd0=?\xb2\xfc\xda\xc9V\xdeaGC\r\xd8\xf1\xd3\xde\xebm\xbc\x99\xf8J\x19\xe28\xe5P\xdd\x13%\x94'\x97\xa8\x9c\xef\xea+/8\xe7\x93e\f\x18i\x95\x1a~\xcf@\x8c\x80P'\x19\xa0'\x91\x18\xabp\u02e7\x915<\xd3\x06d\xdah-{\x94\xbb\xc1\x1e\x9atY\u07a9\x17\xbc\x1a\xc6\x1a\xa5\xbe\xd8\xdc\xf9\xa5\xf89\xa0n\x95)\x18\xba\xa5\xef\x95qrA\xe7Q\x1e\fL\x01\xd0\xd5\xc8\xca\x11\v\xc6\x12\xee\x9c\x04\xa8@\x162\xb9\xb6W\x99=,uA\xd2A?A\x12Q\xad\t\xa9\x82\xcc?)-\x88H\x06l\x18\xb4\xbe\xbe~\x13\x80\x10\xc0\xf0\xde{?{\xb92\xffq\x1d.\xa3\x8f\x01\xe0\xfe\xfb\xbfs\xd3\xc6\xc6\x06\x88(!\"U\x06r\xa5\x14~\xfe\xe7\u007f\x0e33\xf3`N\xb6\xcd5\xc4\u039f;\x05D\xf2\x06\x91R\xfa%\xbd\x96\u010d\u29c0\x9fz\u02cc\x93\u00d3K&\x9f\re\x9d\x14\xa3\xbf8\xe7\x0eG\xa3\xa1\xb5!0\x06\x83\xc1\x00\xfe\xe2\xe8\x9fgff\xe4\x16\x9c;\xef\xbc\x13\xd3\xd3\u04d6\u04dft\xcd\xc3\x1e\xd6\xce\x1e\u00fek\xdf\x01\x01\x01\u05ba$s+\x0e\xe2p\xa9$\xe6\xd4\xc3\xddyZ\xa7M\xb4\xa4N\xb9%k\xa9\xb8\fz\x1aA?\x1fa\xf7\xb7\xfad&\x039\x95\xaa\xf3j\u04ba\xb8\xf8PE\r\xcf\xf0\ar\xc6w-V\xa5\xe1\x93\xea\xf9\x0f\x92\xb6<?%\fj\xb2\xb5\x02(\a\u007f\xbb\x1fUC\xcbA\v\x9b\u049c\rh\x8a\xc4y\x94\x9bTzY:\xb1TN\x99\xb1\x89\x159nTR\xafT*\xe0K\x04\xff%z\r)0\xa7\xfe*\xd0\f\xd5\xd7`\x01\xc4u\xe1t\xe5\x9cW\xe2\xc8\u04cf\u021f\xae\xf6\xa98\x93\xae_\x9c\x85W\xa7V\xb8%\xa7^\x04Q\vB\x05H\x86\x03/$\x05PA@\xc3Q\x1f/\xbcp\x94\xef\xbb\xef\x1b\xd7\x02x\xfc\xf7\u007f\xff\xf7_\xb7`.\xb6\xf3\xc9}\xef{\x0f\xe0\x8b_\xfc\xbc\xcd\x14a}\xd3\xfa\xfa\xfa\xddg\u039cA\x10X\x02\xb2\f\xe67\xdcp=\xee\xb9\xe7\x83\x0e@\xb7\x8f\u0742U\x83\x98\x02\xb5\xa2\x94D\x18\x86\b\xc3\x00\xd25$\x01`0\x18\xa0\xeb\xbcd\xb2\xc6\x0e\xe7\tA\xec\x15\x98\x93\xbe\x8a\xe0\ue0f2\xc8\x16\x10!\xadSd\xbd^/\x9c\x97\x942;\x9fr\xd0-\xb9O\xc1-\xb7\u070c\xab\xaf>\xb2\xf5\"l4:kg\xdd(5\xd2`\u0209\xe7X\xacx\x91i\rS)\xa1\x1c\x1aD\xed\x04j`\x8a\u041a6<\x9d\x17\x89\xf0\u0324\xc8G\xe8\t\x9e\xe1\xe3\r\xc7\xd2$\u0418/@\x99\xf2'g\x04@\x05&\xa6\xfc\v\u04a6+\x19[9\xdbp\b\u05f0t\ra\xe1\xc6\xe1\xa3M\x8d\xa8\xe3\u4525\u0758\x8c\x19a'\xb1\xa1\r\x8cl\xb1SCc\xadm\xc7v <F\xd10\x95\xae\u008bB,<\xd0\xe7z\xa8\xf4vy\xefW\xf1\xdby\fK\xc1r\xc1_\xb8\u063e\x06AW#\xda\xd4\x10\xc3\xdc:\x80\xb4\xb7\xbb\u0206\x95\xbc\xe9k\xce\xdf\x13.u\xbe\ty\xfaRZ\xb4\xc00\xa2\xfa4TP+\xd99\x1b\b!\x91$\x8cv\xbb=\xf5\xf8c\x8f\xbf\ue8c6\xb65\x98\xdf~\xfb\x9dx\xf9\xe5\x97\x05\x00|\xe63\xff\xe1\xc6\xf5\xf5\xf57t:]H)E\xf9\u62e2\x10\xf7\xdc\xf3A\x1c:t\xa8\xc4Y\xfe\xe7?\x92\xa4\xa8COu\xe6\xf5z\r\xf5z\x03J\xc9\f8\xbb\xdd.666~\"\xe7\xf5ZS\xc9S\x8f\xef\u00c7\x0f\xe3\xc0\x81\x03\x97||o\xf3|\xb1\xea+\x15v\xc5\x05\xa8\b<i\xb5I\x0e\x00\x89\xedV<h\xeb\xcc\xff#\x05\t\xa1\xed\xe4\xa3\x1a\x8d\x0f\x90\xa4\xcd\xc6q\xcb\xd7-\xb8\xf1W\xc5\x18\xa3\xb0\xa3\xca\u0399*\x00-\xc5L\x93'\xcb\v\xcd\x10\xb1\xc9\x12\u007f2k\x01\xb6\xbc\u007f\xd0\u0548\xda\xda\xd2\x0f\xa9E\x80\xb1\x96\xb6i?\xc0.\n\x06\"6N~\xc9\x05\xb9g\xa1\x97\xc1\xbc\u014e\x8e=\x15\x8b_N\x97\xab\xec\n\x13\x9c\t\xfd&\xae\xd0\x1f\xfa\n\xa6t'a\xdf7\xe3\xed\xa0r\xeb\x01\u02e1\xa7\xc1\x14\xfe\b\xac\xb7+\xa2\x1c\u0229\xd4\xc2b\xe3f7\xea3\b\x82\x1a\x8a\x96aY\xae\x17\x12mp\xf2\xd4\u0277\xa6?\xfb\xc2\v\xcf]\xa6Y~\xd4\xc7p\xd8C\x145\f\x00<\xf5\xd4\xf7oXYY\x013'\u032c\xca|\xf9\x9e={p\xf7\xdd\xefG\x18\xd6\x11\u01c3mu\x1d\xfd~?\xdb)\xf8\xd4E\xb3\xd9B\x18\x86\xd0:\xc1h4\xca\x1e\xeb\x1a\xbc\xdb\xf20\xc6 \bj8p`\xff%\x1f;\xe8m\xc0h\x03\x91\"o%\xe7_\b\xb5\xcc=V\xbd\xe6Y\x96\xbda,\xc5@\xd0\x18\xc2\xfa\x9d\x10[zB\x0e\xbd\x91\xf5\u0313e\\\xcd\xc1\xd5L\xc0\x04\x9c\xa3\xf1^g\x15\x1d\xe3\x11[\xfe\xa3*\x1d\xc2M\xf1y\u0205J\xb0\xb4\xd2M\xe3\xe8\x17\vt\xc6\r\x19\x01I$\x10\xf45\xd4\xc0NU\x92N\x17\x81*\x8e\xc8\xef\xed\xa0\xd2{f\xfcJ&\u0343\xfa=\x8e\\\xc0\x88R\x03\xba@:\xf9\x95\xbf\xe3\xd1\xd9\u04e2s*\xfc2p\n\x16O?\xc0\x1e%\x95Z\u0792\xa8x\xf2rR\x91\xd7\x1b@\xaef\x01\x03a\xad\x05\xa9B\xef\xc7)\xdd\x12A*\u0143\xc1\x90\x8e\x1f\u007f\xf9:f\x8e\x88hx\xe4\xc85\x97+\xf3\x1f\xf5q\uff5f\x15\x96\x16\u3e53'O\xbe\xf1\xf4\xe9W\x10\x04J\xa4^ ~\xa5\xfb\xaew\xbd\v\xd7]w\x1d\x00\xfdcj|\xfe\xe0G\xbb\xdd\xc6`0(\x809\xc0\x98r\x8e\x87R*\xe4\xd1b\xc0p8\xda\xd6=\f\x00\u063f\xff\x00\xa2(\u06aahE<\xe8\x01\xc6\xe4\f2\x97\x84\u0528\xb2\xf0\xab\xa2[\xf2\xc7\b\u00d0C\xa7X\x19\xd9\u051c\xa0\xaf]e[\x1a\vu\xe0^V\xd0T\xa7)Up\u0095\xc9;\xe3\xe1\xd5)\x9c\xa7\xbe\x8b\\\x1e\x83\xaf\u068a\x94+\xf6t2\u0564\x8b\x92\xfd\xb3\xa5\x974\xea\xeb\x89uB4(6w+\x1a\x80\xd9\x16!\xf5\xab\xa7\xf2\xe3h\x02\x17\xce\xc5\xef\x8f\xfdL\xfe\xbdI\xe2L*?\xbf\xef\xf2\xe0\x14/Y\xe5\xads\xc33\x91\x18\bc\xb2\xc57\u02f8\xf5\xa7\xb7Q\xf9\x16\xa3\xb8\x01b\x18\xef^e6\x88\xeaS\x90A-[\xf5\x84H-4\bA\x10Q\xb77\xc0\xa9S\xcb\xf3\xdf\xfd\xce}w\\\xa6Y~L\xc7\xc6\u01ba\x02\x80\xcf}\xeeoo%\xa2{677@D\xc27u\xb2U\xf9n|\xf8\xc3?\x8f\xf9\xf9%l\xe5o\xfe\x93>\u049b\xf0\xe2\u014b\x8ef\xf1\xd2\xe2\x19XXX\xc0\xe2\xe2\x12\xa4\x14\x85\x9b6\xbd\x86\xd7J\x83\xfc$\x8f\x03\a\xf6cvvv\xe2N\x1b\x00\xe2a\x17Z\xc7VZF^\xfd\xc6\xe3UVUm\x9c\x8e\xc9Sb\xb2*\x94\x9c\xc4P\x8e\x18\xd1f\x82h3\xc9\xd2s\xc8\x14<(\xf3m\xbb).\x1a%bd\x8c\"\x98h\x81[\xe2\xceil)\xe0\U00045a7a\x9e/^s\x89\x82\xb1\xf6\xafy*\x85\xdf/ ]\xc4V\xf6\xa5\x92\\\u0352\xe4\x97E\xd5\x15\xfaV\x8c\xe4\x18\xc5R\xf4\n*\xfe..\x18;\xfbt\fy\x14P\xba\xab\xf0\xcd\xce\xc8 7\xec\xf2{&\x94\xe7\v Wv\xe6\v\xa6\x97\x9a\x95\xf7\x96\u0606\u0478>p\u0618B\x10\u05b2\xe9\xe7l\x91\x80}nm\x80~\u007f\xb0\xe3[\u07fa\xef\xb6\xcb`\xfec:\xbe\xf2\x95\xaf\x02\x00\x9e|\xf2\xc9\xfd) \xa6\x1e&>\x98\xdfu\xd7]\xb8\xf3\xce;\xb0\x9d\x92\x9f\x98\x19RJ\f\x06=\x9c;w\xce\xed (\vv`6\xbc\xb8\xb8\x88\xc5\xc5\xc5\xec\u3402w\xa7\xd3\xde\xf6`\xbeg\xcfn\xcc\xce\u030c\u05fa^E8\x1at0\u8b3b\xc1\x92\xbc:\x1foA\x96A&\xafb\xd3\x11|\x91\x18\x88\xc4d`G\xda\x0e\xa1\x84\xa9\xef\x8aO\xf1\xfaO\xa7\x91+;x\x12R\x95\xbb{\x15 _Q\x02\xf3%8\xf7\x94N\xf0\xab[\xda\nI=u#a\xbcj\x17\x89\xe5\xd9\xc9\u016be\x8a\x98\x8ak\xcb\xcd\xcf\xf2\x05\x94+\x17\x13.]3UL\xc7\xfa\x12L\xaf\xa3K\x97\xc0\u007f\xef/i\x837\xdd}X\x130d\x8d\xf1|\xb9\xa3\xc2\xe2d?\x18\"\u05da\xfb\x97\xe4}>\xb2\u0701\xb1\xb4sF\x10\u05ad\xd6\xdc\x19\xd51\xe7=\x9c\xd4\xcb?I4\x8e\x1d?\xfe\xe6\xcb`\xfec:>\xfb\u067f\x1e1s\xb3\xdd\xee\xdc\xf5\xd2K/e\xda\xecL\x93M\x84\xc5\xc5E\xdc}\xf7\xdd\u0631c7\xfa\xfd\xbe'\xe5\xfb\xcf\u007fH)\xb1\xba\xba\x82W^9\xe3\xe1\\\xf6\xa1\xa1\xe9\xe9\xe9daa^KY4\xac\xe8t:H\xe3\xed\xb6S#7\xa7Y\x18\a\x0e\x1c\xc0\xae\u077b*Y\x96\xf4\x03\x95\x8c\xfa\xe8wW*\u22f6\x9c\xa1D\x85p8\xab\xca-\x18X\x0f\x0f\x998\xe5CR\f]`\xd7=M\xab\xdd1)P\x01Z\xab\xd4*\\\xcd%s\xd5u\xe4\xb1\x19T\xb5\xb3\x98D\x85\xa0\x94\xfa\xe4I\xf7\x84W\xb9\xca$\xe7\u01b3\xa6\xb03\r3\"\x9f&\x9d\u073b\xad\x18\x10\xaa\u0715x\x8f\x13\xe4\xd3\xe4[\x97\xf1\x13\xa2\xf1\xaa\xa2\aS\xed{A*\xca\\\x99\x15\x9aSE\x15\xd4\x11\xd1\x18;\x97[\xafsa\xc6K\x1b\x06\x84Dcz\x11B\x05\u0678?\xdc\xf0\x143 U\xc0k\x1b\x9bX>\xfd\xca5\u033c\x04\x00\x0f=\xf4\xc0e0\xffQs\xb3\xcf<\xf3\xd4\xe2\xf2\xf2\xf2\xf5\xa9\x17K\xd97\xfb\x86\x1b\xae\xc7-\xb7\xbci\x9b\x9e\xbf\xc4\xea\xea*.:\x83-\xf6R)\xa2(\xc4\xe6\xe6\u01a3\xccx\xb1^\xaf;\x89o\u0299\x0f\x91$\xf16\xae\xcc\rv\xef\u078d\x03\xfb\xf7W\\w~\x1d:\x1e`\xd0]\xf7\xb24=\xdeSL\x029_\xcb]\xaa{9\xa5S\\\x95\x97\xd2'\xee\xef\xb6\x1a\xf6\xb7\xff\xec\xe5\x97Va\xdb\xe4\x99\xcfJ\xe8\xa5\xf2\xa2S\x04\xc1\xf2d%\xf1\xf8\xba\xc0\xd9uQ\xe15K\u007fF8n\xc0\xf2\xc8\xc8\x1d\x1eK\xe1\x12\x82\x91}\u007f\xe2\nI\x84KxB\x8e\u007f\x9f\xca\xc0?\xb9M\\\xa4\u04cb\xa4\x13\x97h\xa4\ft\x99KE}\xc9*\xc0\xf5\v\xb2\xc1*\xf7\x021sa\xea\x17\xdeD4;\x9bg\xf6\u04d1\xdc{\x94\xba\x826\xa6\x16 \x83\xc8\xd9L[\xf9\xadt\xf2[)\x15u{\x03\xb4\xdb\xed\xbd\xdf\xf8\xc6W\xdf\f\x00\x0f<\xf0=z\xfe\xf9g.\x83\xf9\x8f\x92o\xfe\xeew\x1f\xb8\xa2\xdf\xef\xdf<\x1c\x8e\n\x14\x8b}\x13$n\xbf\xfdv\x1c9r\x04I2\u0716\u0dfe\xbe\x81\x8d\x8d\xf51\xc0SJ\xa1\xd3\xe9.\x03\xb8\x10\x04AA\x156\x18\f\x11\xc7\u0276\x05sc\f\x84\bp\xe8\xf0!\x04a\xe8\xde/Q\u0612\x03\xc0h8@o\xf3b\xb6\x95\x1d\xa3A\xca3\xf4\xa8\xaa\U000386be5\xe34\xae0\u0784\x9f\x86\r\xd64\\fn\xc6+\xd3J~\x9b*P\x8bQ!\x8es~+\x13\b\x94\t\x8bU\xc1E\xdd\xef\xe4\x19/&\x8d\xb9\xe8\x1b\xe4-\x19\xe94#U\xbdp\x15\xd5\xebX5=\x19\xc1+\u007f\x80\xb6\xdaHU\xf6\x19J\xa6\xf3\x85]VYic\r\xc1\x8c$\xbb\x18g\x93\xab\xe9\x1f\u075bm\xf2\xd7\xc3.\fn_\x94\xe9\xd1}\xab\x02\xcf7\x97\t\xf5\xc6\x1c\xa4T\xf9\u0535\x90Y\x1c\xa3\x10\x02Z\x1b6\x9aw<\xfa\xe8\xa37\x02@\xb3\u0660\xab\xaf~\xe3e0\xffQ\x1e\x17/\xae\xdc\xf1\xd2K/\x15\x12m\xd2\xcaw\xff\xfe}x\xeb[\xef\x84R!\x92d{\x82\xdf\xc6\xc6FiR2o\x82\xee\u0631C\xd6\xebuYV\xdf\f\x06\x03\f\x062\b\u0272\x00\x00 \x00IDAT\x83mE\x19U\xed\x9a\x0e\x1f>\x84\u9a69\xb1\x0fl\xba\xd8&\xc3.:\xeb\xe7,\xb0\b\x0f\\\x04\xc1\x10U\x83\xdd\x16\xdc6y\xab\xa1\x9f\x9eC\x1e\xc0g\x14E\x95\x05,\x97\xaa\xec-\xe9\x0f\xae`\x87h\xac\x06\xcf]B\x18[\xea\xd4y\x12oQE\xcf\xe4\x8dP\xcaV?\xf7{\xd3T\xab\u0523\x9e\x8a_\xd5\x05\xb7G@\xf3$\x90/7\xa7/\xd1[(}\xffR\x8f\x18_\x84\xb9\xb0\xf8\xb0\xb0\xbb5#\xc9\xd2G\xde\xee\x80Y\xdbIO\x14\x15\xe5\xe9k@yW\xd4\xfa\xec\xa7\xf3\xfeY\xc5\x0e\xd4[s\x90\xd2N;[\x03/k\xd6k\\\xffA\xca \xd9\xd8l\xe3\u0631\xe3\xd7\x01\xc0\xaf\xfd\xda\u007fc\xb6\x1b\xc5\xf9\xba\x04\xf3\x17^x6\x05\x85\xc6\xf9\xf3\xe7\xefX]]-LC\xa6\xc7\xcd7\xbf\tw\xdcq'\x00\xb3m\xab\xd8N\xa7\x83^\xaf\xe7\xed8\xd8\xf1\xe9\n\xbbw\xef\x0e\xa2(\x94eJ\xa5\xdb\xedf\xfc\xffv\xbc\xa1\xd2s\u06b3g/Z\xadVa'\x95]\xa7\x10\x88G=\xf46\xce\xe7\x8a\v\u05c02\xe9\xcc5Uh\xb1\xc9\x17\xf6Q>8\xefeQ\x92\xcb\xde\x1c\xab&\x9dv9\xe5\xd0s\x19\x1bc\xa2\xeb\n\xf3\x18\xc8\xfa\x93\x9dE\xb8\u326al\xa2\t\xca\xf2R(\x84O@\x8c\xd7\xc3\xf9.B\xf8\u050a3\x912 (E\b\x03\x81P\xd8\xec\x8b\xc9q\xa0<q\xb1\u068aIz\x8d\xfb\xe71*\x87'\xf9\xed\xa0\x9a\xb11\x04hE\xd0\xd2\x02:$\x81\x94\u02f5ufY\u033e\x04\xd4\xf3\xdaO\xd5+\xee\x9e4\x99\xaf>r\xa0vE`\xad\xb5\x00\x19\xd4\xc0Z\xc3\xc0\a~[\xf1\x87a(\xce_\xb8\x88\u04e7\xcf\x1ca\xe6\x03\x00\xf0\xb9\xcf\xfd\xad\xb8\f\xe6?\xe4\xf1g\u007f\xf6g\x00\x80\xfb\xef\xbfof}}\xfdm\xa3\xd1(\x03\xf2\xb4\x8am6\x9bx\xe7;\u07ce\x9d;w#I\xe2m\xfb\x02\xf7\xfb=\xc4qR\u079br\xad\x16A)\xb9<\x1c\x0e/R\xe6\xe4fo\u0635\xb5ulll\xa2h|\xb5\xfd\xc0|ii\x11\xadV\xb3\xa2\xbe\xb4\n\t\x93\xc4\xe8\xb5/ \x89\a\xce\x1a\x18\xa5\x00\x1f\xb6\x91k4\x89\x06\xc8a\x8f\xfd\xea\xab\x04&\\\x1a{\x1c\x93\xe5y\xcd4\xa6I\xac\x82\xaf\x14/\u007f\x8b\x8b\xe7\xbc\x05A\xe1\xab\xce\v\xc3<\x15\x89B\xd5\xc4\xc6\xf8\xa4,\x180\x12\x90M\x89\xa8\xa5\x10\x86@ \tJ\xe6>\xf4\xc4\x15\r\u05can$\x8fq\xe2\x97F\xf7\\\xc7R\x150W\xfe\x15\xc5&\x01c2{\xc5\x00H\x11\u0114\x84lI\x04\x01!\n\b\xb5@ \f\x04TV\xa1;\xddb\xd5$+\xe5\x9c:\x17\x9a\xb1\x949*\x02@\x106\x11\u0567\xb3\u0140\xa4\xcc\x1dH\xad\xfcQ\f\x87#lln^\xfb\u007f\xfc\x9f\x9f~\x03\x00\xbcp\xf4\xe8e0\xffa\x8f\xdf\xfe\xed\xdf\x01\x00\x9c={\u6593'O-\xd9!\x1a\xce\xfcM\x00\xe0\xdak\xaf\xc5\a>pw\xc6\xe1nW\u03bf\xdd\xeed&[)\bZ\x1b\xd9&N\x9dZ~\xe4\u0529S/*\x15xJ\x17`mm-\x1b\xe9\xdf\u0395\xf9\x8e\x1d;0==3\x0emi\xf1\u010c\xc1\xe6\n\x86\xed5\xab\x15N\xa9\x11m\xdcT \x95\xfa\x88\xc5\xe4\xf7\xb1\xd6${\x9c\xabo\x8d\x9am\u0569dKR\x04\x95,6\x8f\xb6\xc6\xcf\u025c\x0f\x15\x95\x13\x19\x85K\x15\x04L\xe9\x19y<gt\"xzC6 \xa7ZiJD{\"\x04\x8b!d$A\x82\x90Z\xe8\xb3\u007f}\x19h\x93w\x05v1\xb41u<A;8\x89\x17\xa1\t\xec\x17c\x12AUY\xecsi\xffAn\x97Q\x13\x88\xe6\x034\x96B4[\x01\x1a\x8a\x10* \f\x05\x02E\x90\x82\xb2\x1dV\xb6\xa0W%\xfae~\xff9\xfd\x92\ue10d1 \xa1\u041c\xd9\xe1b\x13\x01!\x03{O\xe6\x13\xd9\xc4$x\xb3\u075ey\uaa67n\x06\x80\u007f\xfb\x87\u007ft\x99f\xf9Q\x1dG\x8f\xbex\xd7\xf2\xf2\xa9\xac\xf2\xf6md\xef\xb9\xe7\xa7q\xed\xb57\x809\u0656\xf2=!\x04\x8c\x19as\xb3\x8dr\xf043s\xb3\xd9\xc4\xec\xect\u007fccs\x98\xe7n\xd2\x04\x9e}{\x82\xf9\xdc\xdc\x1c\x16\x16\xe6|[u\xf7}\x93\x01\u06f0\xbb\x81A{\x1d\x82\xf3\xc6V:\xaa]\xfc\xca\x05\u02570\x85\xf4>\xacN\xf5\xe0\x9agF9n\x9e\xaa\xc2\xe0\x90\xed\x02\nU$\x97\x99\x80\x8am\x02\x95\x1a\x9e\xa5\tN\x06\x17\n\u0182\xbb\x1f<\x1e\xa6b\xa2t\\UB\x85\x06\xb1\x0e\bIK\"\xdc\x11!\x9c\r f$h1\x80\x98\x92\x10\x81\x00I\v\xeae\x0e=\xd5a\x17TC\\E\xdb\xd3\x18\u060eW\xdf\xc5\xf0\x16\xe6I\xb2E\x14'@\xa9\x94?\x97>\x8b\x80\xa3\x8b\bAK!\x9cRv\xc71\xab C\x01!\t\" \u021a\x80t\xa9D\xcc\xc5\x1d\x12\xfbrt\xdf\n\xdf\x05\x84\xa3\xe0\x9eh d\x80\u9e7d\x10$a\xb4v\x01\xebA\xf6\x9c\x86\x19R\x05|\xe6\xcc9\xf4\xba\xbd\x9fb\xe6\xc5S'N$\x97\xc1\xfc\x878^~\xf9X\xf6\xe7S\xa7\x96\xdf\xea\xab:\xd2\n|\u07fe}\xf8\u065f\xfd\x10\x00\x8c\x01\xe5v\xaa\xccG\xa3\x18\x9dN\xb7\xb0s \u05d4i4\x1a8t\xe8p\x90$\x894.\x9d&\xa5Y:\x9dv68\xb4}\x9b0\x8c0\f\xb1\xb8\xb8\b%U\x11<\xbd\xff\x8d\xfam\f;k\x99\u01ca\xc8\x1a\x96\\P5\x14E-\x13\\\x14\xab\xceB\x10\x92\x9aDR\xb7\xde\xd8,\x8b\xcd@\x1f\x18\x99\xa80\x0e\xee+\u034b\xd4CU\x1d\u0293Kx\xf67\xf6\x1e\xd7N\xbeR\x851\xa9\xf6\xcd\x11\xa9\x88\x8a\x86\x80$\x10\b\xe7\x034g\x94\u0376\x04A\xb4\x14\u010e\x104\xab\x104\x04\xa2\x86DX#\xc8\xc0\xaa3\xca\x16\x88\\-\xb6\xc1\x98\x82\xe7UD\xe4\xb1'\v\xe2rhK\xc5N\xa6J\x97.\xa5@(\t\"\x14\x10S\n*\x10v\r\xae\vPS:\x87D\x86P\x04\n\x90\xc9\x14\x99`\x1b\xe7\xee\x1a\xb3\xf5?\xf3\ua9ca\u05d6\xc0\xc6@H\x89\xa9\xf9\u0750*\x84\x8e\x87\x10BB\x85\xb5\x8c\x8f'\x10\xa4\x90\xd4\xeb\rq\xf1\xe2\u0291o~\xf3k\xfb\xf1:;\xb6\x1d\x98\x1f:t\x85\x03\xb4\x8d#\x17/^\xbc15\xa0\xf2\x03\x9c\xef\xba\xeb\u0778\xf1\xc6\x1b\xb7-\xc5\x02X\xbf\x98\xd1h\x84^\xaf[Y\xed\xd4\xebu\xec\u06b5\xdb-F\xc5\b\xba~\u007f\x80~\u007f\xb0\xcdo\x1d\x03\xa5\x14v\xed\xdam\u524c\x92\x0e\x98A$0\xe8m\xa0\xbfy\x01\u0485Xg\xfc\xae\xa3A}\x8e=W`O\xe0\xae=\xe0Kk\\\xa9\b\xc1\x9cB\xb8+B4-\x11\x04\xae\x9a\x93\u00abP=m\xbb(\xf3\u9503\x00y\x89N\x15\xa0\xc7>\xe0\x15x\x96-p\xb24\tC%v\x19%\x83)\xbf\xfa\u0512\x10\xce(L\xcd\a\x90\x8a\xb2\xea\x94\x00P]\x82\x16C\xc8\xf9\x10\xb2%\xa1\x9a\x12a3\x05v\x01\xa1\xc4Xq\xfc\xc3\x05\x9e\xd2$W\u0742\xb4r\xac\xd7P\x9eG\x12\x84@\x00R\xa6\\\xb9\xb0k\x9e\x81m~\xb6\xa4\xbd6W\xd8hg>f\xdf?\x02K\xf7\xe5/Zd\x1b\xa6\xe9i\xd8\xde\x1a\xe7\n8m@$\u0418^B\x105a\x92\x11\x88\x04\x82\xb0\x9e5p]\u07ca\x12m\x10\xc7\u0261\xfb\xee\xfb\xf6^\x00\xf8\x8b\xbf\xf83\xba\f\xe6?\xe4q\xff\xfd\xdf}\xfb\u0673\xe7\x9a\xddn\u01fd9:\x03\u024f|\xe4\xefA\xa9\bI2\u07364\x84\xb5\x8b\xd5^s\x96\xb2b\xcd\x18\u00edV\v\xfb\xf7\xef\rF\xa3xl\ua0d91\x1c\x0e\v\x00\xbf=\xafQb\u03de\u0748\xc20\xf3\xd1@\x01\x9f%F\x83\x0ez\x9d\x15\xa7C/\x19\xb2O\xf2*\xa9\xa0_\v\x01\ri\xb8\x80\"\u0526\x14\x9a\xf3!\xa6f\x034\x97B\xd4\xe6\x03\x845\t)\x01#,\x18he\xbf\xd8\xe3\u05ab\x16\x86q=J\x95\xab\xa0w\x19c\xdb\u007f\x06ck\xba\x82_\x05l\x02\xb6*W-\x85\xd6R\b\x15\n\x97\x1f\xee\xe9\xaa\rCD\x02b.\x80\x98Q\x10\x91\x80T\x02A] \xaaK\u051b\x12Q]Zj\xa9\\\xa5W\xf6\x05\xb8\xe2\xef[xLr\xb1/Q\xb4\xdf\xe2\xca>\n\x13 \x05!\x10\x04D\x041\x13\x80\x94p\x13\xacNG\x1e\x120\xa3@\x91\vr\x16v\xc7\x01o\xc7e$`\x04\x175\xf5Y\x95^\xbc\fv1zF3\xc2h\n\u0359\x9d0\xc6\xc0h\x9di\xcd}\a\xc6 \bp\xf4\xa5\x97\xf0\u02993\xb7\x00\xc0G>\xf2K|\x19\xcc\u007f\xc8\xe3\xf1\xc7\x1f\u007f\xc3\xfa\xfa:\xe28v\xa1\u01f6\x8c\xbb\xf9\xe6\x9bp\xd3M7\xd9U[\xebm\xfd\xe2\xa6\x15\x02{\x83\x1f\xcc\xe0(\x8a\xd4\xc6\xc6F\xbb^o<U\xaf\xd7\xeby8q\x0e\x18v\xa4\x1f\xdbzp\b \xec\u06b5\v\xb5ZTIK\x10Y\xb3\xad~g\xd5\xf3\xb2\x9b\\\rV+\xe9r.8\x93A3AJB\u0610\b\xe7\x15\u009a\x80\x02C\xd5$\x82\xc5\x10\xe1B\x80\xa0!\x01E\xd0d+\xdcT\xbfl$\x81\x95\x03t\x81\x92/I\x85Sb\xe5_\u02a1\x11\xb4%{\x81\n\u00a6\x9c\x0f\xe1\xafq\x9a\x19\x14I4\x96B\xa8\xa6\xcc\xf3;\xcb\u02dea\x90\x02\u4302\x98\x0f \x1a\"+\x18d@\x88\xea\x02\xb5\x86\x84\x94\xe4\x16\x03L\x1e&\xe2\xf2\x8b<\u10b8\xb8\x1c\xb1G\x92\xe5\xeb\xf3x\x9b\x97\t\x10d\xabr\x92\xb0TQCT\x8e\xfdS$\x80)\x05\x04\x04\tF\x10\x10T\xe8\x02*\\C\xd4\b\xbb\xe0\xa5\xd6\r\xf9g%\xd7\xe1\v\xca-\x8cu\x92@\x85u\xcc\xed<\x04!\xadlV\b\u5a16|GDRbmm\x13Z\x9b_4\xc9p\x0f\x00<\xf4\u0403\x97\xc1\xfc\xb5\x1e\x8f=\xf6\xb0\af\xed;\x8c\xd1.?\xd3d\xc0\xfd\x9e\xf7\xdc\xe5\u0329\f^\x8f\a3\x9b0\x8c\xb0\xb6\xb6\xfer\xb3\xd9z\xa9\u0468\x85y\xc0s\xfe\xb8^\xaf\ufa0a\xed\f\xe6\xc0\xee\u077b\xd1l6\xbd\x81\x0e\xff\xb3N\xd0I\x8c\xee\xfay\x8c\x06]\x90\x90@\xc9$v\f.x\x1c$\x8b5!AH jH\x043\n\xa2)\v\xce|\xa4\br.@8\x1f j*\xa8\xd0\xf2\xb1ieh$A\a6\\\xd9\u0212\xf7\u01d8\x81k9\xad~\x92\x94\xe2U\xf4P\xbc\x85)\x83?.\xf1\xf1)\xa8\x93\xa5\x8e\x82Y\xe5R\x88\x8a\x96\x01\xec{\x95\xbb\x1c4j)\xd0|\x00jH\x90\xcc\x01;\xacI\x84\r\t\x11\x12\x8c\vv.\xbe\u063e\xddm1:\x88\n\xb4\x10U\xd2]D\xe5\f\xd4j\xbf\x17\"@\t\xabNA$A\xd3\xca\n\xe5\xb9\xc2!\x8c\x004$\xa8!!$ \x89!%\x01\x8a`\xbckO\x9b\xbb\u0185\xd1d\xbaq\x1e\xff\xddl\x12\xa8\xa0\x8e\x99\xf9\x03\x90*@<\xe8B\xaa\x00a\xd4p\xe7m\a\x89\x88\x04\xb4a\x9c^>}\xc3'?\xf5\xe9]\x00p\xdbmw\\\x06\xf3\xd7z\xbc\xe9M\xb7f\u007f>~\xfc\xe5\xb9^\xaf_\b;\x06l\xfed\xad\xd6D\x1c\x8f\xf0z>\x1a\x8d\xba\xba\xf2\xcaCJJU9\xe9ii\x16\xb3\xadi\x16\x00\u0635k\x17\xe6\xe6\xe6\x9cE)\x8d}\x98\xd9\x18\xb4WO\xa3\xbf\xb9\x02!TA\a\\\xa5N\xe6\x02\x90\x97\xab}\a\xe4u\x89\xa0\xa5 f\x02@Z\xc9c\x01\x9f\x14AN+\xd4\xe6\x034[\x12\xb5P \x90\xb6R\xcb\xf8h%\xa0C\x01\xdej\xf2\x06\x93H\xe2\x8a\xc7l\xe5\x9a[\xad\xad\x19_\xcel\xe61DC\"\x98\x0f,(\x1b*\x00\xb9\xf103[z\xd8y\xbd\xd6$h!\x00MI;x\xe3\x1e\xa4\x02BT\x97P\x01\xc0i\x8a=\xa1\xdc\xe4\x98@\x00q\xd5*\x9b\xe3n6\xfcU\xa5\x9e\xf7\x8c\x81\x89 \xa5}ohZ\x82\x1a\xa2\xba\xdf\xc0.\xf89\xe5\xcf#@\x10CR\xdab\xe5,z\x8e\x05\xdb0\x8f\xec\xb51\xee+U\xa8\x98\f\u05d95\x88\x14\x9a3\xbb\x11\xd5g\x90$#\x00\x04)\xc3,\xfc\x82\\\x9f\aDx\xf9\xc4I\xb4;\x9d\xd7U\x94\u0736\xa4Y\x8e\x1e}\xf6g\xd6\xd7\xd7\x0f\xb6\xdbm(\xa52\x8a\xe5\x96[n\xd9\xf6\x8d\u03f1\x8a\x8c\xaa\xff-\bB\x04A\x1dJI7\xe9\t\xf8\x01\x15\x9dN\a\xa3\xd1h\u06cf\xf4\xcf\xce\xce\xe0\xe0\xc1\x83PJ\xa0<\xff\u01ce\x1f\xd8X9\x89\xf6\xdaiH\x15\x8cU\xabT\xfe \x13\x15\xb6\xee\x19^\x01 \xc1\b#\x81\xb0! f\x94U?\x94'@]\u04cf\x14A\xcd\x06\b\xe7C\xd4\x1a\x12\x8dH\xa0\x1e\t\x04\x82\x9cc\x1f\xc3( \t\x85\x954z\xa4|\xbeo\x98\xe0\xa4H\x15Z\xeaI:\xed\x82+ O\xa0c\x9cZ\x8b\x192\"\xd4v\x84\bZ\xaa\x10f\\\x984\xa5\xaa6\xad\x03\xb9P\x00s\x01hV\x01\x91\xbd.A\x84@\x11\xa2\x86\xb4\xfc{\u5fa1\xca[\x86's\xe0\xe9>\xa3\xf0\x92P\xf5\xf3\t@)XuJS\xdas\x134Y\xaa\xee\x16(\x0e\x004\xad\xa2\x85Rq\xbfa\x10\xe7\x03D,a+\xfcBw\xc26=\xcb=\x11\xd61\xea\xad\x05\xcc,\x1e\x86\xd1\x1a\xc3A\a*\xaaC\xa9\xc8=.=s\x81\xd5\xd55\x1c\u007f\xf9\xe5w1st\x19\xcc\u007f\x88\xe3\x1b\xdf\xf8\xd6\xc2\xc6\xc6f=\xedF\xa7\x14\xcb\xdb\xde\xf6V\x1c<x`\xdb\xc5\xc2\xfd \x87\x94\xf6\xa5\xb7\x03CN\x99\xec\r\x0eu\xbb]\x8cF\xa3m\xedi\x0eX\x89\xe5UW]\x05\x95\x01u1\x1fS\b\x81\xf6\xda+\xd8\\[\x86\x10\xaa\xf2\x03L^eJ\x13\\>\x18\x80\x90\x02A$ \x9a\x12\xd4\x14\u0632\x1cf\xab\x83\x14-\t9\xa3 k\x02\xa1\"\xd4\x03B- \xfb\xf97\xae\x99\x16\x88\xdc\x0f\xa4\xb8ba\xe2\x84\xd1V&]\x99/9\x15(j\x1a{\\i\xb1\x92\x84p.@8\xa7\x1c54>y\xeaOb\xd2\xd8\xf9\xb0\xe5\u0195k\"\xce* \xccJW\xdbg\xa8K\u0210\nz\xf4I\x84?a\xf2\xda\u00d4S>\xbc\xc5N\x86\x85U\x1cII\xa0@\x80f\x03P]nyO\xa5.\x88\xd0\xda.H5\xe7\xa3b\t\x95\xac\x02O'?\r\x8c\x1d\xfb\xf7\x16\x1f\xbf\xa5\xcd\xc6\xc0\x18F\x12\x0f\x11\u05a60\xb3x\x10\x00c\xd0Y\x87\njPQ\x03\u0326d\x8fK\u8d3b?u\xfa\xe5\xa3!\x00|\xf3[_\xa7\xcb`\xfe\x03\x1cI\x92\u0726\x94\xac\xc7\xf1\xc8v\x9eMZ\x99\xbf\taX\xc7h\x14\xbf\x8e`\x9b*+\xf5\x14\u0327\xa6\xa6\x10\x04jLOnm\x00\xe2m[\x99\xdb\xddQ\x82(\x8ap\xf8\xf0!DQ*O\u0307P\xec5\t\f{\x1b\xd8\\}\x05Z\xfb\x1e4\x8cr\x13\x8d&#\xbdU5\x84\x04\x11\t\xa0\xa9\x00)&\xb3\x1f)mj\xec\x1d.\xa6\x14\xc4l\x00\x8a\x04\xa4\x04\xa2\x80P\x97\x84\xc0\r\xdah\t\x18\xe5\xa9\\\xbc\xc5e<\x1d\xf9\xd5|\xbb\x8a\xed\xa7\xc2w\xfc$\n\xbb\xeb\x00\xa2)\x85p)\xb4\xea\r\xe6\xca\n\x9a\xb6\xb8\xb7\xb2\xbf\xa7\u0297\x96\x02\xcd\x05v\a\xe3\xaaW)-\x8f.BQ\bw\xf0\x17\x84-]\x12\xdd\x0e\x8a\u0299\xae<~\x9eL\xee}\x13\xee\xf7OK\u0434\xac\xfa\x81\xf1\xb5\x80\x19Fk\v\xcbu\x01\xad\xa8\x90\x13j\xc1\xdd\x01\xbb\xb6`n\xd8@\x1b\xe3vC\xaer7\x9c\xed\xe4u\x12C\xca\x10\xd3s{\x10\x84\r\xc4\xf1\x00\xc6\r\x14\xf9\x94\x93\x10\x12q\xac\xf1\xfc\v/\x04\xdf}\xf8\xd1\xdb\x01\xe0]\uff0b/\x83\xf9\x0fp\xf4\xfb\x83}\xcc\x06I\x92p\n\xe4\x87\x0e\x1d\u0111#G&R\x17\u06cbZ\xb1\xa1\x12J)\x84aP\xf9\xfd\xd42vqq\x11a\x18\x8e\x81y\x1c'\u06deJ\xd2ZC\xca\x10\x87\x0e\x1dB\xb3\xd9\x04\x8d\u036b\xb8\x0f\x92N\xd0^[\u01b0\xbf\t\x92j\xecs<1<\xce\xeb5\x92\x84mf6%P\x17\x97\xd4L\x97{|4%!\xe7\x02\x88\x9a\xb4\xcd8E\xa8)\xb2L\x04;\xb3\xa7\xb0\u031f\xfb\x13G\xfe\t\xb9\xa6$U\xc78\xd3\x18\xdf_0\xcb\xf5*\\\xf7h\u05e8\x8c\x96\xacf<\xdd\x11\x14\x1d\x82+\xf25'\x82:\xf2\u0160)m5\\s\xce\\l\xd7\xc10\x12\xa0\x80,%?&\xe8\xe1\xb1\x19\xa6\xe2\n\x96n*\x8a*\xa0\u00a2E\x16]\xa4 (bP]B\xcc\a\xa0\x80r[\xe2\xca\xe7\xf7\x831\x9cQ\x96\x02\x92\x1a\x90\x10g\xcdN\x18\xb7+H)\x97\xb4]K9\xc5\xc7\xc6d\xbd\x01\xb2Sy`N\u041a\u074d\xe9\x85}`\x93`4\xe8@\x05a\x1e\xf8\xec\xceA\x1b\xc6\xda\xda:^|\xf1\xa5\xb7\\\xa6Y~\x88cuue4\x18\f\x01\x10\xa5\xc3BW^y%\xf6\xed\u06c7\xed\xdc\x14,V\xad\x06Q\x14\xa1\xd9lU\x9e/;\xbd\xe2\xd2\xd2R\x10E5\x18w\xa1\xe9c\xe38\xb6\x9e\x12\xdb\xf8Z}E\xcb\xc2\xc2B%\u0352n\xc6\u06eb\xaf`\xd0\u07c0P\n\x13`\xa2\x1a\xc5\x1df\xaa@@\u0525\xad6\xabT\x10\x18\xe7\x97\xcb\xcc\b\xb5,\xa0\u021a\x84 \x82\x14@\xa4\x04\xea\u03b0J+\xabt\xa9<1*k\xce\xc7\u03c1\xb6\xa0\x0e\u0199\xe8\x94\xcf\x06\u0080\x10\xcd)\x88Y\xe5=\xfc\u0485 OlV\xe6\u007f%fPC\x80\xe6\x02\x88\x86\x84P\x04\x99r\xe85\tR\x02\xc6\a\xeeB\x9a\x0f\x8d\xc5o\x94\u007f\x0f\x8f-d~\x8f\xc35=\x03\xf7\xfb\xeb\x12\xd0[Y9\x96^'\xf6<\xcc#\x01Q\x17\x10\xa2\xd8*\x17\xe9Tq:8$\x04\x84\xb7\x98\x12R\x99\xa2\x80 \x01\x9d\xc4h\xcc\xee\xc4\xcc\xd2!\x18\x9d`\xd8\u06c4TA\xd6\xcfI3D53\x12mp\xee\xdc\xf9w\xa5g\xf4\xf0\xc3\x0f^\x06\xf3\xd7z\f\x87\xa3l\xe23\xb7[\u074d\xa5\xa5\xc5m\x9e\xc0S\x04\xba0\f1==]:\xdf\u051a@\x13\x00Q\xafG\x17l\xd1P$mm\xdaP\xf2:\xb8V\xc6\xcc\xcc4\xf6\xef\xdf?^\x99{ \xb1\xb1z\n\xfd\u038a\xfd\xd0\xf0\xa5\xdcW\xbc\xa7 @*\x01U\x13\x10-\t\x8e\xc4%p\x8e'>+\x11\xac\xdcm.\x80\xacK\xeb\x01B@(\b\ra\xa9f\x1d\n\v\xe8\x84\n\xbd\xb8\x1f\r?I\x01R\x95Z49\xb8\"\b\b\xe1t\x00\xb1\x18\x82\x14Ys@\xc7Qp%\xe8\x95'-\xcb\xf9\xa5\x9e\u076f\xff\xad\xba\x80\x98U\x10N\xbah\x9b\xa2@X\xb3>(LEs\xafI\xec\xc7\xd8N\x83\x8a\x8b({\u0222\x84m\xbe\u04b4\x82\x9cQ\x15\xd4\u04c4EW\x00`\xe3\xeck\xed\xffCEh\xce(4Z\x12\xf5\x10\b$l#\xd4x\x80\x9f\xf2\xf8i\xc2\x14\x18\x865\x8cvT-3\x8cI\xa0\xa2\x06\xa6\x16\x0e@\x86u\x8cF}h\x9dX\xd9,\t/\xb1Np\x1c'X^^~#3\xd7\x00\xe0\xd6[\xef\xb8\f\xe6\xaf\xf5\x90Rf>%)\x98OOO\xa3^o\xbc.T,)\xcdB\xa4077\x8b \b\xe0W\u0756\xa20\xd8\xd8X\t\x9e\u007f\xfe\xe8\x97766\u06f5Z\xbd\x10H;\x18\f\u0728\xff\xf6\x06s\xadc\xcc\xce\xce\xe2\u0211\xab\xac?\x06\xa1\u020b;:\xa2\xbd\xf6\n:\x9d\xf3v\xab[\xa60\xaa\x02\x9d\xddC\x94\x04TD\x90-\t\x9aRn\xc2o\x82\xcf\t\u00c5\xf5\xa2Z\x80\x91\xfe[\u04daU\u0256\xb2\n\vE\b\x94@]Y\xd3*\x1d\t\x98\xc0sW\xf49\x9b\t\xa1<\x05m\xfa%\x17\x18\x02\t L\x1b\x92\x8b\x01\xe0\x86\x83\xf2\x11\xfc\xaa&g\xfe/\x95\xfd\x05\x1e\xcf\x02%\xa2|\u0769\t\xd0|\x00L\u0641*!\xad7z\xbdf-g\x89\xc6s83\x9f\u022cb/\xee\b\xd2\xde\x04yzI\u06ec&H)\xecnh)\x80\b\xa8b\xc7Q\xd4\xed3\xf2\xb8?\xd6\x1aF'\x96\x0f7\x06\x82\x18A\x04\xa8\x96@P\x13\x88\x027\x80\x94\x017\xf2&\xa9\xf1\x16\x027=\x9e\xfa\x9a\x83\x01\x9d\u0118\xddq\x18\xd3\xf3{\x91\x8c\xfa\x18\r{\x102\x80H\xc7G\x89\xa0\x94\xa2N\xb7\x87\x13'OM\u007f\xfb\xdb\u07f8\xe72\xcd\xf2\x03\x1e\x8dF\x1dR\xca\xd2M)\xb6=\xb0\x95h\x14\x00\xc0\xcc\u0334m\x0e\x96\x8e$\xd1t\xe1\xc2*\x1a\x8d\xc6@\b\x91\x94+\xf0tXj\xbb_s\x92h4\x1a\r\xec\u06f7\xcf)Z\xaag8G\xc3\x1e\xba\xed\x8b\xd0&\xa9l\x82\x8e\xcdQ\x12 \x89\xa0\xa4\xb0\xb4\u0234\x0f\b\xd5\xceQ\xa9\xcd\x15\xc0\x93\xeb\xf3\xf4W\xd6\x04h^\xd9\xe6\xa8\x04 \xac?xMX\xa0M\x02\x01\x13\x8a|\xb0(\xe5^\xb7|?\xe8U\xbc[\xf6\xf9\x02A\b#\x011\xa7\x80i\x8fz\xe2\\\xbf]\xad+a\xcfY\xb2\xaa\x84\x9eddnA\x17\x01\x81f\x953\xb4\xb2^)2\x14\x88\"\xfb%E\x89P\xa9Xo\xa9\xfc_\u02af\x9d\t\x80$(a\x9b\xd5b1\x80\xac\x8b\t@>.+\x05\xb9f\xa5N\xc0\u0680\x8dv\x92D\a\xc6\x01@!A(\x810\x90P\x822\xee<S\xbf\x19\xed*\xf1\xb4?\x95\x1a\u0739\xe7\xd7#L\xcf\xed\xc5\xcc\xc2\x01\xb01\xd0\xf10\xf54\xcf_b\x12\xd0\u06a0\xd3\u9d3e\xfd\xed\xef\u0718\xf7\xb2\x06\x97\xc1\xfc\xb5\x1csssQ\x14E`\xce;%\xb6J}\xfdM}\xb6Z-\xd4\xeb\xf5\xe2N\xf8\xffc\xef\u0363,\xbb\xca\xfb\xd0\u07f7\xf7>w\xa8[Swuu\xf5(uK-\xa9\u0552\x00\v4\xa0\x81Qf\xe6a<\x80\x00\x93x9\xb1\x8d\x89\x87\x84\x10\x9c\u007f\xf2\xfe\xc9K\x9c\xbc8\xce\xf3\xb2\r\xbc\xb0\x1c\x9c\tX\xd8\x01\x83\x9e\x102\xc6\x0fd\x03\x92h\t\xa3yhuK=O\xd5\xd55\xdc\u1733\xf7\xfe\xde\x1f{8\xfb\u073a%!/\xbf\u040a\xea\xacURu\u056d{\xcf\x1d\u03b7\xbf\xfd\xfb~\x83\xff\xc8\x14E\x81}\xfb\xae\xcc\xda\xed\xb6\x1c\xee\u078a\xa2\x80\xd6\xe6\x82/\xe6\xd6\x1a(\xd5\u0116-[\xd0h40\xdam\x10`]\xa2\u05dd\x87\xe6\xd2\x05\x03\xd4:\xb3a\xba\x9f\u00f33\tg\xf3:\xa9\xa2T\xbd\xa6\xeec\x1a\x01\x05$\xf0\x04\x0fA\xde4T\xd8\x1a\x02\xb4A\x81&\x14D\xe6.\xfaL\x12\x1a\xbe\xa0[E\xb0\xa9RT8\xb5\xe1\xaa::T\xf0h\xcd e\x1f\x94 \b\rE\x10\x93\n4S\xc1+\xa9\x00sT\x02Sb\t\x81\xe7\x97 \r\x95\xfa\xa1\xe7N\xbe\xa0cR9\x11\x8fpP\x8bj8\ua9d4U\x90G\x88s\x1b\xbe\xb3\xe1\xfdA\xb4i!o\xa2\x95\x11\xc4F\x055\x95\xd5\xf8\xe8\xa3\xf1\x9b\xfaN.v\xd26\xd0\x0f]\x97\rk\x01\xc1\xe0&9\xda)\xac\x0f\xe7\xf0\xcc\x16k\xc1\xda8.zl\xaal\xfc^\x04\xd8\xc72\x94ja\xc3\xe6K\xd1lO\xc0\xe8\x1c\xd6j@\u0238xy\xb1\"\x17E\x81\xe3\u01cf\xbf=\x9cn\x96\xb5\u058b\xf9\x8b:)!\x9e\xb1\xd6B)E\xa1\x8b[ZZB\xbf\xdf_\u0571_\xe8G\xbb=\x86V\xab=\xf2w\x83A\x1f7\xdexC699!\x86#\xf1*\x98\xe5\u009f\r\x00\xc0\xec\xec,:\x9d\xb1\xd5\xe5\xc5\xeb\u05ad5\xc8{K0\\\x823\x91fP\xa0\x96\xa4\xe9\x8bA&\x9d\x9d+\x8fI\xe7\xd3!B\x9e\xa3\xaf2\xc4\xd1pkTM\x8dl\x88U\xbfO~`\x01(\xe1:\xd5\t7\f$\u12ad\xf4\xf7a\x15\xc14\xbdR\x14\x14\xdd\xfcj@|(n\xabL\xc4\xea\x10\x05\x93\x83\x1f2E\x0e\xbb\x9ei8\x0e\xb5\xad\xa0\x82\xf8\xf4j\u0557Gw\xe8#\x81\x9e\xbai\x18Q\x8a\x87$\v\x99\"\xc7r\x99T\x80r;\x13\xca\x04dS\"k\b(\x95\xb4\xca\u00b3n\u05b0\b\x8e*K_\x04\x95$\u0229\fjc\x96\u030d+\xc6Im\xf1\x1e\xe6wz~9\x9b\n/\xaf \x12\xcfb\x91N\xf0\xc5`\bb\bI1g6\xae[\"\x9d\u06cc\xb04\xb6%f\xb6^\x8e\xf1\xa99\xe82\x875\xdaw\xe6\x14\xbby\xa5\x14\x9d=s\x06\x8f<\xf2\xe8\xe6s\xe7\u03bcj\x1df\xf9[\x1cJ\xa9\xfd\xd6r\xb7\u0468\xe0\x89\x03\a\x0e\xe0\u0529S\x90\xb2\xf1\x92Q\u007f\xbab\xdeF\xab\xd5J0\xf3\xc0\xf7\x15$\x84\x90;w\xeez\xaa\xd9l\x9ck6\x9b\xb5\u03bd\xdf\x1f\xa0,_:|\xfa\xa9\xa9)l\u0738\xd1C\xe4\xc3v\x87\x04f\x8b\xa2\xbf\fkJgO+\x87\x82\x13\"F\ue2dd$\xa0)\xc0S\xd2\tG,\xeav\xe2UtP\x1a(\x14\xbb;Ak\x80\x1ea\u06ddt\x82\x94\t\xd0T\x06\x9aR\x10\x99\x80\xf4\vJ\xac;\x020\x99\xa8V\t\x1a\u00a91\xdc\xfe\xf3PG\x8dJ\xb4\xd3$\u020e\xb3\xafE\xa0!bDm\xe3\x90\xda1\\\xbbG\x81/\xc3\x05\x96F\x92\x06k\x1d\xb4u\"%\x9a\x92\xa0\xa9\f\u021c\xf1\x18Ig\xd2\xd5P\x02*]O\x12\x16\xce(\x83\xe2`Q+$ \xc7\xdd\x1c\x802\xb1\xea\xbcG\xa6\x15%\xef_eN\x97\x0e@\x1d\xccb=\x06\x0e\x82\xa3Z*\xb7\x83\x12\xd2\xef.\x84\xa3\xfc\x92W\x11\xdb@]\xf4CR\xf6n\x89\xe1\xff\x9d\xa99L\xcf\xee\x82\x10\x12V;C?!\xbd\x93\xa2\xef6\xba\xbd>\x16\x16\xce\xed\xfc\xecg?{C\n\x9f\xae\x17\xf3\x1f\xf1\xb8\xed\xb67\xcf\xcf\xcdm6Y\u0588\xea\xee\xfb\xef\xff>\xee\xb8\xe3N7\xc7i\xb5\xf0RI\xceN\x8byz\xd5\n!!\xa5\x90Dt\xa8\xd5j\x9d\xe8t\xc6\xfds\xaa\x02*\xf2\xfc\xc2W\xba\x06\xf5\xea\u018d\x1b\xb0m\u06f6h\xff;b\x88\x80\xa2\xbf\f\xa3K\x10I\xb7}W\xae\x80\x84\xad\xbc\x94\x84,#\bE\x80\x02xR\x82\xc7%\xb4e\x94\xb6N\x88s]\xa7+x\x14\xc1Z\xae\x93\xe6\x88kt\xb9\x14\xaap\x85\xb2\x12\u0650\xf2l\x8f\r\x95Z\xb4\xa5\xc8\xd58v\xe7g2\xaa\x02.j\v\x11\xd7\xf0c\x1e^\xc8\xe0\ng\xd6\x12\x90\xbe#\xa7\r^\xa8bQs.X\x9bQB\xabb\xa9y\xd5\"B\u03c3\xdb\xd7=^\x9c\xe4\x9da\x05\x81'=\u44b9\xe2H\x19\x81\u048cQN\x92\x93\u05a2\xb9{\x18J\x8dI\xa8M\x19\u460c\x8b\x00%\xbb\xa2\x10\xc2<\u0681\u04a7\x18\xd9\xca_\x98\r\x83\xad\x89E\x18\x81?.\x1d]\x91}\xe83\xc8\u07f7\xf7\xe7t\x8fc\x9cR\x14U~\xb0\xf1\"Dk4\x88$6_\xf4\n4\u06d3\xd0e\xee\xc2S\x84\x02\x91\xf0<u\x03kM\xd9\xedv\xc5C\x0f=r]\xf8\xbc_\xa8\xb5\xe7\x82,\xe6\x97\\r\u065f\xef\u0739\xf3\xc0\xcc\xcc\f\x88D\x84Z\xfe\u077f\xfb\x1d\xdcq\xc7W D\x86V\xabs\xc1\xa6\u05e7E\xae\xd3\x19[\x05?\x00\x801\x1a\xa7O\x9f\x06\x00\\z\xe9\x9ef\xa7\u04c11&\x8a\U000965d7\xd1\xef\xf7_\x12\xc5\xdcZ\x8d\xa9\xa9il\u077a\xb5\x96\xbf8l@\x95w\x17at\x0e\x9fs\xe6c\u037c\xf22#\u0226p\u0635$P\u01e96\xa1\x04r\xcd\u022d3\x99\xa2Z\xe5\xa3U\x1d0\x8d\xe8\xc4G\x03\xb5\xc1X\xa9bP\x10\x11\xc4d\x06\xb91CcL\xa2\x99\t\xb4\x94@[\t\xb4\xa4[dB\x92\x11\x10\n\x1c\xd5pn\x1e\x02\xa9C!WmW\xc8\u0174\xf7M\x11p\x1dc\x12T\xbd\xe6\xc1\xb4\x1a\x03\x1f\x82\u04c7\xbbt^\x13\x9f\xf63(\xcb\xd0\x1e\x86f\x01\u0414r\xb0KS:\xb8B\xf9\xf7A\f\xfd\xe1H \xdeY\xd2f\r\x81\xc6\xc6\x06\u0114JnZ\xa1\xeb6\xc9\xf2\xe0\x98\x182t\u007f\xb1\x88\x06\x88\xc5\xd4 \x96\xf8\x05\x06+g\xb6\xe5^\"\u007fG\x89L\x94@\x10\x95\xa5fT\xa3\x8a0\re\x8b\r\xdb\xf6`|\xe3\xb6\xf8\xb8\xe13\xc3^\xb0\xa4\x94\xa4\xb3g\u6c7c\xbcr\x033_\x06\x00\xdf\xfd\xee_\xd3z1\u007f\x81\xe3\x87?|0\x14\b\xbe\xee\xba\xd7|a\xeb\xd6-\x18\x1f\x1f\x8f*\xca#G\x8e\xe0\x1f\xfc\x83_\u00a7>\xf5\x87\xe8v\x97\x90e-\xb4Z\x9d\x18\x04q\xe1\x15v\x8b\xf1\xf1qt:\x9dX\xf8\xc2d}0\x18\xf0\xd3O?\r\x00\x98\x99\xd9x<\xcb\x14\x88D\x04\x00\xfa\xfd\x01B\xca\u0485^\u03356\x98\x9e\x9e\u008e\x1d\u06e1\x94\xac\x158J'\x90\xe1\xfaU\xbe\xb3\r\xdc\xe0\xe0\u06e1\b$\x05\xa8! &%DGB\x06\xebT$\xd71%\xfe\xd9k\x18N\xd5\xca\xf6\xdaNX\xfe\xd4\xea\u014a\xc6%\xe4\xc6\f\xaa#\x915\xc8w\xe9\xc2y\xba4\x84S\xfa\f\x19N1\rei&6\x04\xaa)\\1\x1f\x97\xa0)\xd7\x01\xaf*\xb2k\u060c\xa7\v\x16\x0fc\x12\xabj4\x0f\xc5\xd6\xd5\x1f\"\xb8\x97\x18\xbf\x1b\x10\u00a9AE\xa8\x04\x13\x12\u0618\x01c\x12\xdc p\x96\xfa\xbe\xfb\x00\xee\xa4\v\x0f\xef\xa7\x11~\xe71\xad 7fn\xae\xc0\x95\xe2U\xf8B\x1es\xb8\x13\u03da:=\xd1\xf90Y\xad\u0757q\x9duE3\xb4n8\x1a\xa0\x13I\x10-\x01\xf2\xce\u0281\xcdR\x83\xd4D\xb2c\x1b2\xf6\xb4VC\x8dM`\xf6\x92WB5Z\u043a\x88!8l\x8dc^\x81D\xb7\xd7\u0179s\xf3;\xef\xb8\xe3\xcf\xf6:\x94\xe0\xfe\xf5b\xfeB\xc7+_ym\xfc\xfe\xf6\xdb?\xf8\xf9W\xbc\xe2\x15\agg7\xa1\xd1hF\xae\xf6\x993g\xf0\u044f\xfe\x1a~\xf1\x17\xff!\xbe\xf8\xc5/\xe0\x89'\x1eE\x96)\x8c\x8dMDY\xfc\x85R\u052d\xb5\xe8t:\x18\x1bk'\u015c\u0207m\xa8^\xaf\xdf\x04\x80\xa3G\x8f\xdd5\x18\xe45\nc\xc5f\xb9\xf0\x8b\xb91\x1aY\xd6\u009e={0>1Q\xcd4\x88 \xa4\x84\x94\n\x82\x04Z\x9di\xa8\xac\xe9\xe9f\t\xea\xc1\\\xcd\xc0\x04\x1cF>\xae\\\\\x183\x94/:af\x99:\xf5\xb9\xf7\x9bF\x16?pR\xf2\u05fc\xfc\x86\x13t<&;\xe6\n\xba\xecH\x90r\x85/\x13\u0085\x10\xd7d\xff<\x94\xe7@\xb5\u007f\u02cc\x905\x04dS8\x8c\xbc)j\xf8\u007f\x8c\x81[\u02dbf\xad8\xb6\x11~\xf0\xc3I?\xe9e`\xbdK.\xfb\xc7S\x92\xa0(q,\x0f\x1ds[\xc0N+7x\u039c\x1f\x8ejJ\xc8\xcc\r\x87S9\xbf\x15\xf0A\xda\x02j\\A\xcdd\xa0\x86g\xe6\xa4^\x91\x01M\xe2\xe1\xd79\xa5\x1b\xb9\u0398u\tk48\xc0*)~\x9e|\x05?\x16d\x00)\x00\x1eR1\x9ek\x1e`5\x1b=n\xea0\v\xb3Ei4\f\x03\x1bw\xecE\xa3=\u9b54)\x16sv\x9fcQ\x14\xa5VJM\x1d;v\xe2\x06\x00\xe8v\xbb\xeb\xc5\xfcG9\xee\xbc\xf3\xab\xa1H\x1c\xbd\xe9\xa6\x1b?q\xc5\x15W\x94\xe3\xe3\x13h\xb7\u06f5b\xf7\xc5/\xfe\t\xde\xff\xfe\x0f\xe0\xfd\xef\xff ~\xe37\xfe\t\xbe\xf4\xa5?\xc5\xf2\xf22Z\xad\x0eZ\xad\x8e\x17\x1e\x89\x1f\x9b\x822\xf8\xb0w:\xe3\x18\x1b\x1bK\xb0rAy\x9ec\xf3\xe6\xd9\xcbv\xee\xdcq\r\x00l\xd92WfY\x16\xa5\u0100\xa3b^\x88\x03P\"7h\x12B@J\t\xa5\x14\xb2\xccm\xabw\xed\u0685,s\x03j\xe1y\xbbB*\x88,\x83l4\xd1\xecL\x82\xbc\x02Tp\xe2Sb\x00.-\xacq\x96\xa6<&\xc1\xde\xc3#4\x86\x888(\xa2\xfc\x9c_\x88\xe4\xf1\x02\x1d9^\xa0\xbeS\xdb\xcb\xd0;N\\$\xa4\xc3\xf5\x95$\x84H\u04c0\xa1\xa7\x8f\x19\xfe-\x95\xa3\xfb\x89\x967\xbdZ\xcd@\xad\t\x84^\xe4;1\xa2\x93\xe7\xbammH\u0671\xce^W\x80\xbc\x8d\xc1h\xbe|@3\xa8%\x9c\xeb\u2e02jJdaA\x92\xc1m\xd1\xedDB\u0407h\t\xa8i\x05j\xc9\x1a\x1e\x9e\x12i\bnA\xaco\xd9\ua2f0\xd1\x06\xa6(\\Wn\x8d/\xa8\xa6\xf2_I\xbe\b\x0e#bb@q\xa2/p\x98y\xa4*\xc2\xc2x\x13.\xf6!7\x96\x03\xben\xc1l06\xb1\t\xd3s\xbb@B\x80\u0640\xad_\x04\xb8Zu\u03df?\x8f'\x9ex\x82\x00\xe0\xe0\xc1C\xeb\x98\xf9\x8fr\xec\u06f7\x0f\x9f\xfd\xec\u007f\"\x00\xf8\u065f}\xff\xff\xb8\xed\xb67\xff\xdb\u05fe\xf6\x06\x8c\x8d\x8d\xa1\xd9lal\xac\r\xa5T\xbc\xfdC\x0f=\x84?\xf8\x83?\u0107?\xfc\x9c\xd2\xd3\xda\x00\x00 \x00IDAT\xf7\xf0\xa1\x0f}\x18\x9f\xfc\xe4\x1f\xe0\u0211g\xa1T\x13Y\xd6B\xa3\xd1F\xb3\xd9F\xb3\xe9\xba\xfb\xe0\x1f\xfe?\xa7\xc83\xa4\x14\xd1\xf90\xc8\xf6\x8d1\xba\xd9l\xa2,\xcd\x0e\x00\xb8\xf6\xdak\xdb\xe3\xe3\x1d\xdf\xd1V\x17\xf6\u0673g]Q\xf81\xd11]\xe1\x16PJ\xa1\xd9l\xa2\xd9l\xa3\xd1h#\xcb\xda\u0232\x16\x94jB\xca\f\xd62~\xf8\xd0\x0f\xf0G\xff\xe9\xb38\xe3\xe7\x00 \x82\x94\n$\\zK\xa3\xd3A{n\x1b\xa8\xd5\xf2\x83\xac\n\x12\x11p\x81\u077a\xb0\xc8\x19\x184\x05\n\xf6A\f\xd1\x1a\xb7^\x1c\xd2\xee\x94\xd2\xeb<\xc1Vb\xafK/\xae\xa6\a\x9a!Y\xb8l\u034d\nb\xdc\xcb\xe0}Z\x8eLW\x19\u0090\x01\x95\xc7\xc9[\x12\xa2)@-\t\x1a\x97\x80\xac\x9a\xd1:\uf15egQ\xa1\x17t\xb2\x19B\xb5\x93\xe2\xcc0\xbe\x0eJr\xc3Lz\x9e\u05c2*<\xc6\x19\x92M+g5\x9c9\x93.\x12\x95\n4\u0707\x90@c\\B\x8cK\x17\xe3\x96t\xe5<4\xba\x88 \"\x8fp\x8ed\x86-K\x18]\xbaa\xa7\xa9\xe0\x15\xd808\xf5 \u007f\xf2of\xeb\x87\xe7\x0e\xce\t[>\x1b\x86\xa1\\\xd9\xf4R\x98eQ\xb2\xb33\x1a\xaa\xd9\xc6\xc6\xdd\xfb\x00!\xa0K\rcu\x14+1;\x87\u04d5\x95\x15<\xf7\xdca\xed\n\xfb\xe2\x05Y\xcc\u0545vB\xbbw\xef\x01\x00~\xcdk\xae\xa5\xfd\xfb\x1f\xe4\u007f\xf4\x8f~\xfd\xb7\xdb\xed\xf65\x00\xdes\xef\xbd\xf7\xa1\xdb]\x81\x94\x92\xad\xb5d\x8c\x89)\u073d^\x1fw\xde\xf95\xdcu\xd7\xd7\xf1\xd9\xcf\xfe1\xae\xbd\xf6Z\xdc|\xf3M\xb8\xfe\xfa\xeb\xb0s\xe7E\xe8t\xc6 D\xe6\x8bc\xfa\x81\u05c9\xda\xf2\xef\xbc\x1c&\xd3\xfb\x8a\xb2'\x84\x10EQ\xa0\xd7\xebZ\x00\u0631c\xfb\x9f\x13\xd1B\x96e\x1bz\xbd\xea\x12x\xf2\u0267P\x96\x03dY\xf6\xa2\xf3N]\xe7,\x9c\x87\xf8\xdf\u025am\xb0\xb4t\x1e\u01cf\x9f\xc0\x91#Gp\xfc\xf8\t\x9c<y\x12\x87\x0f\x1f\u0191#G\xf0\u88cf\xe1\xe0\xc1\x83\xf1y\x86\xe1 3C\x00h\x8dOaj\xcf\x1e`C\a\xf6\xe42\x94\x97\u07a7\u0233e@\x17\x8c\xa2\xeb\xba')\bMIh\n\x8a\xc5 \xad\xa1i?Z\xeb.\x87\xa0\x98\x17W\xc69v9!\x10\x813\xc7\xc9f\x00\xb4\xc8qg\xc2\xe4Ph\x81!;X\xe9\xb0uj\xf8Y\xc0\xb8\xc7\xc9m\x15FM\xab\x1e\x92\xd6\x00\u007f\u0590\xbf\xbf\xc0\xf9[\xb8\xbaG\xfe\xf3N\xbc\x06\xda\x1e\u051b\xecY q\x81d\x88&\x81\xa7\x14\xd8j\xf0\xb2\xae\xf8\xf0\x1c:AFF\x02\u0658\x9bs\x00\x80\xb1\xec\xe8\x82\xc3\n\u0580\x97\x87\x957\xe5\xa32`u\tS\x16\x11+\x8f\xa2!O)D`\xb2\xf8\x02\x1d\xbd\u030d\x05$\x83U5r\xae\xa2\xf5\x1c\xb6DI\xf3Vy\x88\x91_|4\xa81\x86\u03b6]h\x8cM\xa2\xb7p\x06\u031e\xb1B\x95\xe2\xb8(J,..\xb2\xbb\xb6.L\xc3$\x85\v\xf4\u063f\xffA~\xfd\xebo\x15D\xd4\x03\xf0S\xbf\xff\xfb\xbf\xf7\xb1F\xa3\xf1\xaf\x1f~\xf8\x91\xe6\xe9\u04e7\xa9\xd7\xeb!\xcb2ffbfdY\x06k-\xf2<\xc7\xfe\xfd\x0f`\xff\xfe\a\xf0\xb9\xcf}\x1e\xbbw_\x8c]\xbbvc\xf7\xee\xdd\u0633g\x0fv\xec\u060e\xad[\xb7`nn\vfg7a||<\xaa\xba\xcar\xf0w^\xd4\xd7\xc0\xf0\xd9\xc3\x14\x12\x00\xf6\xec\xb9\xe2\xc9-[\xb6\u0627\x9f>\x00cl\xfc\xa4\xff\xf0\x87?\xc4\xe2\xe2\"6m\x9a\x83R\xcaE\\\xbd\xd8\x12l\n\x94e\x89\xb2,\xdd\x16\xd3\x1b\xf5\xaf.\xfeT\xf3\x86)\xcb\x12\xcb\xcb+8y\xf2$\x9e~\xfai<\xfc\xf0#8x\xf0\x10N\x9e<\x89\xf9\xf9\xb38wn\x01\x8b\x8b\x8b\xab\xa0 !\x84\x8bw\x13TQ-\x85\xc0\xc4\xec\x16l\u06b5\v\xa2\u04c0!\x86\xb4\x94^\xe3\xae\fy\x86\a\x9d\xd70Z`0&\xd1\x17\x04E@K\xba`\t\xe1;+Z+K\"tb\xb6\x1a8\xf3*\xb1\xca\xf3\x15B\x1ajS]e\xe4\xcc\a>\x18G\x97C\x11\f\xa1\xbc[\x9f\u007f|+\xe0\x84Ha\xa0;&\u0717/\xb0\xc4\u0568r5.\x9e&\x85R\xa2~J\xb924\xf2\x8c\xe3z\xc0\xe4!\x06\x1f\xecL\xf0\xd1s<\xa2\x9b\xf7\xc3\xc1\x04\xbe\xaa\xa0\x1f\xbf\xc06%\u0318\x85Y\x04\xac\xa9\xcf\x17\x049\aFa\xbc{\xa1?5c\x1dN.\xd3A\xb1\x1fZ\x83\xc3{R\xedI\xac\xd10y\x0eS\x140\xba\x04[\xedZ\xfc\xe4\u06b1\xfe\x04+\xd3,[\xb9*\x02n\xb1\x94\x00\xca\xc4x+\xce\u0753E\x88\x03'\xde\xc2\xc2\xe1\xfd,,\x1a\x13\x1b1\xb1e\x17z\vagY\xf1\u0549\x88\x8a\"\u01d6-\x9bw\x00\xc0\x9f\xfe\xe9\xff\xd0G\x8e<K;w\xee\xe2\xf5b\xfe#\x1e\xdf\xfa\xd6=\xd6\xf3\x12\xf9\xd7\u007f\xfd7\u007f\xf7\x81\a\xee\xfb\x9b/\u007f\xf9+\xff\xfa\xde{\xef\xbb\xee\u0631c\xe2\xf8\xf1\x13T\x14y\xd1l63f&c\f\x84 \x94\xa5\xeb\xb6WVV\xf0\xf0\u00cf\xe2\xe1\x87\x1f\x05\x00t:\x1dLOOabb\x12\x1b6L\xe3\xe2\x8b/\xc6\xf5\xd7_\x87\x9bo\xbe\x19W^\xb9\x17\x13\x13\xd3\x00\x18y\u078f\x9dp(p/\x16\x96\xf1\nV\f\x06\x03\xf4z\xdd\xda}\x01\x84F\xa3\x11Y.\x00\xe4\xf6\xed\xdbOH)f8\x99\xe6\xddw\xdf\xfdx\xee\xb9\xe7\xb0i\xd3\x1c\xa4l`ii\x01\xddn\x17\x83A\x0e\xad\u02f8\x05t\xb8u\x16\xe1\xa3\xc1 \u01c9\x13'p\xe0\xc0\x01\x1c8\xf0\f\x8e\x1e=\x8a\x85\x85\x85\xa8*]\xdd\xe5{S$\xff\x1c\x8d1\x18\f\x06X^^\xc1\xd2\xd2\"\xce/.aqq\x11\xf6yv\aB\b\b\x95\x81H\xc0Z\xe3!\x1a\u9f22\x1bMl\xbb\xea5\x98\xd9~\t\xca~\x0e\x16\x04\xab\x00a\xaa\xe2\x14\xa5\xf8\f\xa8\u0082\x96\x19\xc2\x00yG`@\x84B3\xfa\x9a\xd0\xc9\x18-%\u0410\x14\x87\xa2qx\xe7\xb1\xe1\xc0N\vN\xb9\x15\xe6\x1e\xba\xb3Q\x85\x8dc\xbdI\xfd\n\xe3\u06d1\xaaE\x1d\r\xde\x15\xeb\u042d\x1a\xc0\x04\xb5a(\xd8M\x01LHG\xbf\xe4Qv\xb5\xb4z\x96IC\xbf\x1b\x81\x8e\xd3Z#\xd2d\x9e\x10\xa4\ubafb\xfb\xa1\xbf\x8c\xb5\xaf\xb2\x8cu\xfe(\x8c\x82\x19yia\xfa\x16\x82\x192&bq\xb5\xc6\b\x00}\x03,k`:s\x96\a\x89\x86I\x10\x0f\xa9Y\xa9\xfe8\xc6@\xe79L\x91\u00d6E\x84W\x90\xc8\xf9\u00d0\u0630\xf5E\xbe\x1ab\x82\xfd\xfb.\tF\x02\x9a-`\xac\xb7\xbd%\b\xff\xees|lO;\x14>\aV\t\b\xa3\x91\xb5'1\xb5\xfd2\x9c~r\xbf\xff<+\b\xa1\xc2g\x99\x8a\xa2\xc0\xec\xec\xecnf\xdeBD'\xf1\u0084\xd2\xf5b>\x8c\xd9\x02\xe0\xdf\xfd\xdd\u07e1\x8f}\xec\xe3\xfc\xeaW\xdf\xf0\x97\xcc\xfc\xe6\xff\xfe\xdf\xff\xeb\xffv\xcf=\xf7\xfc\xd2\xe1\xc3G\xdfx\xfc\xf8\xf1\u01a1C\x87\xc0l\xb5\x94JJ)I)\aKh]\x87P\xba\xdd.\xba\xdd.\x80\xe3\x00\x80\a\x1f\xfc\x01\xbe\xf1\x8d\xbf\xc0\xb6m[\xf1\xa67\xbd\to{\xdb[q\xcd5\xd7`\u06f6- \x1a\x8b\xf0B(\x80U\xd7N\x95q\u03e8\x8e\x87\x19\x8dF\x03R6\xb0\u007f\xffw\xf1\xc8#\x8f\u059eO\xa3\u0450y\x9ek!\xe8\xa0\xffy\xf1\xab\xbf\xfa+w\x18c\xaf\x96R\xc6b;??\x8f/}\xe9\u03d0\xe7\x05\x1ez\xe8!\xfc\xe0\a?\xc0\xc1\x83\x87p\xee\xdc9\xb8\xb0k\xa0\xd9la|\xbc\x83\x89\x89\t\xb4Zm0[\xcc\xcf\xcf\xe3\u0109\x93\xe8vW\x90\xe79\xca2\x9c\xbf\x19\xda)P\xf4\x1c\x0f\u07ff\x18\x8a'\t\x01!\xa4S\xcdI\x05!\x1c~EF;xE)\x90\x14\x98\xb9x\x0f\xf6\xdc\xfcV\xa8\xc64\xfa'\xcfC\x90\x80\x95\x1e\u03b0U:\x1bK'\x19g\x02d\u0260\x15\x03bF1.a\x05\xa10\x8c\xd202a\xd1\xca\x04Z\x8a\u0410\"F@\xba\xady(h\xec|\xae\x87\xdc7WA\x15\x9e\xed`9\x91\xfb\xd7\xec\u0513n\xd8\xd3\xe10\xa9\\\xa1X6\xe0\x81\x01t\x05MXCN\x8cSX\xb0P0\xd2CM\xab\xb2,|a\f\x16\x05\xb5\x1a\xef\u07cb\b\xf7\xa4[\xfe\xba\\?-\xd6a\x14\x11\xb1\xf1\xe1\xa0\xe9\xb0\xc8P\xeaT\xb8\x8aM\tm\x19}m\xd1-\x19\xb6k\xd0X1N\xdf\x14\x16\xaf`\xe3\fr\xe7h\x18\xb4l\xdc\xef'$\x023\xd1V(\x8e\xa7L\xa6\xe9\xae\x04k\xdc\xc0\xd3\x149LY:Z\xa0\xc7\u016d\xad[\xe0Zk1l\x04C\x9c:(8\x89\xbf{\xc1\xb8\xc2\xe6\xfd\xfb\x1b\x94\xa3a\x12\xab%\xa0\xa5\xff\xbb\xa2\x84\x18\xeb`|\xd36d\xcd6\xcaA\x1fBH\x90l\x00\xa6\x04\xc3\xc0\x18\v\xa5\xb26\x80\v6\x13\xf4\x82.\xe6\xe1\xf8\xd8\xc7>\u039f\xf9\u0327\xe9\x97~\xe9#LD]\x00\x9f\a\xf0\xf9;\xee\xf8\xca\a\xbe\xfd\xed{\xfe\u0791#G\xde\xf6\xec\xb3\u03e9g\x9f}\x16\x83\xc1\x00D\x8c,SPJ\xc2\x18\x05c\u071bQ/\xc8\xce3<\xcfs\x9c?\xbf\x80\x83\a\x0f\xe1+_\xf9*^\xf3\x9aW\xe3u\xaf\xbb\x15\x97_~9\xe6\xe6\xe60;;\x8b\r\x1b\xa6166\xf1\xa2\xceY\xeb\x1c_\xff\xfa\xd7\xf0\u06ff\xfdoq\xe0\xc03\xd1\x1e\x16\x80m6\x9b\xb2,\xcb\x03\x9b6\xcd>\x19n\xff\x8aW\xbc\xe2\xf4]w}}U!\xfd\u0527>\x8d?\xfc\xc3Obqq\xf1y\x16=8\xfa\x9f\u007f\x8c\x88iz\xe8$\xfc?\fR]\xb3\x13\xf2U\xd9\xc3.\xbc\x9a\xa7;bq%\xe1L\xfe\x11\xd9B\"^_\u0194\xf1\x82\x93J\xa23\xb5\x01\x9b\xf7\\\x8d\xab\xdf\xf5al\xdc{#V\xe6{\xe0\u0702\xd8\xfb\x1bJ\xc0\x12\x83\x02\f`\xad\xc3.|\xbc\xa7`\x8b\u018a\x1bD\xe6\xe3\xd2\xd9\xd22PX\xa0\xc8-\xba%\u0412\x16c\x99@S\x0e\x85\v3\xc5b\u00a3\xe4\xf7\xb4\x1ax\xb1pp\x81\xa5\x91\xe4\x16\xf7\xba\x19\xef\t3.\xdd\xcfK\xeb\x8a9\x85N\u0531G4\xbb\x8eU-\v\xf0\x84t\v\x95M\x89\x1c<Zk\xef!\b\xe3\u03ff\x1a\xfer\xb4\xc7\x1dAV\x84\x89\xefy\x1d!\xaaM\x15(\x88r\xe0\xc56T\xf3\u0231\f\xe4\xa5EO[\f,\x83KF\xabo\xa0\xb4\xdf}\b\xb8\xc5\xcc\xc3(L\x0e\xd6p;N\v\xb1\xa8\xbd\xe0K\xd6c,\x98\xeb\x93W\"\a\xad\fr\xe82\x87.\x8a\x04/\xd7Ug\xee\xa5\xfcQ4\xc4\x0e\"\xb4!\t\xc9\xc7\u00b1\x0f\xe1h\xf8HA\xee\x01\xa6tx9{\\\x85\x02F.\\\xeek)\xdd\xeb+\x8c\xeb\xd2\xd9X4:\x1b\u041a\u0704\xa2\xff\x1c\x84\u02a0l\x03F\xf7aJf)%\xba\xdd\xee<\x80\xf3\xe9\xc7h\xbd\x98\xff-\x8e\xb7\xbf\xfd\x1d\xfc\xe9O\u007f\x12\x1f\xf9\xc8G\xe3\xcf\xde\xfd\xee\xf7|\x1e\xc0\xe7\xbf\xf9\xcdo|\xf0/\xfe\xe2\x9b7\x9f8q\xe2\x8d\xe7\u03df\xbf\xf2\xc0\x81g0??\x8f\xa2( \xa5\x8d9\xa2\xcc6v\xec\xc6T\u0662\xc6Xt\xbb]\xf4z=\x1c9r\x04w\xde\xf95LOOc\u01ce\xed\u0631c'\xb6n\u0742\xed\u06f7ann\x0e\x9b7o\xc6\xc4\xc48\x84\x90qq`f\x14E\x8e\xc1\xc0-\x0e\xa7N\x9d\xc2C\x0f=\x8c\xbb\xee\xfa:N\x9et;2\xa5TP\xac\xb2\xb7\x8c]y\xc3\x1b\u07b4\x10\x9eK\xa3\x91}gnn\x0e'N\x9cDQ\x14\xb1\xa8.,,<\u07ee\xc5\xc1\x1b\x9e&XAB\xd5\xef+\xc3)\x8f\x8bZ[\xfbZ\xabx\a\x8f\v\x12\x94\xd8\x0f\xb3\xef\u0185\xc3\x14\x85\x80j4\xd1\x1c\x1bGgf\x16\x13\xb3\xdb\xd0\xd90\x8b\xe6\xf8$Tg\x12\xed\xd9\x1d\u0630\xfb\x1aL\\\xb4\x0fK=@,\xf6\x91\x19\xac\x9a\x00\xd6\xc9\r\xec\xf2%\xfdV\x98\f\x90u\r`\x81bR\xc24\x84\xebf\xddn\x1a]\xcb(\xacu\x98\xba\xb7\x96M\x85!\xa1C\x14\xabL\xb9\xea]+E\u067a+\xa4\\\xf3#\xa9\xa2\xc8\x02D\xcb\xc2]=\xc1\xc6\xc3Z\x8e\x1eV,\x1dvn\xfa\x16\xda\x14\x10ZAN(\u020c|\xe3H\xf1\xf1\xac\xc7E\xc2p\x17\xfe\xb1-\x1c\x13\x05a\xf8\xeb_2\x9bb\xdbT\x15a\xf6\x03\u3c01\xa0!*|x\xba6\xb2\x88\xc2\xca\xe0\"\xd2\x06\x861(\x19\xb9\xb1n!\" \xcb\x19\xb2`P(\x98\x02\xb0YP\n\xf9\x9d\x88f\xcf<\x12\xc8\x18PK\xae\xa0sS\xa4\xebfmg\xc2ZC\xe7\x03\xe8|\xe0\x18,\xa13\u05fe\x90'\x9f\u05fa\xd9V\xf5\uf2ad\xe3v7\xc2\u007f^\xdc{\" \xc8\u0090\u007f\rQ\t\xbb\xb4\x00J\xc1q\xe1v\x10\x8e\x00\x1b\v\u065a@kj\x13\x96N\x1c\x82 \t)3\xf7;\x06g\x8d\x06\x0e\x1e:\xf4\x04\x11-\x02\x10\x8dF\u04ee\x17\xf3\xbf\xe5\xb1c\xc7E\xab~\xf6\xf5\xaf\u007f\x8d\xde\xf6\xb6w\xf0\x9b\xdf\xfc\x93\x9f\x03\xf0\xb9S\xa7\x8e]\xf9'\u007f\xf2\xa5W\xed\xdd{\xc5\rg\xcf\xce_\xdb\xeb\xf5\xae\x9f\x9f\x9fo\x9e:u\n\xddn\xcf\x0f\x01\x1d\xf4R\x14\x05\xfa\xfd\x01\xf2|\x00c\x92\x0f\x871\xe8\xf7\xfb\xe8\xf7\xfb8q\xe2\x04\xbe\xff\xfd\xfd\xa8\nn\x03SSS\x9e\x19#\xfc\x82`\xc0\xec\x06\x86E\x91#\xcf\v\xf4z\xbd\xf87\x81\x8b\xad\x94\fjI\xbe\xe4\x92K\xb0o\u07d5O\x11\xd1\xe1p\xbbV\xab\xfd\xec\x8e\x1d;\x8e>\xf9\xe4S;F\xe3\xda\xf5\">\xfc}\x1a1\x97B)\xe1{_\xb8\xf9\x85HzD\x04\xa9T2\xfd'\x80\x84\x13\xffd\x19\x1a\xad1\x8co\u068a\x99]\x97c\u64bd\x98\u06be\a\x9d\x99Y\xb4'\xa7\u045a\xd8\x0096\x01-[0B\xc1\x88\x06\x8a\xdcb\xb9?@\xd6+\xd1.Qc2\x90\xf5A\x04\xc1\xf1\xce\xc0o\xe3\xb9R\x1b\u00b5\xca\xd9\xc0\x80\xc0\xc8'\x15LC\x80\xa9\u2a57\x86Q\x96\x8c\xbee\x8cenP\x9aIJ \x16\xdfuS:(\xad<\xae\xeb\x905\xd5\xcc\x0fC\x17\xccL\xae`\xfb\xdd>k\x06V\f\x88\xbdp\u01ba\xc1\x9f\xf10\x06\xc1y\x8c\xe8\x9c\xc1\xe7J\xa0\xb0\xa0I\x05\xd5\x10\x88\"R\xaeZ\u22bf\xed\x9e/\x0f\x85\x98\x86\x1al\x93?c[)+\x89\u073f\x1dN\\\x87t\x1c\xb3eH?J\xee9\x15\x86\xd1--\xfa\xda\xc2\xf8\xa1)\t@\u632co\xe2L \x0e\xab\xc3\xceE\x00d\u072e\x89\x19\xd0\xd6u\u045a\x19\x12\x80\x9c\xc9 2\x01k\xbdM\x82p\x85\u0616\x1a\xba\xc8aB!\x8ftD\xed8\xde\xc1{\x85\x19ll\f\xa7\b\u0316`o\xcb\x1e\x9b\x93\x82<\x1f\xdci\x15\xb8t\xb7\xa3j\xb5\x84\xa7\xa0\xc0\x92t\x1d\xb9\xa8\x16n\xf2\u05c71\x06\xb2\xd9B\u0599\x8a\xf7\x97\xc2TB\b\xf4\a\xc5\x00\x00^s\xfd\r47\xb7e\xbd3\xff\xbb:\xee\xb8\xe3+x\xdb\xdb\xde\xc1\x0f=\xf47\xf4\xe0\x83\x0f\xca_\xf8\x85_4ss\xdb\x1f\a\xf08\x80\xcf3\xf3\xb6\xff\xf6\xdf\xfe\xcb\xdc\xe1\u00c7ggf6\xbe\xe9\u0739\xf3;\x1ey\xe4\xe1%)\xd5E\x1b6lx\xeb\xc1\x83\a\xd5\u0463G\xb1\xbc\xbc\x82\x95\x95\x15,//#\xcfs\x0e\n\xcd\xe1\x8e\xd5u\xdf\x05\u039c9\x833g^\x98\x16\x18:q)E\x847\xac\xb5\xbcy\xf3\\v\xf9\xe5{\x06\xb7\xddv\xdb\u007f\x01\x80\xc7\x1e{\x98\xf6\xed\xbb\x86?\xf8\xc1\x9f_z\xf2\u0267\xfe\xea\u0211\xa3\x1fx\u4447\xd1\xeb\xf5G\x16\xde\xf4\xbc\xc2\xf7C\x85\u007f\xe4\xdf\x11\x11Z\xad\x16\t!\xe2\x1c!\xfd\xbb\xb4\xf8K\x95\xa13\xbd\x01B5AY\x86\xf6\xe4\x06\xcc\\t\x19\xb6\\~\r6\xee\u078b\xf6\u62d0M\u03601>\r\xb4\xc6\xdd6\u057a\xf3\xe8\x97\x1a\xa56\xb0\u0682M\x0f\xb64\x10\xc6uy\xc2$\x92\xf9\x9a\xf2\x93]\xa7g\x03\x8c\xe0)e^\x90\x130c\u0577\x00k\x14\x13\n\xa6\xe9\v\x9cE\xbc\xb8\xf3\u04a2,\x81AF\x18oJ\xb4\x1aU\xa7\xce5x\x82V\xe1\xc4\xe9\x8bfb\x91\xe4\x88)k\xc3\u0425+.\xc62d\xcfB,\x1b\x90u\x85-\x868\b\xef\u07c2D\xe0h\x00\xbbl\u07023\xa9@\r\x82\x02\x90\x81\x90\x81#\a\x9c\xfd\xf3\x14*a\xa2\xa0\xdaYp\xbaCH\xb6\xfa\x82\xea\u04c0\u06ae$\xb0K\xb8>-\xd0\x06\xe8Y\x87\x8b\x97&Q\u0252\xdf\r\xf5-T\xe1vI,\xfc\xfba\x00\xd2\xd6\xe3\xfc~\x87\xe1'\xa8\x82\x00\xd6@I\x8cr\xd9@\nB\xb61\x83\xf23\x83R\x1bp\xe9\xf0q.\x1dk\xc5j\r\xa35\xd8w\xe4)\xac\xc2\u0182\x8d\x89\x83P\xa7\b\xe5\xa4kO\xb6\x1b\xecp&1\xb0\xb0%;s-\xef\xfbB\x11\xa7r,\x1f\xaey\xe1x(\xce/ \xa42H\xcflcx_u6\x00;\xe7\x97M\xb3\x9b'\x01`\xff\xfd\xf7\x99\x93g\x17\xd6\a\xa0\u007fW\u01fb\xdf\xfd\x1e\x8f5\xbf\x8a\x01\xe8\x13'\x8e\u04b7\xbf}\x8f\xba\u77bf\x12\x9f\xfc\xe4\xa7\n\":\x1e&\x9d\xcc\xfc\r\x00-\x00\xdaZ\u04fe\xf7\xde{7\x1e8p\xa0\xdd\xedv_\u007f\xe4\u0211\xf7?\xf6\xd8c?q\xf8\xf0\xe1\xa9n\xb7K\v\v\xe71\x188_\x14\xb7]\x17\x15\xc5\xcdo\xfd\xa8>?\x1b\x8aJ\xab`\x8e\xa4Sf\"A[\xb7n\xa3\x9bo\xbe\t\xb7\xdcr\u02ff\u0677\xef\xea\xbb\x01`\u07fekB\x90s~\xd7]_\xfb\xbd,\xcb\xde=>>>\xfe\xc0\x03\xfbiqq\xe9Gz-\xaa\xee_\xa1\xd9l\x12\x11\xa1\xdf\xef#\xcb\x14\x8f\x8d\x8dQ\xbb=\x06\",l\u07fe\xddn\u06f6m\xfe\xfc\xf9\xf3_\x1b\f\xfao\xfc\xde\xf7\xee{e\xea\xff\x12\x1c\xe1\xc6&7\xe2\u019f\xfbUl\xdc\xfbJ\xa09\x86\xf1\x99-hM\xcd@\xb6;0\xaa\x05C\x02eYb\xa5,\xa1\xcf/:\xb8*\xf0\x95\xa9JI'\x02\x98\x04Di\xdc@3TTI\xd5\xf7\x9c\x14\xf6\x00lp\xc5\xf1vX\xad\xc3iI\x00j`AF\xa3\x98\x94\xd0-\xe1=^b\x1d\x85\xb5\x8cA\xc1(5\xa3m\x04&\xda@K\xc8\xc4\xe3|m<\xbc\xb4\fm\x19\x03mQ\x9a\nk\xb6\xceb\x1b:wb\x16\xa9\x19\x8d\x15\vU\xb2\x87\xa1\x82\xa0\x06\x95\xe8\xa66\xcfd\bKP\x03\xbf\x18\x8dK\x14\x19\x01\xdaB\x18\x8bL\xb8\x9d\x84\x92\x04\xa5\b\x92\x9c\u06b2\xdaI\x84\xe2\x9d\xf8\x8cp`\xab\x84q\"\xc7\xcf)\xaa\x97..H\xe9B\x95kF\xcfX\xe4NG\x19\xcd\u0442Y\x95\xea[\u0201\x89wB\f@3\xa4\xf1,\x16\x03\b\xe6\x88\xd4 @\x16\x9eQd4C\x9f/Q\x12\xa31)\xa1\xd8\xc0\x16\x8e\xb1\xc2Z\x03F;\uee9fc\xc1\x98\xaa\x90\a>\xb9\x87A\xd9$\x1cs\xb6\x91c\x8e\x80\x99\xb3\x05\x97\f;0@a\"}2:4\"\xb11`@X\x86%\xaa\xbbf\x06\xdez\x80\x1d\xbdk\xa21\xda)G\x01\xc7\xcc\xf2\xa1\xcf\xd5\u02b7\u0799\xff\xffrl\u077a\x83\x01\xd4\xd2\x1c\x9e~\xfa\t\\v\xd9^\x90\xe3\xa2\x05\v\xc2\x12@\xa8\x92\x8f\x03\xf8\xf4\xf1\xe3G\xf6\xfd\xf1\x1f\xff\xe7\x9f9|\xf8\xf0\xcf=\xfd\xf4\x81\xd9~\xbf\xbf\x19\x80XZZ\u00993g\x90\xe79\x8a\xa2,=.-\x84\x10\x94b\xd1\u00d0\a\xbb\xc3Z\u02d4e\x99\u06b0a\x9a\xb6n\u074ak\xae\xb9\xfa\xec{\xdf\xfb\xde\xdf{\xeb[\xdf\xfe\u007f\x00\xc0\x1f\xfc\xc1\xef\u04ef\xfd\u06af\xf3\x17\xbe\xf09\xba\xfd\xf6\x0f\xf2\xdb\xdf\xfe\x8e\xfb\xfa\xfd\xe57\\t\xd1\xce\xffs\u01ce\xed\xb7<\xf1\u0113\x8dS\xa7Na0\x18@)\x85\xb2,K\xa5\xd4\xf2\xe4\xe4\xa4PJ\"\xcb\x1a\x12@\xbe\xb0p\xee(3z[\xb6\u0309]\xbbv5\xa6\xa6\xa6N?\xf5\xd4S\xff\u03d5W\xee\xed\xddt\xd3M\xadK/\xbd\xd4LMM|\xf3\xaa\xab^\xf9\x8c?\xbf\xe6\xdf\xff\xfb\x1f\xfe\xcf\xdf\xf9\xcew_\x19,k\x03\x9c\x026 \xa9\xb0s\u07ed\xd8y\u02ed\xe8\x19\x86\x06A\x1bF^\x14\x18\xf4s\xb05\xb1\xd0H)!\xa5\x88\x1d\xec\xaay\x1e[d\x85u\x83&O\x13$\x9bx\v&xFJ\x19\x8c\x9e\xdb)\x89#p\xa7K\x8b\xe6\x12\x83\x8cB\xd9\x11\xb1[Mi\x88\xdaX\xac\f\x18%\bc\xd6A/JT\x9d\xab\xf5x\xb5\xb5\f\xc3@a\x18\xb9\x1f\x06\u06e4\xcb\r\x1d\xb01\xae\x90\x90\x01T\x9f]!\x0f<f\x06\x84\xae\xd8\x16\xc2T\u0404\x15\x04\xab\x1ch,\r@\x03G\xf3\xcb;\x12\x85 h\x03\x94\xdaV\x83l%\x905\x81\x86r\xe7\x9by\xb5i\xe0K\x8b\xb0\xb3!D\x8a \xa3>#\xa9\xcdU\xfdBg\xd8-p\xfd\u00a2[X\x18\x01\x88L\u01a4\x9ep{\x953\x1a=S=\a\x06\x84f\x90\xf6\xbb(\xeb0\xf4\xf0^\xa4\xf3\x06\xb7\xbb\xb2\xb1\xf8\x17g\v\xf4\a\x80j\x1ad\xa6pE\xdcxz\xa1\xadb\xe1\xc2\xc03\xf8\x98#t\xe2\xa6\xe2\x92G\v[\xe3\xfd\xcd\x03,ZZ\x98\xae\x86\xcd\xdd\xcf\xe3{b\xc9\u0767_\x04c\xb0\x89\x00l\x18\xe4\u05f6#\xc2\x0f_\r\xa4Tn\xbeV\x16a\x17\xc0\xadV\v\xcdv;\x0f\x9bo\xb7\x8f\\/\xe6\xff\u04ce\xcb.\xdb;\xf2\xe7\xcf<\xf34.\xbd\xf4\xb2\xf8\xefm\xdbv>\x06\xe01f\xfe\x97\xf7\xde\xfb\xdd\xeb\x1ex`\xffM\v\v\xe7_\xf5\xec\xb3\xcfN//\xaf\xec[^^\xdeND\x9ds\xe7\xce\xe1\u0739\x05\xf4z\xbd\x04/\xaf\x8a\xb9\x10\x8e1\xd2\xe9\x8ccjjRNMM\xa1\xd9l\xf6ggg\x0f]u\xd5U\xdf\xfc\xf0\x87\u007f\xfe\xd3ss[\x1f\x03\x80\xf7\xbd\xef\xe7h\u04e6\x19\x06\x80\xdbo\xff \u007f\xedkw\xd2;\xde\xf1Nn\xb7'\x1e`\xe6\u06ee\xbb\xee5\xb7\xdfu\xd7\xd7\xdf\xf5\xdd\xef~\xaf\xd9\xedvinn\xae\xdbh4\xbf{\xf3\u036f=\xf2\xaew\xbd#k\xb7\u06f0\x96\x1b\x9dN\xe7<\x91\xba\x87\x88\x06\xcf=\xf7\x1c\xee\xbb\xef\xfe\xf8\xbc\xbe\xf5\xado\xe3S\x9f\xfa\xbfG\x12\x8c\x17\x17\x97D:+H\x9b\r]j\fV\xfa(\x16\x80>\xaf@+G\xb7\xd3\\\x9f]\x06\x88\xe4\xf9\xecE\x84f\u021c\xbd\x89Q`\xa9$\x86F\x89\xc9\x16\xfb*\xcabd\xc4g\xac\xea\xc2\x12\xa8`4\xac\x06A\xa2\x1c\x93\x18\x8e\xbe\x16\x9e\x9fg\xc0X),\x06\x06hJ\x82\x0f\f\x82\xb1\x0ek7\x89\xe4=\xb0A\x86\x1e\xce\r\xf0\xfc\u03f2\x82\x91\xe5\xbe`\t\xf7\x1aH\xcd\x0e~\b,\x99\x94\xefg<\u03ac\xdc\x0e\x83\fC\r<\u04e2I(%\u0570m\xb0\x13\xe7\x14\x96=\xd4\xe2DS\xd2\xf3\u01a5p\xcf\xc1Y\n\xa0bQ'\x9c\xf2\xb8T\xfa\x81mQZ\xf4s\x8b^\xcen\xe1 @HQ\xdfa\x12 \r\x90\xf5\rD\xe9\x19 \xcc\x0e\x177\u026e*\x95\xd8&\x19\x9b\x1c\x02 `\x00m\xc1\xc2\x02\xb9\x81\x19\x18\xf4\xdb\x06J\x19\xb4\xc8\xe1\xe9\b\xbe*1\x1e\x8e\xa3\xdd-\x12qP\xc57\xf7\x81\x15\x89X\xc8\x18FQX\xe8\xae\x06\xe5\xa6fU\x1f\x98J\x14w\x1d\xceg\x80-\x03\x86\xc1B\xfa\x1cU\xf6\r\x84\xdbY9\xb7\xc6\x02\xc2\u04c3\x1d;\xcb\xdd\xf1X\xa7\x83\xe9\r\x1b\a\x00\xf0\v\xbf\xfc\xab\xb4u\xd3\xf4zg~!\x1c\xa1\x90?\xf6\xd8\xc3x\xcb[\u0789\xa3G\x0f\xa7\xdd\xf5\xf7\xfdW(6\xaf\xf8\xeaW\xbf\xbc\xf7\xe8\xd1c\xaf}\xf0\xc1\x1f\f:\x9d\xb1\xab\x1a\x8d\u019e\xc5\u0165\xb2(\n\x18\xe3\x042Y\x96\xa1\xd5j\u0271\xb16///?\\\x96\xfa\xe8\x15W\\Q\xec\u06f7\xf7\xe1w\xbd\xeb=\xf7\x12\xd1s\x1f\xff\xf8?\x03\x00\xbc\xf7\xbd\xef\xc1\x87?\xfc\xf3\x1c\xa0\"\x00x\xc7;\xde\xc9\u007f\xf5W\xff/n\xbd\xf5\x8da'\xf1y\xffU;>\xf3\x99\xcf\xfc\xad\x9f\xf7\xa1C\a\x82]BK)IB\xb8\xc1\xdd0\xe8`u\x89\xbc\u06c7^\xd10\xd0>\xb6\xcdC\x1d\x9c$\xb1F\xaa\x9cg\xba\xa0^\x1c\xc8\x02\xaa\xe0\xd8\xe5\x11\xfb\xa1\x19\b$\xb8\x1e\xda3\xd4\xd2\x13\xb9A\x1bQ=U,\x82\xd1\x02\x10\x1ah,\x1b\xc0\x00&\xab\x84\x8eQ\x96\xe2\x17X\x80}\a\u0315\xfd\xe9pr\x19\xd5a\xb3d\"\x8a #R\x1a\xc8r\x1b!\x05b@\x1a\xf7\xfc\xaa>\xad\xae\xd4$\xf8\xae\xd6\x12\xac\xf4E\xdd\x12Tn\xd1,\x9dK\xa1n\xb8\xd2#\xe06G\x94\x88?K\u02c8\x1a[r\x9d\xb9c\xbe\xb8b.C\x81\x17\fEa1r\x85\xbcdF^X\xe4\x03\x8b\xbc0\xfe\xf5&\a\xfb\xd0\xea\x18S\x99[\x88\xbc\xfa@\x90\xf5\x1c\xf2Z\xa6\a\xd5\xf0*f\v\xd6\xd6\v\xe8\r\xac\x1fb\x82\f\x98\f\x044\xa8\xb0\u8d41\\0Z\x02\xf0\x96\xe9\xc9{jcQG*\x18\x8a\x85\xdc\xcd*\xc2\xe3j\x03\xf4\xfb\x1a\xbao \n\v\x19\x86\x99\xfe=g\"\x18\xb8\xc5UD\x18T@\xa02\x1b\v\x10Tx=!\x04t>@\xd1[\xf2\xbb\x00\xe3rA\xfdy\x8e\x8fOb\xe7E\x17K\x87\x02l\xbb \xeb\xda\u02f2\x98\x87c\u07fek\xe2\xf7\xa7N\x9d\xc0\xdc\xdc\xd6Q\uc387\x00<\x04\xe0\x8b\xbe\xb8w\x00\\\xe2\xe1\x9a\xe1\xfeQ\xfa\x06\xebq\x1a%3\x04\xf0\u05b7\xbe\x15\x9f\xf8\xc4'\xf0\xda\xd7\u07bc\xeaw\xb7\xde\xfaF\x1c=\xfa\x1cv\xec\xb8\xf8E?\x97\x83\a\x9fF\xa3\xd1x\u07bf\xf5\x85\xdcw\xa0>\x1e\v\xa6>\x00\x00\x90\xf7\x96\xd1]8\xeb@\xabA\t\xce\b\xba)`\xd9\x0f$\x13\x18\x82\x13~\xb5\x1drV\x92\u05ba\xe2g\xfd\xb0\xcc\xfa\xa2\xe7)\x11\xf4|#\xdbT'\x13\a\x9d\x9c\xdc\xd6A\x17T02m\xc1\r\x81A[\xc0\u02aa\xa0\u04c8D\x1c\x1e\x01y\xa64>\u02d1\u679c\n\x83J\x8b\xac\xe7\xb0\xffpr\u00b3o\xc8\x0e\xe1\x1a\xc3\xd6\x00\xfe\x8eC\u0383\x95N %4\xa3\u0673 \x16(\x9b.FO\f\xf1\xdf\x05\ra\xfbn\xd6\a\xcd\xd1\xd36\xce'\x94\xa0\x1ac\xc70\xc3h\xf7\x05\x10\x04U\\\xc1\x9a.\u04cb\xb4T\xdf\r\xab\x01\a\xa5\xb8E(Y \xd9\x01O\x96C\x87\xec\xbai\x1b\x92~\x9c\x91\x80\u01f6\r\x98]3 4C\x82P6]\x91-\x89\u0410\x80\"\xe7\x9e\x19\xb8\xe2A\xbe\x1f\x86\x9fV\xdb8\xec\f_\x85\xb6\xe8\r\\\u01df\x95\xbe\x90'\xde6\x14\x06\x9d\x91zX\xa7\xc2:h\x8f\xa3\xe5\x04E\xc30\x05\x9d\xf7\x90/\x9fw\x9b\x03S\x82\x8d\r\x8c(j\xb7\xdb\u0631}{\x1f\x006\xcdlZ/\xe6\x17\xf21\xaa\x9033\xee\xbd\xf7\xbb\xf4\xe0\x83\x0f\xd0\xd2\u04b2\xbb\xb6\x9ch\xe9\xe1\x17\xba\xbf\x8f\u007f\xfc\x9f\xcaM\x9bf055\u016fz\u056b\xf8\xc6\x1bob\"\xc2\xddw\u07cd\xbb\xef\xbe{\u037f\xfb\xdb\x14r\x00\xb8\xe4\x92\xcb^\xd4\xed\x83#b\r\xef\xf7\xb4\x0f\xf2\xdd9\x9b\x12\x18\x14\xe0e\x06\x93\x02\x87|DO\u007f\v\x85Cx\xdb\u046a/\xa5\xd8\xe9\xa9\xc2\xe3\xab~\xcb.\x18#r-\xeb\xb5/0Z(\xf8\xb7\xc8\x04\xc0\x0e\xd7f\x80\xa8\x85S\x916z.\x95=\xefH\x87Q\xfb.7\x8c\aG\xfa\x13&4\x97\x10~\x1c\u050b\x14:r\x02Dn\xa1V\f\xd4\xc0-L\xc1cE\x9a\xb4`\x8c\x1a\xab\x0e5\xf8\xec\u0395\xe0pzA\xfeu\xe9\x19\x10\x00=&\"\x85e\x94\xab\xef0\xf3&\x8d\x1ce\x8f\xfb#\x11\x1aU\x8f\x9d\xec\x16\x86m\x81\xc3\x0e\xaao\x1d\xa7\u0727jS\xc4\xcc#\x01\xdf\x15n\xad\xc1\x1e[\x8e\x8a\xe2\xf0\x86\x04\vZ\xdf\u055a@Q\xb2\f\x05\x02\x93\x80V@\xdfZ\xe4%\xa3)\x81\x86\x00\xa4\xd7\xe4[c`\xb5\x01\xeb\xb0X\xa0\x1a|ZF^\x18\xf4s\x03S2\x1a\x06P\xa1\xb3\xf0|Q\"\x87\x93\x93\xb7i\x11\xf1\x9d\xaf\"\x9d\xd3l\xec h\x12B@*\t[\x0e\xa0\a]\a\xcf\x19\xed\xf1{a\xa5\xca\xe4`\xd0??>\xde\xf9\x1b\xbf\x1b\xe1\xf5b\xfe\x12;(j\xa2\xe3\xf5d\x9f}\xf6\x19ZYY\x11\v\v\xe7\xbdO\xca\x00B\b\x8c\x8f\x8fcbb\x02\x13\x13\xe3\x18\x1f\x1f\xb7;v\\l.\xf4\xe7\x96\n\x8d\\\xf1\x930\u05a2=\xb5\t\x9d\xa9Y\u75e15\xa8\xcb\x10\xc2\u008c+\x80$\x94\x94h*\x89\x8er\xe2\x96\xdc\x00\xb9q\x8d\xbca\x9f#i\u06337\xb8\xea\xf4\xc2\xe0\x93it\x15O@\xea\xa0t\x8c\xc6Tr\xa8\xa0yqQ\xb0\x9c&\x06\x1a\x03\x87e\xe7\x1d\xe1|Q<\x9f\x8f}g&\x92\x87K\xad\x188\xed\xf6\x87\x02\x96\x85f\xa8e\x03\xd5s]k\xb0\t\x10\x9aGx\x82\x8f\xf6_IU\x9b\xe4\xbbHa\x1cvm\x15A0\xd0\xe8:\xb8\xced\xfe\xb9\xda\u054b\x01\x0f}\x9f\n+\x13K\x97:\xc7|8\b\x9a\x87\"CAn\xd1\xed\xdb\xc8\x1b'c\xe12\xe5\x9c\u0692|\x00\xb2\xb5\x16\xec\xbd\u01a3B3\t\x99p]9;u\xa7\u05700\xb0d\xbdI\x8e/\xaem\xf2\"+\xa0o\x19\x05\x80\x8c-\x945\x10\x9c\xb8$\xa6P\x8e\x05\xf2\u0720;0\xb0\xa5Ef\xfc0\x99\xa8\x06\xd5\t\x1b:\xf2J5\x15\xf8\xe8Lu\xfc\x8c\u00c4]H\a\xcb\x18\x8d\xb2\xb7\x04\u05b9\xeb\xee=\xc3\x06\x04\u03b2\x06\xc6\xc6\xc6\xe6o\xbd\xf9\xc6S\x00p\u026e\x8b\xd6;\xf3\xff\x15\x8e]\xbb.\xe5:6\xf1\xd2<\x94R$j\u05a0.H\xc2\xe8\x12\u04db/\xc6\xcc\xd6Ka\xf2\xbe+D\x85\x81Xf(\t\xa0\xcdh\x801\x91\tlk\x13\xba\x86p\xaa\xcfCb\x14@\xf6-dnk\u04514\xf4\x1dV\xf7\u02a8\u0665V\x81\x91\x80\xf1\xe1\xcfI@M\x10\xda @\x1d`'.bF.\b\xdc\xf0\xde,\xb6j\xbd9\x8apFy\xc2r\xec\xc8\x1d\xf7\x9d\xa1z\x06\xaak \x8a$W\xd2\xfa\x0e\x9b0zQJ\x8a:y47\x9a\x81y0\\\xa4\x18\xbb\xa7j\x8a\x9e\x81\x96\x84\xa2\xe3\x02\xaf\x89\xebU\x9c\xd6\xda\x04\xd0j\u007f\x95\xd5\u007f\xc3\xf5\u074f\u01cdEi\xd1X\xd1P\x03\x03\xd2\xd6\xd1\x04=\x93\x04\x91.h\x00\x1f\xda\xc06\xfcn(\b\xc3\xe3\xe76t\xec\xde\x13\x9c\x03\xe3F\xc03b\x04\xd0\x12Q,Uh\x8b\xbc\xd0 \xb6h\b@\t\x06\xac\x89|sk\x19EiQ\xe4\xce\xeeZ1A\xf9\xd7\xce\x18\x87\x8b+\x12n\u0776\x9e\xba\b\xae\r\xf6\u067f\xa7B\b\xb0p\xb9\xa0L\x02\xf0\xcai\xa9\x14L\x99\xa3w\ue933\x15\xb0\x1a\u0194N\xf0\u0116'g&09>\xf6\xdc\xc6\u0249\xc7\x01\xe0\xdaW]\xb5\u0799\xaf\x1f\x17\xcca\a\x83\x81\xa6\xe4\xc2v_\x0ev\x99\u06b4\x03\xe3Ss\xd0y\x0e\xc1\x16L\x04QZ\xd0J\xe9\xdc\x0e%\x81\xacA\xc9\n\xda\xc2\xcb\xdf\x13\xcf\x13\xed\x04\x1c\x94\xd0\xdb\xc8>_\xe1\x1b\xb6{\xad\xb7\xa1\x01g\x0f\t\xf1\x9cH\x1c\xa9\xb6\x89v\x9445`@\x18\x94\r\x11;\xfapq\xa7\xdd\xfe\xa8\x90y\xf6\x95\x91\xc8q\xdaU\xdf@\x1a\xae\u01fc\x99\xb5h\u018c\x11%tu\xdf\x1e-\xbd9\x0e\x14\t\x0e\xda\u023a.R\xaf\x18\x97\xe0\u0107\x9cj\x03\xe7:\xee\xcf<bm\x1c2\xf5\x8a\xc1\x0e^\xcd\n#\x1c\xcdr\xa9\x84\\\u0480\xb1\xce\xdaW\x9bD>o\xaa\x82\x1e,i\x13\xf1N\x98'T\x01\x13\x15\x8e\x0e\xb6\x91Ji}Q\xb6l!\xfa\x16\x86\x85\x8b\x04\xd4\x1e\x1b7\xae\x1b\u05de\xad\x12\xe4\xf9\x96\x19\xd68\xe6\n3#cB\xe6\xc5d\xde\xe38J\xf2\x03\xccRE\xcbzk\x83\x10q\xe4\a\v\x04\xf2Fo\xce$\x8e\x84\x80P\r\x94+\xf3X9y\b\xd6j\u8c80\u0445\u007f\xad\xd8n\x9e\x9d\xc5\xd6-sG\x88h\x05\x00v]|\xc9z1_?~\xbc\xc7\x03\x0f|\x1f\xaf~\xf5u\xc8\xf3^\xb3(\x8a\rU\u01a8\xb7r\xb2\x16RJL\xcelA\xa62\x94\xdd~\xf4\x90\xb5\x02@nA+\x06F\ttK\v\xddw>&\xa5\xe7f\a\xbf)\x99[\x88\xc2\"u\x06\xac\xb7\x94#@s~\xbei\xf6\xc3_Q\x00\x00 \x00IDAT\xa8\xef\u01bds\x96\x15nX\u82b7\xdb-\xb3\xad{9e9C.\x1b\xe4D\xce\xcf%\xe9\xc4y\x04lQ\xc1-\xde\u042a\xe4\xaa#\xb7U[\x1d\xed\x06\xaa\xdc\xf75\xbcP\xd3\u0166\x8e\x9bG7\xc6Z:}\x80_\x18\x8d\xae\x06\xc0(\xc6\xd5\xd0b\x94@7#\x1f\xaf\x1a$\xc6\xf5\xd1X\x87q\x97\xdaK\xdb\x19$\x01+\x04Ta!\u03d7@\xee=\xe0\r{\x01\x8f\x89\x1e(\xd1\x0f,\xfc\x8c\xab\xc2\x1e\x87\u00fe\x98W\xe1\u02ee#\x8f\x03\bN\xa8\xa9\xa5\x81\xb0\x06Zy\v\x8a\xa8nr\xf7\x19\xbcj\x8cGA\x02\xe2\xa2\x18P\x815\xe4\x1f[\x10A\x06;\x84\x04o\t\"(x\xbfu\xeb\x19.\xcc\x16\xde\u0165*\xee\xc2\x05\x90\xaf\x9c:\x8c\xe5\x13\x87\xc0\u01a2\xcc\xfb0F{\xc1 \x1a\xadV\x13\x97_~\xf9\xa3\x17\xfcn{\xbd\u013d|\x8e\x83\a\x0f\x11\x00\xfe\xcaW\xbe2\x93e\xd9\xcd5?\x17r\x17Tsl\x12\x13\x1b\xb6T\xb8\xa5\xb5`\xeb\xd3\u7640\x9e\x86Q\x80!\x89\xc2\xe3\x00%\a/oDY\xb50\x88\xb1[\xc18k\xb4x\xfe\xf9\x8a\xfcj\xfbB\x0e\xf0\x8bq\x8c\x10p\xc5(a\xf2\xaaD?DT\x03\v@c0\xa1`\x9bT\xc1\x11C\x0f\x1f\x9d\xbcC!\xb7N\xce.\xca\xcad*.(5N>\xaf\x02Uj\xb0C\xba\xe3H\"\xf0j\u05b5\f\x10\v\xe7\xaaH\xd5.&\xeb[\x80\r\u028e\xf0\xe6V\xd5\xcea\xd8J\x97B\x18r\xf4\xff\xae|Gl\xaea\xcb2B# \x02\v\x82*-\xb2%\r10\x91\xe7M!<\u067f\xef\xa9t\x9e\x83\x978\xd8u\xec6\xc9w\xa2Du\xe9\xcdZ\xc2\x10;\x1ac%\xd9~\u04baA\xb2\t\x1cO;\x94\xfb\x17T\xa7\xfe\xf5\x94\xccP\xda\x0f\x9b\x03\x87\u072b9\x11\xe9\x96>\xc0\x82R\x9f\x1a\xe1\ny:\xbf\x00E\xd5.\bP\xcd&t\xd1\u00d9\xc7\xefE\xb1\xb2\x80|\xd0E\x91w\xdds\x90\x02\xe3\x9d1\x8c\x8d\x8d\x1d\xb9\xed\xb67\xfd\xc5z1_?.\x98\xe3\xe7~\xee}\f\x00\x87\x0f\x1f\xbd\xe2\xe8\u0463\x1d\xf6pBhj\x98\r:\x13\x1b19\xb3\x13\xba\f9\x8c\x1c\xa5\xe2L\x02\xa4-xE\xc3H\r\x9b5\xc0\x02\u0436\xa2\x83\xc9\u04b1\"R9(%6\x1a\xb4*d\x99F\xa3\x15D#\xb1aw\xf1Wj\xd1\u062c\x86\x02@\x1c%\xfe\xf0|\xee\x96\xd5\xc8'%L\xab\x12\xca\f\xd3\x0fA\x1c\xe3\xc4D\xe9\xf0\xfeXP\xfc\x13\xa0\xc8J\\K\xca]\xcbMJA\xf2\u06a8\x80\x12\x06M\xa0\x04\r\xc3\xef\xc2\vx\x82\x9f;gT\x9d\nP\xa5\xf0\x04I{\xf01\xf1?'x\xca_a\xc0\xdaT\xe6QB@\xb0\x80\xea\x1a\xa8\xae\x87W\x9cK\xd8*\u07df\x00\x9b\x04\x13\x1c\x8e0\x8bM\xd01Nr\x908v\xc4Q\x1d\xed\xb9\xe3\xae+\xae\x1c\u0365e\xd8\xcc\xdb\xd2&\xbe@\xe4!!cl\f\fQ6\x14*\xaa\x99\x93\xa5a\u0615\n\xb6\x82\\l\xb0\xe9\x8d\x0e\xa3\xae)\x11$\xc1R@6Z\x00I\x9c~\xec\xbb8\xfb\xf4\xf7\xd1\xef\x9eG>\xe8\xc1\xe8\x12B\bXcx\xf3\xe69\xba\xfe\xfa\xeb\xe7\xf7\xee\xbd\xea\a\xeb\xc5|\xfd\xb8\xe0\x8e\x13'N\xbc\xf1\u0631\xe3U\xac[\xa0\x9e\xc1\xa23=\x87\xe9\x8d\x17\xc1\xe6eT\xf5\x81\xc8yZ\x04\xac\xb7`\x88\x15\r\u06f60M\xe1\xb0M\u007fO\"\xe7\n+\x8f\x85kh\x1aGk\x10\xcb\u04e4\xddx\xfb:o\xbb\x86\xbd[$\x9d.\xfb\xce\xd9ue,\xc8\x1b(\x11Ta\x81e \a`Z\"v\xe8<40\f\xb6\xa8j`A%GC)'\xf6\xf4~25o\xd9a\x80\x85VA8\x15\xf0\x9f<M\x9b\x90g\xc2j\x17\xe6\n\x89\xf3\xa5\xb0@\xe6\x19:\u0178\xb3\xff\x05[\xb06Il\x9a\xad\xba\xe6\x14\xdb\x0e\x10\x8b6Q\xfe\x1e\x8a\xac*\f\xb2\x15\x03*=\xbe\x1d}u\xbd\"\u0606\xe2]y42\xd7\x03#\xc2\xebm\x83\xa3\x0eW\xc3\xd0\u040d\x1b\x138\xe76\xd9\x121\xac\xb7\u0115\x02\xb0\xd2\x17x\xebY'\xd6:\f\u076f\u007f\x99u\xc5<\x18f\x85\x05L\xacN\xaf\xf6\x1f\t\xbfK\x10\"\xb0\xde!H\xfa\xbf\x0fb!\v\x92m\x90T8\xfd\xc4}8|\xef\x9dXY8\x8db\xd0s\xaaO\xb70p\xb3\u0664\xb9\xb9\xcdx\xf5\xab\xaf\x8d\u2f75\xf4(\x17\xc2!\xd6K\xdb\xcb\xe3x\u6667B\x91\xe8\xe4\xf9\xe0CeY&\xb6\xb9\xae\x1b\"\x12\x98\x9e\xb9\b\xe3\x93[a\xb5\xae\xb6\xd2\xecq\x14\xed}\xd0\r\x83rvI;%bP\x01iv\x1d\xed\xf0\xb6\x19\x89D\xbfFf\xa1\xb5\x91\x96\xe1\x82I\xb5\xf8\x9f\xb8P\x84\xc7J%Zd\x87\xa8\x8d\u059dWs\xd9@\xe664\xed#G\x97\"\xb7\x8eO\xceU\xc7\u01e9K\x17\x8f\xfa\xab\xe1\xd3\x1fAQ\xe4\xd1{\x11\xb2~\xa0\xca\u4564\xc1\x03\xc5?\xb0a\u023eFv>\aus\xe7\x17b\n\xa7\x03\xd0\x05\u0614\x0e\x13\x0f\xe6U\xb6\x8aV\xe3\x98.\x15\n\xbc\x85\xd0\x16\u064a\x86\xccMe`eS\x88\xc3e\xe0\u054a3\xb8\u05a9W\xde\xf8\xec\x98\x1f\xfe1l\x90\xe6\x1b\xeb\x04?6\xc9\xe9\xf4\xefQTezz'\xb4\x1brF\u007f\xfd$\xbfSZg\x95\x00\xcb0\x9e\xa9B\x00$\xeaA\xe0\x11\xff\xf6\xda\t!\xa4\x1f\xde\x10\x84\x94\xd1\x04(\xec\x84T\xbb\r\xa1$\xce>y?\x9e\xfb\u0397\xb0|\xe60\xcab\xe0\x14\x9f@p9\xa5\xd9\xd9Y\\s\xcd5O\xde~\xfb\a\xff\xaf\xf0h\x17j!_/\xe6/\xa3\xe3\xd2K/\a\x00|\xf3\x9b\u007f>\xf7\xcc3\a7\x17E\xeeY\x01\x0e^\xb0\xc6@eMl\x98\xdb\x05!\x9b\xce\x1b\xa3\xa6\xbeC\x84]\x9c5\x1eCu-\u050aF\xd8\xc1\xbb\xc1\xe7\x90\xe9v(\xe4r\xa8\xa3]\xa5\xe3\x1f\xa2f\xd4\xc484:\x95\xc7:>\xbbH\x1e/\xa6\xf2$\x18s\x18\x94\xca\u0722\xb9d\x90\rx5:O\xa1+w\x1dk\x8a\x92\b[Q\t\xa96~\x1cm S\xa3v\x0fS\xea\xb9>\xf4%\xeb-\x01<\x1f:\xf0\xf1\x85\xb6@Q\xba\\\u0322\x80X\x19 ;\u05c3\xe8\x0e\xbc\x87\x88\xb3\x8e\xb5Z;K\xc7\x00\x97p*\x87\xaf0hf\xebT\xac]\r\xd9\xf7\xf0\x8a\xef\xe6\x899\xc1\xc28\xc2'q\u01d6Z\xce&\x9dz\xbdsw\u007fk\xb5\xf1\xbeE\x1c\xb1\xed\xcaL\x8dc\xda\x12\xc0 c!Jw\xbe\xe4\x87\xc1\xe1\xe5\x91\f(\x1df\x16\x15\xfbH0U\x9e+)\xa7\xde\xffCx\x97S\xe7y \xdc\xe03(B\x05!k\x8d\x81m\x89\xe3?\xf8\x06\x0e}\xeb\vX>y\be\xdew\x8aO\x0e\xc6q\x12\xcdf\x13{\xf7^\x81w\xbe\xf3\x1d\x9f \xa2\x02\x00\xbe\xf8\xc5/\xac\xc3,\xeb\xc7\x05\xb4z\v\xf9\xe6s\xe7\xe6\x91\xe7E\xb4\xbc\r\x95\x85\x84\x84h4\x87\xe2\xba*\xa5%\x05\x01L\xa0\x8d0\xa0z\x06\x99\x00tK@\xe5\\\x85\x19\u0512\u042b\xa1\xd5jT\x85\x86\x18}k\tq\xea\xaaE$\x14\xf4X\xb8E:\u0324!%\xb7\x83\\dn\u0440\x06\x93\x84nz\x8d\xa0\u007f\rT\xe1\x15\xab\x11[q\xf0\x920Im\x16XM\x00\x1f\xb9\xb5\xa8o\xffk\u03c6\x91.3\xee|\r\xbb\x01.\x12Z\xa0\xe7\xbd[o\n%\xb4E\xa6\t\xb6C.B\xcfw\xf6\x8c49\x89+\xd1&\u06da\x8dm\x96[\xa8\xdcQF\xbd\x02?\x89\xacOv\"\xf1g\xc904\xe0\xf3\xb1\x90\aY|\b\u00a6\u02b66\fJ-j\xf0J\x94\xeb\xa3R_*r\x81\x1e\x9a\xaa\xe1\xabd\x82\nj\xe1\xd4R:\b\x8a\x82\xc68}\x9e\xd5\b\xb6\n\a\xf7\x8b\x8c\x90\nY\xd6\x06I\x81\xe5s\xc7q\xe4\x89{p\xf2\xc9{Q\xf6\xbb1\f\u00c5\x9a\x13\xa4\x92`\u02f8\xea\xaa}x\xdd\xeb^\xf7\x1f\xdf\xfd\xee\xf7|\x15\x00>\xfa\u044f\xd2\xfb\xdew;_\xd0\xd7\xf6zy{y\x1dKKKse\xa9\xfdE\x97\x94 \"Xk0\xc8W\xc0#:M\x1f\xc1\xe8qj\xcf\x06\xf0[\xe5F\u03c5\xfe\x8a\xe8Y\xee\xf1KOidO\xfe%\x1e\xeaVGu\xdb\xc35\x91\xab\x01e\xed\a4\fkT\nS\b\xaf\xe7\xf6\u007f\xc7IJ\x0e\x19\x86\x1cX4\x96-T\x81\xc8[\x17\x9a!\xbb\x9e\xc1\x82j\xb8'-Cr\xe2OO\xcf\u007f\xb2\\\u06d2\x8c(\xf3\xf1iT\u07be\u0387D\x83\x8b\x01l\xeeS\xeaKO',5\x10%\xf4\x16\"7h,\x97\xceo\xdc\x06/\x14\x93\xb0M\xc2\xe3\xa4\xee\x86\x16j\xa0\xa1r\xebw\x19\x1c\xe1\xa7t\xf7\xe5\xfcTL\x02\xa7\xd8X<\x81\x14\x8e\xe1\u029b\xc5T\xd0\xca0\x9eN\x1e\u06a9Ac\xf0\xec&[9-\x8a\x84E#\xe0\n\xb9\xb4\x9ez\xe8\xc3]\x04Q\xfc\x8aiP\\}v)\x11\x84!\x0e\xb9%\xb2\xac\x05\x10ci\xe1\b\x0e\xfd\xcd\xd7\xf1\xc87?\x8b#\u007f\xf3M\xe8\xbc\x0f\xa12\x17\u007fH\x0e\x8e\x91J\xc1\x1a\x8b\u077bw\xe3\xcdo~\xd3\xe3\xff\xe2_\xfc\xef\xff\f\x00~\xe6g~\x9a\xde\xf2\x96\xdb\xf8B\xbf\xb6\xd7;\xf3\x97]g^\x850\xd7\n\x8d\x10\xb0\xb6\u0120\xb7\xe8\xd3}j\x16\x84C\x05\x98\x12\xcbU_\xd0KS\xf3\xbd\x88\x05\vU\xe8A\xed\xf1hDXD\x8d\x88\x9d\fCiD\xe7;\xb2)\xa6\x8a\xd1\x12\\\xbah(\x17\xde\xe3\xdf2\xb7h,i\xe4S.(Z\r,dak\xc3Z\xb2n\x0e\x00\xfc(Y\x04#h\x97C;\x8d H\x8aV\xae\xa1\u02e5*\x12-(&#VM\x95\x0fwX\x91d\xc1hXF9&\xa0\x9bb\u0235r\xa8#f@\xe5\xce\xf0L\x1a$\x90T:\u04f0C\xe9=~Qb\x8eC\xc7\b\xa9p\n{P\xb4\x92\x8d\xae\x87\xa8\xbcpl(\xe4\xfe\xef*Q\x16\x92\xa1)A\xb1c\x9e\x18\xe1(\x88\x8a\xadw\x00\xa0\xca?\xc5&Cu\xaa\xad\xe4N\xd1\t\xf6\xbeA~\xc1\x90\x19\u02a2\x8f3G\x1f\xc6\xfc\u0267\xb04\u007f\x04\xcbg\x8e\xc0Z\x03\xa9\x1a\xee:\x00\xc1J\x05\x995`m\u0266,h\xc7\xce\x1d\xb8\xf1\xc6\x1b\x9e\xfc\x95_\xf9\x95\x9f%\xa2%\x00\xb8\xe5\x96[\xf8\xa7~\ua9f1^\xcc\u05cf\v\xeah\xb7\xdbOeY\x06)E\x15\x1b\xe7\xc4\x11`mP\xf4V\x92Z\x1a\xe8p\x14)^\x84\xaaH\x06\x95\x0e\x99\xca\xe6\xd6J\xbf\xe5\x0eb\x11/\xf4\x11\xb6Nba^\vv\xa6\xd1?\xe7\xa1\xda\xe8\u04cdyDkO\u05a9P\x8d\f9\x90ud\xc4\xd5s\u03eb\x16\f\xd3\x10\x90=\xafX\xb5\xc9I\xfa\xe7\x14\xec|\xe3\x02E\xc3\u0388\x8cQ\x88z=\xc0\xc4\x15<\x1b\x14\x96\xa8\xa0\v\x88\n\x1a\x89\u007f\xe8\x17\xb6\xb8\x96Q=\xf8Zj\x06y\xb5\xa8n\x8a\xdaZRQ\x16\x1927\xce\u007f\u0778!v\xf5<\xb8\xce\xebDU`+\xf6\x89\x8d\xafU\u0569W\"*\x8e\v\xa8\xf5b\x1fNT\xa9\xd503x\xa8\xb07\xacw\v\x96\xcb+\r\xcfI\xb2\xc3\xc33\xdfp\xb0\x17\x1a\xc1CI\x91zjy\xd5\xce\xd1c_\x11Z#!Q\xe6=\x1c;\xf0=\x1c;p/\xfa+\xf3\xaeOWM\xa8\x84\x11\xc3\u0110RA\xaa\x06\x0f\xfa+\u0632u+\xae\xbf\xee\xfa'\u007f\xf1\x17\xff\xe1{w\xef\xbe\xf4q\x004?\u007f\x86gff_\x12\xd7\xf6z1\u007f\x99\x1d\u059a\xefl\u06b4\x11Y\xd6\xf0\xb8y\u0569\x1b[\xa2\xe8\xaf\xc0\x98\x12\x14$\xd0q\xe7JUWe\xd9\x19MyN\xb7cdT]\x19+D\xae4\xfb!\x1fE\xee\xe2\v\xb4\xb8<\xe4}\x9b0Ix\xa8\u00a7!\fq\xdc\x19\xdc\x15\xb5ss4\xa2:\xe7\xaa\xe5\xe6\xb8-\u03fa\x16Y\x8f\xeb\x186\rm\x0eR\x1d=\x85x\xbbZ\xd4r\r\xcbO\xb2#+\xfc\x98\x11=Ol\xe899Y\x1b\x12ug\x8a\xabGn\xb9q\x11u\x10\x15\xebD2\xd0\xec\x1a\b\xcb([n\xb7D\\A\"r`\x90\r\x8c{\xaflE\x19\r\x90S\x85.\xa36\u8904\xc1b\xb9\xf2\x19\xb7\xde3%\r\xa7H$\xa2\xfe\xed\xe3jw\x10\x96\x8a`\x9aOT\xb1g\xa8Z\xf6\xd8r\xe5;\xceT\xc9\xf1}\xe1%\"? v\xb4\u01b8\x03\xe1\n\xdc\x12\x14\x18Y\x0e_[8u\x00\u01df\xb9\x1fy\u007f\t\xb2\xd1Nf#\xfe~\x02\x14D\x84R[\xda4;\x87\xd7\xdet\xe3\x93\xff\xf4\x9f\xfc\xc6{o\xbc\xe9\x96\xc7\x01Px\xec\x97\u032e{\xbd\xbc\xbd<\x8e\xa7\x9f~\x02\x00\U00016dfc}qvv\xf3q)e\x92\x14C\xb1\x9b\uebdcG\xde_\xae\"w\x12\x8b\xbd*\u6362\xbc^\x98P$\xd8[\xbc\xfad\x1a\u02f5b\x94\x0eBG\x17\xf1\x14\u007f\x19\xfe9\x8d\xbei\x8aj\xac\xc6x@\x06\x90E\xa5\x10\r\xe7\xe5,\x00\xdc\xffe\xc9P\xb9\xb7\u007fM\xf3\xe2\x14\xd5t\xfeU\x97]\x89cj\xcf\xc6\x17\x13k\n\x18=\x80\xd19\xb4\xcea\xb4\x0f/6\x05\xac)amj\x1f[%\xe7 \xc2\xd1\\1R\x12<\x1b\x9e\xe1\"K\x860\xde\xe1P[\xc8\u0720\xb1T\xa2\xd1\xd5 cb\xa7-\n\x83\xacg\x1c[\xc4\xdfV$y\x9a\x96\x13\u02a2\xef\xc2\x19)\xab%\x04){\xcfr\x9b\xc07IW\xcf\xdes%(b\x839\x96+\x84\"R_\xab]C\xaa\u016c\x16\u02cc\x05T\xf0h`\xef\xcb\x12>k\xe1M\xf1l\x15\x11\xfe/\x84\xf3V\xa1\xa4+\x97\x12\xba\xe8\xe1\xec\xd1G\xd1[\x99w\x8d\x8a\xad\xf2=\xd97\v\x82\x84\v\xea\xd6\x1a\x17\xed\u0709\xf7\xbd\xef\xfd+\xff\xe1?\xfc\xeeo\xf9B\x8e;\xef\xbc\x13ke\x12\xac\x17\xf3\xf5\xe3\xc7z\x84\b=\"Z9}\xfa\xf4\x97\x01\x80|vg\xca\xd3-\a]\xe4\u0765\xea\x02\x1a\u00a9E\x9a>\x13\vd\xbd\x8c\x86\xa81w-\xd2\b~\xf9\x1a\x85\x9c\u05c0\xa1\x87\xb0sZ\x033\xafq\u0343J0$\x01\x05\x9es\u02b6\xb1\x15O=\xdc\x16\xd6eDj\xe9%4f\x88#\x9e\xbc$A1\xca X\x1f\x00lt\xe1\xa4\xf5)4\x118\xd8<\xe42\x98b\xf95\nPb`\x1537\xabs$\xed\xa9\x8b\xda\x15[*-\xb2e\x8d\xac\xeby\xe6\xda\x19\x84\x91\xf6\xd2zckB.$E\x9c\xb9\x12\x04\x85\x02n\xbc\xd0'\x0e@=\xc6]\xa9wiUh\x84e\xaei\xc3\u023b\x18V\x8aYJ0o\x11?\x1b\x10\x02\x12\x14\x13\x80d0|K\x06\u035c\xe0\xe5B\b\xa8F\x1b2kA\b\xe9<\xf9\x93\x0e^\xaa\f\xc5`\x19K\vG=MQ\xc4\xddN\x1c\xce\xfa]\x811\x06Zk\xbc\u136f\xc7o\xfe\xe6o\x1c\u0771}\xfb\xb7\u00a3\xbe\xf3\x9d\xef\xe4\x97\xda5\xbe^\xcc_\x86\u01ee\u077bt\xa6T\x8d\xbf\x1b\x14r\xbap\xd1Y\xa8\rI\xb9\x06\xaf\n\x06\x84\xa5\xa1`\x06\xaa\x15R2\x88E \xd2\xe5\x80\xe7/\xecT\x83\x9e\xd7f\xba\xa4\xb5/\xfa\x9b\xd4\rvca\t]\xb9\xa5\u06a2\x11\n{0\x80\n\t4\xc4\f\xa3\x00\xad\x9c`\x87-\xaf\xe9$\x93\x9e\x905\xa5\xe7*\xdb\x1a%p\xd4*U\x1f\x89\xa6\ue0e8\u007f\x9f\xf2\xb3\xfdW\xe0\x8f\xbb<K\x1b\xbfH[g\u05fbRB\xf5J\x88\xdcq\xcf\xc3b@\xe0$\xe8\xc1\xc6\u0162V\x90\xa3}m\x85\x91\x10\x8d\xe0\xd4sE[\xa4\xa1.\xbbF\u4a7d\x06\x14\xf38\x1d\xc5\xd3\xf9\xcd;\xe4\xcd\xdb,\xf8\xe7\xc5\t\x03I\xf8BO\x00\xa4g\x9f,/\x1c\xc3\xca\xe2\t\xb7\x03!\u007f\vA\xb1S\x0f\x85;\x18j\x11\x11d\x10\x13\x81\xfc{\xca.\x88\x9c\x19\xa7N\x9e\xc4\xe9\u04e7\xce\x11\xd1\"\x00\xdc\xfd\xe7w\xbf$\xaf\xebu\xcc\xfcex\xccl\xdc(\x85\x14\xae HT\xa0\x817\xe9\xd7e^\xebr\xabb\xc6\xd5\x169\xe5w\x0f\tc\xaa\xe1\xa7/W\"\x91\xe1'^0\xa3\x8a4\xa5\x83<Z\xa3cO\xb1t\x1e\u075eW1g\x1c\xe3\xce(\xca\xed+\xa0d\xb8\xdb\x176\x14\x15D\xbb\x80a\xae;'-\xa8\x83V\xb4Sd\x06\xe5$\x0f+A\xa9f\x80\x85\x11i7\xa9\x1dT\xcc\xe6Ko\x1b\xbf\xb5U\x97\x19^\xaf\xe0\xf3^22\xe3\x1d\f\x93\x1d\x87+\xd4\t[\xd3\xff\xb7\xe6G\xce\t\f\xc1\x0e\xd7G\x12\xe5VS\x8a&&\xeaL\x15N\x9e\xf2\x86\\\x13/\"\xf6\x1en\v\x0f\xb7\xc5b\x9d2k\x02\\\x15\xfc\xc7\xc9Q\x14\x89\b\xb2\xd1\x04\xdb\x12\xc7\x0e|\x0f\u01df\u074f\xd6\xd8\x14.\xbe\xe2\xf5\x98\x9e\xbd\x04\xe4\\\xdd\\\u0291)16\xb1\x11[.~\x15\x9e{\xe2\x1e\xe8\xb2\xef\xe4\xfc\xc2\x17\xfd\x00\u01d0\v\xc6&b|\xef\xaf\xef\xc1\xefg<\xcd\xcc3D4\xff\u05b7\xbc\xf5%y]\xafw\xe6/\x93\xe3\xfe\xfb\xef\x8d\xdf\xcf\xcf\xcf\xeb\xb2\xd4U\xed#\x8a[h\xaa\r\x1c\xab|E\x1eQT\xe3P12\x18\xeaTF\xc1\xf5\"\xbe\xa6\xce&)\x00\xe9u=\x12nI\x19\x1f\u00eb\b\x11\xb8\x9eP\xba\xeaN\xa8v\xfbp\x17\xf5T\x1b*\u0639\x16\"2\xf7\x86\xa0\x960\xb830\xba\x841\xa5\x0f\xce\xe3j'R{\xd2\u0460v\xcdn\x1d\xa8\v\x8a\xe2\v\x91\xc2\xf2\xb18'L\x98p[\x1f\x80,4 KD\xa1\x13\x12(\x87\x13;,\x8e9\xa9\x9e\x85\x14\xbab\u02f0\u01b1n\xac1\xee{c\xc0~\x97\xe2d\xf7u\x1ac\xf0\u008f\xe2\x1e\xcf\a\x8f\x9e\xf3\xfe\u70bcca:\xe6\xb0u\x8b`\"\x02K\t\b\x01\x05\x01)\x15\xb2\xe6\x18t\xd1\xc3\xc1G\xbf\x81\x03\x0f\u007f\r\vg\x0eb\xe1\xf4!\xac,\x9d\x02I\a\xb3\xd8\xe8\xa9n\x905Z\xd8u\xd5\xebq\u054d\xef\xc7\xc5W\xbe\x0e\xd3s\xbb!\xb3\xa6\x87\xc0l\xdcG\x18k`u\x8eS'\x8fb\xff\xfe\xfb\xf7\xfd\xfb\u007f\xff;\xbf\xf0R\xbe\xc6\xd7;\xf3\x97\xc9\xf1\xb9\xcf}.~\u007f\xe2\xc4\xc9+\x8b\xa2p\x17\x90\xa8\x04@\xbe\x1f\x8a\x98fZ\xfdhT~dr\x1b&W\xb8\xb9\xc6Cv,\x05\xf6\xdd\x15q\x02\x81\x8c\xf07\xe7\xc4X\x8bW7\u0135`\x86\x91\xb5\xbe\x96\xe2\\7\xa6eJ`\vB\x8d\xeaG\x89\x1a\x95\x85\x8f\x1f\xd3\x1e/\x0f\xf7FTu\xdb$\xc0!\x8d\xc6\x18\xdf\xf5s\u0176\xa1d\x87\x92\x9eR-]\x82\x86^N\xaa\xbd0\x9c\xa4,E>w\xb4\xa2Mp\xf7\b\xb3se\xcb\uba60qTKCV\xbdT1X\xd2\xf3\x8c\xbc\xf7\xf8\x8es\x92\x9b\u0249i\xa5\x0f\xd3\x18\x9ac\x04\xdex\xfc>\xb0A\x98*o\x14f\xefxY\x050\x13\x10#\xfe,\x18\x86\x80\xa6\x90h\x90\x04\f\xe3\xfc\xd9g\xf1\xdc\xe3\u007f\x89SG\x1f\x8a\x9d\xf6\xd4\xccE\xd80{i\xf2\xdcS\x96\x92E\xa39\x8e\xed{\xae\xc7\xdc\xc5\u05e0,{XY>\x8d#\x8f\u0743S\x87\x1ev]:\t\x94\xf9\x00\xf9\xa0\v\x80\xb1\xb0\xb0\x80o}\xeb\xdb\xefd\xe6\xdf'\xa2\xe2\x91G\x1e\xc2\xd5W\xbfb\xbd\x98\xaf\x1f\x17\xd6\xf1\xd8c\x0fc\u07fek\x00\x00\xf7\xdc\xf3\xad7\xfc\xd6o\xfd\xf37\xe5y\xee/t\x91\\\xb6\f\xa92\b\u0568\xbalN\xbd\xbb+\xd3\xff8\x90d\x97\\\u00de_\xee\xac\aQ\xf3\x05!\xeb xJ\xf0\xea\xb5\xfc\u0369V\xd8\xeb\xf8\xf7\xea\f\xcc\xd5\xeeU4\xac\xbe\x8c\x9cw\xacR\xfe\x04?tG\xa3s\x85\x9c\x05\"/\x9d\x86r\x90+*\xa4\x81\xb1%\xac\xd1\xd5\xd9\xc7\x13\xe6zd\x1b\xf1*J\xe5\xf0s\xae\xadp\x91\xceX\x9d;\xa7\u06e4\x14~\x11\xe9\x16\xdb\xdf6P3\xadO\xeb\x11\x04\x96\u038dP\xd8*$48\x15F\xffr\x0e\x83lJ\xe8\x96\xee\u0770\x81R\xe8?\fb\x84\xb7\x0e{\xf6\ty\a\xcd`\xaf\\\xadU\t/?\xf8\xa5\x84\xb5-\fO\t\x10J\xa23\xd1F\xc3\x02\xfd3\xa7q\xec\xc0~\x1c{\xe6~\xac,\x1e\a[\x83\xe6\xd84\xb6\xef\xbe\x0e\x17_\xf9\x064\xc76@\x1b\x97\b\x94f\xd9\x02\"F\xd7\t\x99\xa1\xdd\u0684\x89\xd9\xed\x18\x9f\x9aA1\xe8b\xfe\xf8\x01W\u030b>\xac. \x84\xc0\xe2\xe2\x12\u039c9u\xe5\x9dw~\xf5\x16\x00\u007f\xf9\x8do|c\xcd}\xe4z1_?~lG(\xe4\x00p\xf7\xddw\u007f\xfc\u0631cY^\x14P\xaa\xe1\x99\x03\x14/\xeaFk\x1c\xad\xb1\xa9*\xf1<\u059dJ\x91I$\x12\xf1\x90\xff\x9d\xf2L\x84P`5\x83J\vmC\xf8\xf2*,au!g\xd4\xc53k^M<\xac\xea\xc7\xda\x0e\x8c\xf5\x01f*:r)>\x04\xf6\x90\x04\xab*/\x94=\x16\r\x93\fm}\xf2<\x9b\u0485\x1a{\xe1I\xec\xa4y\b\u02e7zi\\\xf3\xd9$\x90IM\x12\x13\xc9\xdau\xef\xf6T\x94D\x02qH[\x87\xa3\xb8\xf6\u007f\x8a\xef\x1dy=PJ\xd7\xf34D\xaf8\r\x81\x0f\x15\x86\x9e<\xbf\x04.\xa2\xaa\xf7O\x05\xa7q\x19\xa0!\xc0)\x8a\u0390\x84H\xc0\xc7\xc4\tBc\xac\r\xd9\x16\xc8\xcb\xf38v\xe01\x1c~\xe8\x1e,\xce\x1f\x81.\xfbPY\x1b\x1b\xb6]\x89\x8b\xf7\xbe\x1e\xb3\xdb\xf7A\xc8\f\xba\x1cT\x8c\"F\xa2\xf8\r\x90\xa1\x1bp\x9ab\x00A@gr36\xcc\xed\u00b9\x13\xcf\xc4YG`\xc1\x94e\xa9\x1b\x8d\xe6\x96#G\x8e\xbe\x1d\xc0_\xf6\xfb\xfd\xf5b\xbe~\\X\u01d93'0;\xebl;\u007f\xf0\x83\xfdo\xff\xc4'\xfe\xf9O\x9e=;\x0f!$\x93\x10DB\u0536\xf3\xad\xce\x06\xb4;\x1bk\x8c\x860\x8f\v[k\xf77\xbe\x83\x16\x04\x92@&\tR\x917\xa9fP\u18fd4\xfb\x82>T\u022b\xfd\xfbPGMC\x9d\xf0j\xf8\xbb\x12\x86\xd7d\x93@m\x81I1\xee\u0564\x9bXtd\xf5\x05fp\xe1=kb\xec}T\xb3\xb8A\xa7\xd5\x15?;Q\xb3\xd6 \x13\xa2\xd50\u03ea\x85\x86*\xc9<W\x81\xc8\xe9\xb3\u4d10'\xf8yjW\xe3\xd6\\N|\xd0\xfd+\x14\xd7%\x02Y[\xbd_\xc1\x13%\xb0X\xac\xad-\x85\x9c,0\xd5\x0e\x01\t|bk\x8b\x10\xa7p\xcf(\x9f_\xae\xa3[\xa8\x911\t*k \x93\r\b\xa1\x91\x0f\xce\xe2\xd4\xe1\x838\xf6\xf4\xf71\u007f\xfc\x19\x14\xfd\x15\x10\x11&\xa6\xb7a\xdb%\xd7a\xc7e7\xa339\v\xads\xe8r\xe0\xeeEP\x9c\xcfT\xf7\u02c8\xbc\x19\x87\xe1@@\xa2\xb7|\x16K\xf3\xc7\\T\x9c5\xc3\xef\x8a-\xcb\x12\xa7O\x9f\x19\x03\x80s\xe7\x16\xd61\xf3\xf5\xe3\xc2:B!\a\x80/}\xe9\xcb\x1f=x\xf0P\xa3\xd7\xebCe\x19\xa5!\u0396\x9d\x05\xee\xe4\xc6\x1dh\xb4\xc6at\x19\u0558iWK!7\x11\x95\x9b\xa2R\x84L\x02\x94\t\xf0\xa4t\xe1\x15K\x1al}\xea<\x11J\xc3q\x98:*h\xad2\xac\xad\xeb*k\x1du\xf0\xf7`\xc2\xc8\x14\b\xc2PU\x1dz\x9c\xa1\xa2.\bPd\x80\x16\x81\xdb\x04\xd1\xf79\x98p\xae\xb2a\xb1r\u007f\xc70TQ\xfa\xa2w\x8d]#\xab\x82Fd\xd4\xf1P\xe7L\\\xa3\b\xa6\xb84\xa7.\x88\x9c.\x1a\xd5+\x13\x99$\x82`\x85w\x17\xf41w!e\x87\u0233\x8a\xac\xe3\x82S\xc26J\xef8\xfa\xb9\x87$\xb7\x14\xf2\xaa1U\xfcp\u04cf|\xeb)\u054888\xfb@\u620bK\xbf\x9b#\x01H\t\x91I\x186(V\xcecq\xe1)\xac\x9c{\x16\xa7\x8f=\x8e\x85S\xcfA\xe7}0[t&7an\xd75\u0636\xeb:L\xcf\xee\x01\x84DY\xf6\u076e(\xb2\x96\xc4\xd0\\b\xf5\x9a\"d\x06\x06\xe3\xf8\x81\aq\xee\xe4!\u05cc\xb0M\x82\xcc}<\x9d\x94\x90R\xf8\xef\xc5z1_?.\xac\xe3\xfb\u07ff\x0f\xd7]w\x03\x0e\x1cx\xf2\xda_\xfe\u53fc\xfa\u0529S\x90R0\x91 \"\x17f\xcb\u0582\xb5Fkr\x16\x9bw^\u5de7v\x15\xb2\x11\xe8fQ\xf2.\xdc\x00UJ\x82\x10\x04j\x10\xb8-\x80\x16\x9c\xea\x10\xec.6\xed\xca@\x99fa\"\x8d\xfa\xa9\xa3\xe1\\A\xab5\x98\xb8\xda\xc0s\x95(3t~\xb4\xc6\xf04\x14\xbf\U0003d400\x94\x06R\x02\x18\x03l\a \xc9`\xcd\xe0\x9c<\x06LU\x19\xe3\xba#\xa2\xc3\u05a9V\xc4R\f\xbf\x86\xb1\xc7\xf3I\x02\xd6<\x0f\x9a\x92\xbf\x8b\u007fS\x1b\x84\xa6\x88QB\x89\x1c\xde\xd3\b\x00\x8a\xa2\xd25.\x8b>)\xca\xd6\xc47\x1c9\xe2\xd5\x1e \xb1\xb3M\x9eD\x05\xb3\xd8U0\v\x00_\u0607\x86\xcc\xe4\x8al\xe0}\x93T`\xab\x91\xf7\x97\xd0\xeb\xcec0X\xc4`\xb0\x84\xc1\xca9\xf4\x97Nc\xf9\xec\xb3\xe8.\x9e\x811\x06D\x02\xad\xce\x04f\xf7\\\x8d\x8b\xf7\u0744\xd9\xd9\xcb!\xd1F\xbf\u06c3\u0579\xf3\x9d\x17\"IY\rV\xb7\x15k&\xce\x1a\xbc\nU\xca\f+\x8b\xa7p\xf4\xc0}\xd0\xc5\x00\xaa\xd1r\xf1yBF\x16\x8e\x14\xa4\xb2,\u00e6M\x9bN\x03\xc0\xf4\xf44\xaf\x17\xf3\xf5\xe3\x82:\xae\xbb\xee\x06\x00\xc0\x1f\xfd\xd1g\u007f\xe6\xf4\xe9\xd3[\xbb++Ve\rA\xe1B\v\xc3(v\x91q[.\xfe\x89(|\xa9\xa9@\x13\xa3\xadt\xa8%\x04A\t\xf7\u007fd\xe4\u0094\x05\x01S2F\xac3\x18\x8a\xad\xcb\v5\x95!\xd3\u8ac5V\x99$2\xd7\xfb\xf6\x80\xe9bx\x00J\x18\n\x19N;K\xf7#K\x00\tF&\x18\x92\x18\xdc\x04\xb8I\xee\xfb\x16\xc0\xe3\xe4\u0515\xc6\x0fD}A\xaf\x89z\xbc\x83!%])s\xe54\x99\x92\x13+\xb7\xc6\x04\x13\u786e\x9b\xea\xdd\xf1\b\x14\xaaF+$$\xb3\tA\x90\u0085X\vE\xdew^\xban\u0738\x9d\x86\r\xa6VBx\xa5'\xd5\x17\x87\U0003ace3\x1e\xa6\xf1l\x95\x1a\xb5\x1e$\x1d\x19$\u0273$\x92\x90B\xc5\xfd\x85\xd1\x03,\x9d=\x8c\x85\xb3\x87\xb08\u007f\x18\xfd\x95s(\x06\xcb(\xf2\x15\x94\x83\x15\x98r\xe0\f\xb7T\x06\xa9\x9a\xe8LOc\xf3%\xfb\xb0u\xefO`\xe3\xae=\x18\x9b\xd8\b\xbd8@\xb1\xd0s\u063a\x10u\x1dAt`LX5\xbe\x90W#\x1e\xb7\x8b<s\xf41t\x97\u0382\x84\x84\x14\x02\xa0\u0327\x12\xb9\x1b6[m\xa1\x94Z\xb8\xfa\xea\xab\xef\a\x80\xad[\xb7\xadw\xe6\xeb\u01c5s\xfc\xf5_\xdf#n\xb9\xe5u\x96\x99w\xfd\xfc\xcf\u007f\xe8\xa7O\x9e<\t\xa5\x94!!D4\xd8b\x97\xec.T\x86\xb9\x8b_\x89\x89\x8d\xdb\\|\x96\xf5\x85\x83\xd2B\x85j`\x1a\xb6\xe5\xd2s\x95\xa5\x005\x85\xb7je\xa0)\x80I\x99\x10\xb5\x9d#\x1e\xb3\x830\xb8\x16\xceP\xa1\x9d\xab\x80\xedT9Z\xc3\xdc\xebc\u0151~H~\xa2\x1ay\xe1\xfe\ue534\x90\x12 \x05`\x8c\x00U\xc1%\xd4\x06`\bb\x89A\x85\xb3f\x85\x00\xac\xf5FV\x94\xac0\x89\xf5\xe3*\x1b\x8f!\x8b\x82\u0618\xdb\xcab\x96\xbc\x83`2MD]\xd2?\xf4|\x89jCG\x10AH\x82P\x80Pn!\xa5\xb6\x04\x8dI\xa0g\xc0]\xed^y\xe9\x829X\x00\xd0\x02\xd6$+Qub\xf5\xedP\f\xb3N\a\xae\\-\\\xe1y\v\xd7\xf9\x86\u075c\xd1\x05\x06\xdds8}\xecQ\x9c9\xf6\bV\u039f@\u007fe\x1ey\u007f\t\xd6\x1aW\xf0U\x13*k\xa21\xbe\x01\u0371\x0e\xa6\xb7\\\x84\u037b\xf6b\xd3\xce\xcb0\xb6a3\x1a\xed\x0e\xac1(z]\xf7\x1e5\x04l\xe1\xc3:l}\x98\xbb:\xc9))\xf4\xfe6\xba,0\u007f\xf2\x80\xeb\u02b3\xa6S\x89Z\x1d\xbdc\xb4\xd6\u0632e\x0e\x13\x13\x13\xfbo\xb9\xe5uw\x01\xc0\xa5\x97\xee^\xef\xcc\u05cf\v\xe3`f|\xe8C\x1f \x00\xf8W\xff\xea_\xfe\xe4\x93O>\xb9\xf7\u0739\x054\x1aM\x05\x12\xae\n\x03Q\xbe\u0759\u070c=\xaf|;\xa4j\xa2,\xfa5\x17\xabp\u0344n>:\x0f\n\x17\xc5\x05\"\xa0\xe1\xbejT\xb4\t\xe5\xfc7\x16\xb5\xdf\x0232\xaf.\x8c\xb8\xf7*`\xbc\x8e=\xd7\vI\xa0\xb2\r\x03\x19\xcf\x17\x0e]\xdd5\x03P\xc4P~\xe0\xc9m\x80[\x89\"2\xc0\x1b\x1dw\xfe\xbc\b\xd8\x02\x10R\xb8\x8a\ue855\xe8\xefB\xe9yP\x05]$\xbcm\xa4\x8cE\xae\xf4\xa7\x18\x82\xd6\u04c4{\f\xdd\xf5j\xa6\x8a{>R\x02J\x12\xa0\xdc\xc2$[\x12bB\x82\x1b.\x9c\xc32\x83z\xda{\x98\x93\xe3\xfb\x03\xd0\xcc~qJ^_\x1b\x98\xf2\xc2u\xc1CR\\w[\x0fn\xb1\x05\t\xef\x03n\f\x06\xbdE\xe4\xfd%t\x97N\xe2\u0729\xa7\xb1p\xea),\x9d;\x86\xbc\xbf\x04!$\x9a\xedILo\xbe\x04Yk\x02\x8d\xf68:\u04db0>\xbb\x19\xe3s[0\xb1i\x0e\xad\x89\rh\xb4\u0690B\xc1\f\n\xe8\xfe\xc0\xc1t\x1e\u02b3\u02a9\x88a*\xb7E\x11\xb8\xae\t\xf54\x8d\x90\x8b\x88\x94\x90(\xfb]\fV\x16\x9c\xddB\xd6\xf0\xe2(\x1bEf\u030c\x99\x99Mx\xfd\xeb_\xf7\x9d/\u007f\xf9\xcf\x00\x00\xb7\xde\xfa\x86\xf5\xce|\xfd\xb80\x8e\xaf~\xf5\xcf\xc4\xe7>\xf7\x05\xc3\xcc3\x1f\xf8\xc0\xed?\xfb\xdcs\x87!\xa5t\xad\x91\xc7\n\x9dO\x87\x01\x83\xb1\xf3\xf2\xd7b\xeb\xaeWE'\xbf:\xd4\xe1\xbaRA\xd2\x0fL\xfd\xc6\xdb\xc3+\x9cB,i?#\\A\x87\xf7\u0486e\b\x16\xc8\x04P\x94\xb6\x8a.C\xbd\v\xac\xb3X\x86\v\xf6\xa8\xf8\x9e\x14?\xe6\u06b6\xbb\x86\xf9\x13 \x84\xf7\x0fo\x00\u0726J\xab\x13 \x8fP\x14\xc6\x00a\b\xb4L\b\xc4\x1e\x04\x9ae-\x0e\x82W[\x9b\xd30F>\xb4 \xd10\xb0N5f\b\rC\xed\xb12U\xab\x9f 89z\x80X\x1a\x02\xd4\x11\xee}\xb0\x8efI\x1d\xe9\x1ds\r\xc0\x06V\xfb\x9cSE\xd0\xc6\x11:\"\x84\"$B\xb5w6\xb4aA\xb2\xa8\xf3\x87\b\xa4$L\x99cq\xfe9,\x9e;\x823\xc7\x1e\xc1\xe2\xd9gQ\f\x96\xa1\x8b>\x88\b\xad\xb1\r\x98\x9e\u074d\x89\r\xdb19s\x11&gw\xa1=\xb5\t\xcd\xf16\xb2\x8e\x82\x1c\xcf \x9a\xca-\x1a\xd6\xc2\xda\x12\xe5\xa0\v\u03ad\xe3\xbb3`u\x12&\xad\x84O|\xaaXU\x11+G\x02\xbf\x90\x83\xc6\u020b\x94(\u0495\xfc\x1cDkg\xfef\xc3\x02l\xd1\xe9\x8ca\u02d6-\xa7?\xf2\x91_\xb9\xeb\x1f\xff\u33fdd\xaf\xf9\xf5b\xfe\xbf\xe8\xf1\xd0C\x0f)\x00\xff\x1f{o\x1ak\xd9u\x9d\x89}k\xed}\u03bdo\xac\xc7*\xb2XU,\x92\")Q\xd4\u040eE[\x92GIv \xc5v\xcb\xee\x8e\xed\xb4\xe1\xb8\xe18v\x8c\xfe\xd5\b\x108H#\x06\xd2H\xfe\x04n\xa3\xff\x04H\x1b\xe8\xd8\b:A#\xed\x8ch\xb8\xd1vK\xeeA\xb2-Y\x94<\xc9\x16\xe7\xa9X\xa48W\xd5\x1b\xefp\xce\xde+?\xd6\x1e\xcf=\xaf\xa8?,J\uaec1G\xbeW\xf5\xea\xbd\xfb\xce;g\xed\xb5\xbf\xf5\r\xcb\xdf\xfc\xcd\u007f\xfc\xd1\x1b7n|\xea\u06b5kh\xdb\t\xeb\x8d\u0369\xb8\x88w\xd8\u07bb\x80\xf7~\xf7\xdfD;\xdd\xc1r~\x98\vbY\b\x99\xf5\x81\x8f\x10\v)F\xab\xc5\x04\x90\x86\xebf5V\"\x03\u040e\x85\xf49\x1f\xd4B\xedG\xbb\x90\xbc^\x0e\xcfV(\x84i\xf0\x9a\a\x82\xe5\xc00\xe7\xc1Q\xfd\xba\x87\xfe'\x00\x98\x04lD\xef\xfa\xa9n@\xa7A#D\x00m\t\f\x04r\f\xf4\xc1\x00\x8a\xca\xd3\x01\xc9\n\xf7;\xa6\x1d\t\x95HF1<-R\x84ra\xafs8+\x9c\xbc\n\x80\xce\xc1\xc4\xd62\xd8\xe8\x06\u028d\xc2+\x98h\x91J\xa7\x83P\xd0\x01\xc5\xcf)\xf8\x92\x13ge\xae\x16\xbe\x12_\x96\x14\f\xa1\xd7Y\v\xa5\xf8 \x02b\x1df\xbev\xf5/\xf0\xf8\x9f\xfc\xbf8\xb8\xfeu\xb8~\x01\x88\xc7ts\x0f\xb7\xdf\xf5~\xdc~\xe1!\uc77f\x0f\xbbg\xef\xc6\xc6\xf69e\xdb@@-\xd0l2\xcc\x16\x03\r }\xa7\x18}\xe2\x9dS\x86\x92\xa2A\x1a\x91\xdaB0\x81\xac(\x05\xb1\x18\xb2f\xedZ\x91\x10U\xda 3a\xba\xb1\x8d\xe9\xe6^\xf0\xac\xe9\xe1\\p\xe7'\x86x`\xef\xcc\x19\xdc}\xf7\xe5\xafN\xa7[\x8f\xe8I\xf6\u007f\xc0\xaf\xfe\xea\u007f\xb7.\xe6\xeb\xf5\u03af\xaf|\xe5\x11|\xf7w\u007fd\t\x00_\xfe\xf2W>\xf1\xf4\x99\xea\x13\xc8\x00\x00 \x00IDAT3\u03c0\x98\x05\x14x\xe5D\xea\x9c\xe7\x1d\x98\x19\xef\xfb\xf0O\xe2\xe2}\x0f\xa3\xeffy\x00V\x15r\xed\xdc\xc8T\x92C\x18\x0e\u0130\x86A\rUP+S\xd1\xcd6\x04:cUH\x14\xe4\xe2\x16\x04\x0f\xc0\xf5\xa8\a\xa2\xc3\xc6{4_ndh\x1a\xe5\xf9\x05\x8e-\xf5\xe1B)\u0261\x90\u01ee|\x85\xdaX82\x1a\x06h[1\x12\xdaw\xe8\x8d\xc2\x11zB/B\x1a\u00a0\x14\xa1\b\r\x1b\xf6\x8c\x9bS\xfawCX%\xe7\xaaJ\xadlM\x15?\x98\u007f\u015f\x83\x94\x92H\xb1\x90o\xf0hl\xaaX\x00\x9b\f8\x06y\x17NC\xf1D\xe5\xd5\xe1WrPC\xe5\x99P\x9c\x1c\x88c\xf8\a\xc1\v\xe0\x9c\x83\xb1\x13\xdcv\xc7}\xd8\u0739\x03\x1b[g\xb1w\xfb}\xb8\xfd\xd2C\xd8\u07bb\xa0\x9f\xe7z8qp\xbd\a,\xd0Z\xa3\x8c'\x13\xbdd2K\x88\r\xeb}\xd4\v\xa4\xf3\x05\xaf\x9e5\xb8\"\xa41I\f:\x19\xccLjQ\x1b\u00acD?\xb6\xed\x14g\xef\xbc\x1f/>\xf3\b\xfa~\xa1\x0e\x8c\xc6(\x13\xcb\x1ax\x11\x18c>\x1f\xbf\xde\x0f\xfd\xd0\x0f\xad;\xf3\xf5\xfa\xe6X_\xfc\xe2\x17\t\x80<\xfa\xe8\xd7>\xf8\x0f\xfe\xc1\xaf\xfd\x9d\x97^|\t\xd64\x94\xbc\x9d\x83 \xa4\xef\xe6\xb8\xe7\xa1\xef\xc7C\x1f\xf9\x8fam\xabX\xf9\x00\xd0 \"\x80M\xa2qA\x02\x9b\u00c4\x87\xc5\x10\xa4e\x90\xd1b\xee\x11\xc2p8\x1c\x00$\xccD\xa7\f9cA\u0483\xe6\xc5@\x14\x8a\xdf\xc2K\xee\xfa\v||\x05J\x11\x19M\x9a\x1b2\u05c7\xaaO\"e\xaf\xc0@\xa9\x93v\xc0F\x17\xacl\x1a\x04Q!\u0536\x9ao\x91\xf3\xe8\xbd\xd2\"\xc5\xc5\xf4$\xaa\x10\x15B\xe9\xa8(\x15B2\xf4a_\xd9\xc0bT\x1c\n\x91\x10\xd5\x1b\x9b\tTP\nb-LI\x8b5\u05db_4\x10\x13\xaf\xbf\x00\xda4\xba\x19\xf4\xda\xf12\v`\u0507\xdd;$.}}\xa4 T\xd9\x12\x1e\x1aDa\f.\xdc\xfb\x9d\xd8\u067b\b\xb6\r\xa6\x9bg\xd0Nwa\x9b\t\xbc\xeb\xd1-\xb3\xa0\u01c7\x13L\xd32\u0314AS\xbdW\xe0\xc3=\x02B\xa1A\x03&\x16\xd2\xf5jY\x1c\x94O\x86\xd4A\xd1#\xde\\a\x04\x1cB\xc9K\x83\xaf\x94PT\x84\x8e\x13[\x9c\xbf\xfb\x03h'[X\xce\x0f\xe1\xd9\x06\x91\x11\x81\x8d\x85\xf7\x82\x83\x83\x83\x97\xe2u\xfe\xbe\xef\xfb\xc1u1_\xafo\x8e\xf5w\xff\xee\u007f)\x00\xf0{\xbf\xf7{\xdf\xfb\xd8c\x8f\xefz/`c\x82%\xa9\x16\n\xd7-\xb0\xb1s\x0e\x1f\xf8\xfe\x9f\u0159\xdb\xefA7;JY\xa0\x89\xb1\x1d:\x1b\xf5\x9b6\xe9A\xf7&C,b\t\x98P1\xac\x8bc\xb4\xc21/\u05ba-\xa3\xb8\xb9\xd3\x02\xce\x00\x1aa,{\x9f\xf5\x9c\xd1#DFb\xd9N\xe9\xcaSP\u0100\xa0Q\xd6H\x06\x94\xb52!\u0224TK\x16\xa2\xf3\x01\xa1\xa6\x82+\xb6\t\xa6S\x0fqu\xc7\xd59\x82/\xbdfB\x11A\xc1\xd7N\xe3N\xaa\x1b\xffj\xc7\xc9f39^u\xf8\x93\x87\xc2jX\x8b\x1f\x19\x807\x8c\xce$\xa2\xc7L\x04g\x06\xf2\u007f\x91\x00-mi\x87\x8ec\ar\u0280i\xc0\xf0,\xf0NB\xfe\x85$oxfJz\x83d\xe4\x15\xbe^;\xddQ\xa5\xb0\bDz\x888t\x8b\xe3\xe2\x17NI9k[\x86\x9d2h\x83A\x96C\xe0rqc\x14\u061c\x18\x81\x01\x01\xc7\x0e\xe8B\x188\xeb\xafN\x85gT\x18\x8a\xd1\n\x8bI7n^\xb1\xcc\u07fe\xed\x12\xb6\xf7.\xe0\xe8\xc6\u02da\x9c\x14EV\xe1\xb3\x1ak7\xbe\u055f\xfb\xb5\x05\xee\xb7\xd9\xfa\x97\xff\xf2_\x84\x1a$;W\xae\\\xf9/\x9ey\xe6Y\xbd\xb9\xd9\x14\x93\u007f\xf5\xa5x\xe0\xe1\x1f\xc1\xbb>\xf0\t\xb8\xe5|0\x95C\xe2iG\u0447\xb2XD\xbd\xa8\r\xa5\xc1\x1b5\x19{.\xc2\xe5*\xa8\xa1\u00a1\xb7-h\u01c0\xac&\xd3Y\xab\xf8\xafX\x827j\a\x10\x1d\nW:p\xa9;\xefa\xd7.9]l\xd0\xd5\v\x98Dq\xda-e\u007f$8Z\xe4-\x1d8\b\x00M\rh\x87aZ\x85_\x8c\x91\xa0\x9f\na\n\xac4F\x89S\xc9\xf25\x0f\x8d\xbeb\xa8\x03\r\xaf7F\x15\xf1\xf1\xd3\f\x87!3\x11\xa8a\u040e\x05\xb5\\\\\u3890\x17j\u0304E\xb7\f\u07b1\xa0M\x03\xb2a\x98\xc9\xfa\xc6Q\xf8\x15m\x84\x933\x1a\xd5v\xbe\x01\xaf\x12\xf1\xe8\x963\xf4}Td\x02d\x03\x14\x17\x18NB\x80i\x18\u0366\x85\u0659\xc0n4`\u00ea\x06\xb5\xe1\xcdpbIqp3\xa4\xa9\x05m5@\xcb\bLX4\x86\xd0X}\x9d\xe9\x0f\x83N\xa2\xcc\xe9L\x14ZP\x8e\xad\x13\x81\xb5-vo\xbf\f\xa2`\xc2\x15\xec+z\ufc71\xb9\x81\xcbw_\xfe\xd1\xf85\x9ex\xe2\xd1ug\xbe^\xef\xfc\xfa\xb1\x1f\xfb\xb4\x00\xc0o\xfc\xc6\xff\xfc\x1d\x8f>\xfa\xe8G\xe6\xf3\x85\xe2\u0764Gj\x88G\xbf\x9c\xe3\xf6w}\x10\xef\xff\xf8\u007f\x8a\xb6\xdd\xc2\xe2x?\xc9\xfa3\x9dC\x1f\x06f\vf\x9b;\xd8\u062d\x87B -\xd7\xee}A\x15\xba\x02\x8f\xc4V\xd9\x00\xb4\u06c0;\x81\x1c\xf5\x00\x01\r\x03\xbd#t^s;\x99bp\xef@\x918\xa8v\xa9\v.\xc4D\xa5\xadJzM\xa4\\k\x9a\x12dR@\"e;\xef\a5\xbdpK\x04\xd4\bJ6\f\xa4\xf3`q\xc0\x12\x89\xfb\xdd\u01df\xcd\xc7\xe0\x03_\xbd\xbeX\\\x93;a9\xa0\xa3\u06b7\x86V\xc0{\xfd{f\xe8\xc0\x93\x01\xb2\x04l[`\x83\xf3\xcc`\xe0\u03d2,y9t\xdaqc\xdd\b\xf7\x02\x11p\u0483:\xc0\x04\xa1\x97x\x01;\xc0\xc5{\x05\xc55\xaa\xc2@\x94\x11\x14y\xde\xe5\xe7\x88d\aCb\xc0n2\xecn\x03\u07b6\x01v\xf3\xe9s*\x9c;\xfe\x9c\xf1\xf7\xb8\xc1)@\x9c;\xbdn\r1:\x84\xccQ\xcf\xf5\xb6^\xbc\xbe\xa4\x04-~\x99l,6\xb7\xcf\xc6<\xbb\xf0m\x19\x86\x8d,\x17Kz\xfe\xf9+\xf7\xc5\xcf~\xef{\u07ff.\xe6\xeb\xf5\u0373n\xdc\xd8\xff\xf9g\x9f}\x0e\xbd\xf3`\x13\x8a\xb1wp\xdd\x12\xa6\x9d\xe0\x81\xef\xf94\xce\xdf\xf3\xd7\xd0]?\xc8~\xe65X\x1e:&[\x17\xbd\xb2\xe0\x9b\u0615S\xf2<bR\x15b\xb6?\x1f\xd0\x15=@-\x01{\x8d\xd2\x15O\x1c\x98\b\xad\x12\xe8\xe0\xbcv\xd1e\x1aN\xe9\x12\xb8\x12\xe1F\x85\xd0\x05\xb9(\u050dd\xe8\xca7\x90\xbar\xfd\x11\xe4\xf4\xa6|$\u308d\x81l:H\xef5W\x14\bC<\xc0\xa5\xc0k\x19\n+Q\x99\x98\xa4bB#\x93WZIgC(\x8a&\xce(X\a\x9e\xbcm\U000b597cAD\xf5\xa3G\xbd\xd1\xe5\u0670\x80\xa6\x06\x1c\n6\xc1\xab3d:-\x10\xc8\x01\xceyxY}\x99\xb5\x0fM\xf8\x1e\x1c\xac\x94\xa3\x8bd\x80?LK\xb0\x9b\x16\xbci\xc1\x8d\t\nS\x14\x02\xab\xbc\x19\x94\x9e\xfa\xf1g\xe2\x8d\xd0\xe1\x1f\xf7\x9a{\xea\x152\xf7\xac\xdf+\r:#C(\xb8v\x0e\xdc\xdb\xe1\xc5\u00d0A;\xdd\x0e\f\x16\x97\xe6\n\xc6Z\xda?8\xc0\vW\xae\x9cy\xe3\xcdW\x1f\xbe\xfd\u071d\u007f\xba\x86Y\xd6\xeb\x1d_\x9f\xfd\ucfca\x0f4?\xf9\xd4S?x||\x92:l\x1f:\xa2\xbe[\u0dbb\x1f\u0103\xdf\xf3\x13\xc0\xa2\xd3\x1b;\x03\xc4\x05\x19$\x14r6\x15\xd0+\xa1\xa0p\x80Yb\x8a=\r\xa8e\xc3X\xb8\x12;\x87\x00<e\xd0\x19\x85\t@j\t\u0432\xf2\xa7}e#@5\x94]\x92-\xcaa(Q\xc6\u024bM\x89\x10\xe0\x90 j*_S\x15\xeaSvx\xa7\xe4\x93\x121\xc8\x1a\u015e7\xf4\xe9a\x12X\xf2`)\xfc\u06c7P\t\xa12,+\x1f;\x19\xc0\x03\xe5&\x15\x83\x1b\f\xe9|\x81\b\xe0\x96@;\x16hy\x00-d\x8c\u0757f_e\x88\x04EN6@\x13\x06\xef\xb4\xc0\x94\x03\xe4\x15\u0330\xc2\xef\xd6\x04\bD\x8f\x04\xfa\x96r;cV]\x80\xef\x925\x04\xe7\x8fMc`7,x\xab\x01\xb56)\x87\x13\u0155\xb9\xc6\u0283\xc3c\xfa\xb9)@0\x1b\x06\xb4\u0640\xad\x81\xb1\f6\n\xc7\xc4\xe8\xb9\xe1p<\x8a\x9aT\fW\xd8\xf7\x82`L\x9bN\xa0\x89E\xc3\x06\xcbE\x87\xe3\xe3\xe3\xf3\x9f\xfd\xccg?\xb2\xc6\xcc\xd7\xeb\x9bb}\xf2\x93\x9a]\xf8\x97_\xfd\xb3w]y\xfe\xf9w-\x96]!\xae\x10\xb8\xbe\x83i'\xb8\xe7\xe1\x1f\u0199\xdb.C\x16\xf3\xccb\x18\x14\x126\x06ll]d\x8abI\x1c\xba\\\xa3\x85\x86\x8b\xbc\x8a\x8cs\x94\x82}AM\xd2\x06h\u04c2\xb6\x8d\xb22\x88\x14\x17\r\"$g\x02k\x86\x8atw\u0501\xd0BE\xcd\x1a\xc3\xd0\xe3\xa7F\x06KS\x1f0\xe4\xe6)n\xa3x\xbd\x16t\x026\t4\tu\x8e\x04\x86\xfc\u02b06a\xe2\x05\x9c\xa0X\xb2\xa4\xacO\x12*\xb0qZ\x11Gq\xf4^\x89FZ\x9bV\a\x89\xf1$P\xfdz\x02C#}\xa5\xe8\xac\x18_{\x1e\xae\x82\x00\xda`\xf0\x96\x05\xb5F\xf1k\u03b3\x106\x14\xe0\x97\xf0\xcb5\x8c\xf4BH\v*\aK\x88\x14\xb9\xcc\f2\x06\xc6\x1a4\x13\v\xb3\xd9\xc0l\xd80|\xcf\u007f\x1fo\x16b\x0e\xf1r\\\x17\xf1P\xe8%Z\x18l\x06\x8b\x82\xf0\x12\xac\r\xc1D\x91\xdaH\xa5\x1dqpk\x1c\xf0\xf9!\x9a\x0f\x1a\xff4\xde*\u030c\xdey\xf4\xde7\xcf?\xff\xc2G\xbf\x95\x9f\xff5\xcc\xf2m\xb8\xfe\xe8\v_\xfc\xfe\x97_~e\xbaXh\x8a\x8a\x84\xecH\xd7/\xb1s\xe12\xee\xfd\xceO\x80\xe6\x0e\u03b9\xcawE\x12\xa7<;\xca\r\x1a\xdd\x02/\x0f\xbc`\x1at\xddT\x97RZ\xa9\x94\x89\x12\x016\xa4\x96\xb9\x9d@\x8e<X\x80\u01aa\x19T\x1fC!\xdcX \xe80`\xb9\x00*\xa8B\x8a\xc0\x10P+\xca+\u720d\x13\"\x1a$\xa5\x0f\x8a\xd4\x0e\xb6+\xdf\x0eH\x91c\xd2x\xc8V\x10=-\xa0\xb4GR\xb8\xa58\xe8h\xd5q\x92\x82\x84\x13\xb52\xa1S2j\xa1\xab\xff4\xe6f\x86a\xf3\u0504\u036f~q\xa5\x97x4\xc0*\x8d\u0232QV\xe8\xe0#\x0ee\x00\u06b2\x80\x10\xfc\x89\x03z\u0162M\xf4<w:I\ue4f5{\xb4\xfd\x95!\x8b?\xfd<\x86\x01\u06f0Z\vl\xea\u026b\xa2\x8d\x06!R\u029a\xa5l\xa2V\u0307Wn\x19\xda \x88c\xb0\xf8`\xf5\v8O\x9aGZ\xde\x04!l\x9c\x06\x9e\xc7\"\x1e\xce\xf5\xa9\u0448\xb4Q\x02\x81\r\xcb|>\xa7g\x9ey\xfa\uce98\xaf\xd77\xd5z\u9957\xbe\u007f\xd9\xf5\u4703\xb5MrA\x04\x11v/\xbc\v{g\xee\x06\xf5.p\xbb\aC+\x10\x88,\x88l\xfd0\x84NX\x8a\x937l\u044d\x97\x14\xc1\xdac0\xb3\"D\x8a\xce=\xc0\x11\xad\x01\x9d\x01\xc8\xf5\xc0\xdc\xc10\u0436\x04\xdf\t\x9c\v\xc5\xcc\xc5H\xb3REIE\x1d\x91\xaa\xb0\u01d7\u0361+\x97i\x80YJ\xba\xa3\xc4\x01\xe5i\x8d\xb8r\x9b\x87S\xd1\x14\xceA\x81\x92\xe9(\xe1\xb9`\t\\\xec\x02\xad\xa6\\\xd0\v)j5\xac\xab$\xb0\x85\xf0\x89)\x0e\x94\xa1*\xcf-\x03\x9a\x98\xf1}\xad\x84\x8f\n\xd7\xc0\xe0\xbb0\x88p\xe3\xfc\xc9\r\x01;z\xba\x92yNZ\x82S\n)\\\xf4r\xa7`\xc7 u\xb5\xcd\\J\x18#\xb0\x96\xc0m(\xe4\x1bF\xf5\b%\x9eE\u0671\xb1\xe2\xe1\x17\x1b\xbfO\x03J\xaa\u007f\x86\xcd\xf0\xfe\u072b\xc8\xc8\n\xd0{8W'\x1f\u9f55sGc\xb7\x1e=g\xe25\x97p\x0fXki\xff\xc6\r\\\xbfv\xfd\x9c\x88\x9c!\xa2\xfd\x17_\xbc\x82\u02d7\xef]\xc3,\xebu\xeb\xd7s\xcf=\x93\xde\u007f\xf5\xd5\u05fe\xcby\xa78y\xf4\xb6\xf0\x0ev2\u0145{\xbf\x03\x1b\xb2\tY\xf6%\x18\x8c\xa8\xf2ccal\x9b\x02(\u02beZ\xe2CK\x81Qa9w\x81\xc3\xe69=\x9a\x94\\\xaeJL=w`\x02l\x18p\xc0\u03c95~nbT\xae\xee,\xc17\x04_\bKd\xb8I\f\x11\x96\x88Q\x13@-TTc\xb0\x12G\x97\xbc\xba\xc3?\x12\x1a\xd6\xc6\xf1b\x1f\xad\x83\x01(\xa3d\x8b\x14w6\x04k\x04\xcc\xd9\xd8K\xaa\xe8\xf9A\xcb_2:\x06\x17\x90\x89`\x8cZ\xb4\x92e`\xd3\xd4\xe2 \xe44\xfb\f\xe7 \xd1\xf1rVk\xf8\x1a\xc4a\xf3\xaa\xbf?\x91\u0489h\xbb\x01O\x03\xb5\xd00\xb810\xad\x01O\x14\xff&\x13\xb2D9JP9\xd3\x14I4\xe8\x83\x19l\rx\x83A\x9b\xac\xf3\x14\x19$TQ~\xbf|\x8b\x94\xc5\xfcu\x91\x86\xa2\x11\u07e6hO0Q\xe1\x11\xd9p3F(\xa8\xbc\xc4\x15\xa1\x8a\xaa\xa0\xf1\xca\xd0\rj\xc6utt\fb\xbe\x0f\xc0\x87\x00\xe0\xf5\xd7\u07e05f\xbe^\xef\u023a\xef\xbe\abW\xb6\xf7\xc2\vW\u03dc\x1c\x9f\x80\xd9$:\x9c\x17\x8fv\xba\x85\xdb\xefx\x00\u0187\x89>\xd5\xdcp\xf5\xe0j@l\u01cbL\xc0\xaf\x99\xa2\xb1\xd6*\xfb\xf0\xadJ!\xd5\xe54Uu\xda2\xcaA\xb7Z\x9ct \xaa/\xc3\xd9\x10\x18] \u00eb\x1b\x87\xac\xf6\xd6F@\x93\x80\x95\xaf\xee3yc\x10\xacJ\xe8k\u06eb\xb1\v\x11pb@6\t\x98\x86\x82\xde\x00l%3z\xa2\xb3\xdf\n!>^\x02Z\xb9*\x89SN\x01^\x99\x04\u0738\xc9hx\xe5\xea\x1d\x8bc(\xe4\x1c\xb0i-\x949\xc0\x99\xaa\x01c$4\x86\xdf\xfd4\\\xff\x86\x03\xb4\x13\xe06k\x14S7&\xa8\x81\x8b\x90\x0f\xa1\x84n\x18CZ\x90\x1bV\\\u007fb\xd2\xd03\xfb\xa5P5\xac\x15p\xa0\xbf\x9a\xb4A\x12\x13L\x84\xb2b\xfaR\xe9\xb7b\t\xb4\x11 >\xa3\x9b\xbcg\xe49Da\xc2\xe5\v\x8fx\xf5{\xf1\x15\xbb2\x0f\xe7\x19^\x04'''\xf4\xf5\xaf_em\x88^]\xc3,\xeb\xf5\u03ae\xc7\x1e\xfb\u068f\xccf\xb3\x8bG\x87G`2\x15M\xae\x99la{\xe7\x02\x98\fzY\x1d\xd61\x99@c\xc4j\xe4\x19B\xdeg\x1c~6\x14\x98\rC|\x93*\xc9=Um8*\v\xaf\xe8\xf3\xa1\xd1g\x02\xda5 \xe7A\a\x02t\x82V\b\x8e\x80\xa5W\u0699\x0f\x18xY\u0487q\xc9\t\x8ae\xa8\xa9V\x18|\u0280\x8e<\nU\u0230\u04e7U*\xe4\xe04\x03\x0f\xc0\x00\xb2\x05\x90\xf3\x80\a\x8c\xd5\xef\u0457\xedy\x84#\xdc\xc0\x18jU+\x14\nr\xf0'o\t\xbc\xd5@6m\xf2G\xc9\xf8u\x86L\xa8\x18\xb4f\xd8K9\xe6\xc3@'\xa9\xcfMH\x86/\x9b:\x90\x95\xc3^\xed\n\x90\x19C\u0300'V\x8f\x14\x93\xbd\xcdI\bM\fy0\xca\xc5\xf7\x1bV9\xf1\xc1\xdd\x11)\x12/\x02!A#\xccT\xec\u0152](\x83\t\"\x93\xfa\xf8\x88\x1f\xd0=\x1b\x06m\b\xfa\xce\u00c5\xfb2\xfd\xb6$x\xd8H\u212aj\x19\x9a\u16b7NN\xf7\xae\u0386\x18\xf3\xf9\xdc_\xbf~\xc3\x01\xc0\xfe\xfe\xfe\xba\x98\xaf\xd7;\xbb>\xf3\x99\xcfto\xbc\xf9\x86O\xe6LI\xfc\xc1\x98n\xecbs\xfb\x1c\x04\x94p\xc9\u071eS\u8fb8\xe0\xe9\x16\x85<(\xfa\x88C\x10\x82\xe5\x04\xfd\xd2J\xdfM+\x01\xbe\xa9#\xa5<\x1a\x95\xd2\xdeV\xb4H\u040e\x05\xf7\x02\u007f\xd4\x03 L\x04\xf0^\x14r\x8e\xaf!\x16\x04\xa2\x15\xe7\xd9\n/\xb7\x9a\"T>\xd72R\x98\xa5\b!\x93A\a\x1f\xf3\x96\xb8\xf4&g\x02\xb3\x81\xa7\x10\xec,\xba\xb9\xc9V\x18v\xf6\x801\x1e\xde3\\\fS\xe0\\\xf8U7_\b\x8a\x90\xf3,\x05\x019\xe0\xe0\x86\xb8\x99;f\n\x1bB\x19\ueb1b'UB\x19*\x87\x9c\x83\xbc\xeb\xf2\xf7J$U:\x121\x03\xdb\x04\x11\x86\x9c8\xf8N@^\a\u04d0\u0e98X+jUkC\xd2\x14\f\x01S\x03\xbf\xd3\xc0Y5\u01f2\u0308\xe1@\xbe\b\xf3H\xf4H\xa2\xc2D,w\xe1\xe2)Q \xc5\xfb\xc2q2of\xae!,\x1a\x86\xef\x04\x86\x00\x98`\xb3\xdc\xfb\x94\xad\x9a\xad\x90\x87\x16\x00\xf1\u0102\x90\x96\xc5pT\xf8\xd9\u007f\x8b\xaeu1\xff6[\u05ee]\xbb\xec\x9d\xdfr\xce)%,\x0e\xc4\xd8`\xeb\xccyL&\u06c5\xaa\xa6\x18>E\xc9?h\f3Q8\x81\x00K\x8aU\x92-\xfcP\xca\u007fB\x92\x1c\x04W\x02%GQ\xc8:\x81\x81&\x04\u0675\xa0Nt \ua056\t\v\tG\xe4\xb1/]\xd2\x16C\x81#\x16\xd0\x14\xc0d\xe0\x0f.uC\x9e\xde$\xabH#\xc9\xd0\b%\x9c\x99\xc8\xe7\xa86 ][\xefz\xc0\xa9<\x1c\x13\x026\x058Vq\x8b1\x02/\x9c\xaf\x9f\xc4\r\x82s\u0403\x97\xca\x13\x87)@\x16-\xc1l\x1a`\xa7Qs\xaa\xb2\xe8\xca`JP2\x8a\n+\x81X\u0529\xa2\xeaQ\xb1\u007fsm\xbb@\xba)\xd1n\xf8\x1d\xfa\x1e\x0e\xac!\xd7>\xc0\"\xb1\u007f&\x85UR\xfek\xc3\xc0N\x03lZ,\xc2\xe7\x9b \xed\xd7Z+\xd9Q2\xfe\"\x88rP4\xfb\xc0Z\xa5\xa0\fR2M2{C\xc1Z\n\f\x16\xde`\x18\x11\xd0\"\xdc\x1b\xbe\xb8\a\x92\xd9X\x0e\xa2\x06e(\x87\x8a\x98\xb9H\x87d\xc3h\x1b\xc5\u4b35\xebb\xbe^\xef\xecj\xdb\xf6\xfb\u06b65}\u05e15&\r\x19\xd9\x18\xec\xec]\x80m\xa6)\x99\xbc\xc4Q#\x15QF\xea,R\xd2{\xe8\x1a\x83\x8fv\xd5\xecq\x8d\a\x8fR\xd7V\xb6\x8a\xf0\xa0E\xf5_(8\xbc\xc1\xf0g\xacv\xb2\v\x0fk\xd4\u072b\x8b\xec\x12\xa6D\x8e`PEnI\xf4?\xab\x99\x9ed\u01af\x93\x14 \x83T\xddyP\xe6\x83 \xa1S\xa60\xc43\xe5\xcfE\x04\xb6\x16d\x18\xd2;x\xd7\xc3;\xa71t^\x80c\x82a\x817\x02\xe79\f\xf1\xd43\xc0s\xe0f{\x0fr\xb1#\u0542n\r\xa3i\x18\xbc\xc1\xe03\x16\xbc\xdb\xe8\xbc \x9ap\x15\xfcI*v\u04c8E3\xd5\x13\t\x891uq\x00(\x12h\xdeC9g\xf1\xab\x9a\x10d\x97!\x0e\xe0\x99\x03\xbc\x0f8|d\xbaxx\xca\xcc\x1e\xa5MZ\xf0N\v\xb1\xa4\x8c\x17P\x10\xf6Pm\x89<<\xaf\x89@\\\x1f\xd8'\x94n&a\xc9x\u007fiw+y\xd87\x99\x90n6\u0783:=\xec\xa4\xcfd^\xb1\x1b\x8a\xf1v\xe5\xecE@\xc9\x17\x06\x02\xf4\xce\xe9&\xed\xfd\xba\x98\xaf\xd7;\xbb\xdex\u336d\xae[\xe6\x1b?\xdd\xd0\x06\x1b;\xe7\xc0\xa6I7j%V\xa1<8\x1a>\xdc \xc03\xa5\x00g\xb2\x94\n0\x15\xc1\xc9\x15\xd7;\xcd6\x87A\xc0\xa3\x8ft\xe6\x1b\u01c7u\xdb\xc0\xf7\x1e\xf0\x1e\xa6#\xb4\x81M\xd1G\xfc4\f\xf1\xca\xf9\xa1\x14\xf8\x88L\x83|\x1f\u046av\xc0,\t\x0f\xb3:\x05ReEK\x10\x98\xd0\xfe\u01df'\b\xdf\xf5H_\x15\xd40\xf43\f\xea\x1c\xbc\xef![N\xa9}3\r\xb7p\x88~\xe7\x04\x11\x82k\x00!F\xb3\xa0\xe4?\xa3\xc3F\x86\xb5\f;50\xb75\xa0\xbdF;^\x19\x04\x8aV]y\x8e\xf5\xe3\xf0;\xf4\xa21\xda\x10\xc0;\x1f\x92|\xbcF\xa5En7\x97G\x15Z\x9ddo\x1a\x18a\x00K\u0639\x83a\r\x87\x96^\u0099\xc2k\xd7m\x83\xe9\xd7n\x03\x99X\xb0al\x98\xb0\xf9\x15\xc3Wp6\xf2RA\x1aC\x9c\x87\xeb:\xf4\v\xc0K\x9f|\xed\x01\xa3\xd4W\xf6\xf0\xec\x01'up\x93\xa4\xfd\x1c\xd2\x12\xfc\x86\x81\xb8\x1eB:\fe\x9fKu\x05c\x19S\xb8K\x04\x8fv\x11\x90\xa8+\xe8b\xb9\xc4k\xaf\xbd\x06\x00\xb8~\xfd\xfa\xba\x98\xaf\xd7;\xbb\xae\\y\xa1\x9b\xcd\xe6 c\xb2\x98\"\x14\x8b\xe9\xf6m0\x8dM\x8eq\ts-\x1b\xe3T\xa4\x8b\xee\x95\x011*\b\xe1\xe8\xc7\xc2\xf5\\\x8f\xa2\x87t,:e2\x83\fl\f\xa5<\x0e\x0f\a\x91\x94\x9eV\xda\r\xdd\xf9A\x0f\xb6\x04\xdb\x10\xbc\x10\xbc\x93\xdc\xd1sV6\xa6\x8e\xb3!\xd0.@\xad\x9a\u056e\x84^\f\xe4\xf6\f\t\x12\xf8:\x9b\xb3\xe6\x8c\xc8\xf8n\x15?\xd7\x18\x10\x1b\xb0cx\xeb \xe4\xd4\xfeu!\x80c8\xd1\v\xe6\xa1\xd4E/\x00,\xa7\x94$b\x8bfb\xd0N-\xec^\x03>\u05c2[^y\xad\xd5V\x1bX2D\x92\xe4\xfe\x01\x91Nv0\xe2<|\xef@\xceA\u0125\x81\xacZ\xd3\u02c8\xd50EZ7\xb0c\x000x\xbf\x83_zH/AD$ \x16\x9de\x18V\x13\xad\x9d\x06\bE\xdaZe\xa8\xb0QX\u00c7MS\x88\xf3\u07c7\x1d\x9e\xfb^}\xf0\xbd\x878W\xa8\x889X6\x0f:\xf3\xf8\u07c2\xa7\xefZ\u00b2!\x15=\x05\x02\x8d\x06\x92K\x1a\u019b\u05a0\x99n\xa9E\x05\x96\x85\x1f\f\x87a\xb9\xc5l\xb6\xc0\xd3O=\x05\x00\xb8z\xf5\xc5u1_\xaf[\xbf\x8e\x8en`{{\x0f\x00pxx8Y,\x16\xc9\xfa3\x1d\\\xd9`\xb2\xb5\v2\r|\xe7R\xe1\xaelk%&\xf6He1\xeb\rrWn\xb4=MRy\x1a\xcf\xdb\x04rr\xa4\x10*E\"\xc6R\x85\xa4,\x8e1}\x99\x81]\x8d\x9cC\x0fp\xaf\xa2\x94N$\x85\x1e\x10)\xe4c\x88B\xbe\xa7\xe6\x8e\u04b6W\xc1P\x14\xf3T\x91?\xf5\xb9\x80\x13\b/\xc1\x17\xab\x84\x85\ua23a\b\xbbTXsa\x88E\u0702!\x90\xd6\xebP\xee\u0423\xe9\bB\x06\x10\x81\xf3:\xa8sN\u09aa\x18U\x81\x92\x01O-\xb0\u05c0no`&\\c\u0660:\xfc\xa2\x00\xaa\xa2\x9a5\xa3\xe2\x94^c\u0083\x9dQ\xab\xda8\x88L\xd6\n\x18\x19\xfceoq\xec\x1a\b\fx\xbf\xd3a${\x15\xeb\x10\xe0\x9d\xa0\ac\xd94h\xd8b\xc2\fc-L\xdb\xc0X\x930q&5P\xeb%\xe6MPr\x824\xb6\x81\x99\xb4p\xddB\x87\xf2\x92g/Y\xa2 \xd9\xd5R|u\xa3\x91\x10\x9c\b\x16\r\xc1t\x04\xebD! .|\xe6\xc3\t\xaa\xd9\xdc\xc9\x16\x15\x05\x04\xc3`\x881X,\x97\xf2\xecs\xcfy\x00\xf8\xfa\xcb__\x17\xf3\xf5\xba\xf5\xeb\xf9\xe7_\bl7\xf9\xe0\xdf\xf8\x1b?q\xff\xf1\xf1\xf1@_/0\xb6A\xbb\xbd\xabFQ]_\xc3\u0525'Hx\xd0S\a\xc8HF\xfe\x86\x82\x80\x85)\x15\xb5\xe1\xfc\xb3\xf4\xe5\xa6*2\x1d\xab\xdcu\x1a\xa0\xe8\xd1\x1a\x15E\xe5l\t\x14$\xff\xd4{M\xa0q*\xe36\xa1\x80F\x95$\x99(\x80a\xf5\u0096^;P\x89\x015\x8a\x1b\xfb\x01T\xa2\x98\xb5T0\xcb\xe0\xf2\x9d\xba\x04y\xa0F!\xb0\"\xf2\xb8\xe5L\xa0\xd9\x1dQ\xb2\xa1\xd5z\xaa\f\r\u01c07\x04\x17\xd2vx\xdb\x02\xb75\x10\x9b\xb3<\x93\x87\v\rLe\x86\x1cK\xca\xd2~\x19\xd9Y\x89\x19\x863w;n>\x88CXHE\xc9D\x80\xfe\xb9\x05\xc4z\xf8n\tYv\xf0\x8d\x16Vo\x04\xd6\x03=\x04\vg1\xeb\f6\xc8`\xbbi`Mvt\xf4\x14\xe5\xff\x04\x1b\xae\xbb\xf3\x81\x9c(\xba_\xab\u0372\x81\x90\u02ef!\x89\x89\f\xbcs\xf0\xe22\xfe\x0f\xa0\xbc\xb5,\x01mC\x1a\x9d\xe7\xe3\xf0V\xed\x94)\xa9\xac\x18\xcd\xc66\xd86I,\x86\xf4;\x03\x96\xcb\x1e{{{\x97/]\xba\xf4\x9d\x00\xfe\xe0\xde{\xeeY\x17\xf3\xf5\xba\xf5+\xe2|o\xbc\xf1\xdaE\xe7\xdc^\xd7u\xa1\x13.\x86?\x93\t\x9a3\xbb@k!\xb3y\x91?\x19y{\xb9\x13\u00a0\x9e\xa6\x0e\x86D\x85B\xb6\xb6\xb6\xae\n\xb5\fA\x81S\x99\u06ab5\x92h\u0624\xeb\xc7S\x03\xd9\x16`\xe1\xc0>\x04\xfcz\xa5\xfaq\u8b09\x95\x93M\x93 \xb3gh\xaa\x0e\"\x05/|\x8b`l\xe5\xbd\u2fa9\xb8\x95\xa6\x89\x95\x81\xd7\n\xf912,\xb3\u0425\xa0\xbfQ\x12\xc4\x04\xdcgK\xb9\xe5\x98\x03\xe8\x03\x17=\xc0I\xd6k\x98\x05\x87@l\xb3caZ\u029bd\xe1s#R\xb2\xb43\xa3e%K\xf4-\xaetR\xe1\x06Q\x90\x1eMdt@\xcca\xb0\xed\xbd@Z\xb5\x11\xe6^\xe2\x88R\xf1i\x02Z\a\xb8%\xe3\x18\x84\x1e\x0e;\x04lNM\xa2%\xe68<\n\xac\xa1B\xd0\xe3\x01O\x04\t\xc2!xWX\xff\x1a0;xb@\\\xb5\xe9\u01efG\xa4\xf3\x14\u00c0\xb7\f\x0f\xa0w\x02\xe9}\xa2gF\x05\xa9m\xa60M[\xfe\x84z\x12\x15\x0f\xd7\xfb\xe5t\xb2\xd1z/\xef\a\x006\xf6-\xb6\xf1u1_\xaf\xb7a\xc5a\u0375k\u05fa\xf9|\xee\xca\a9\u0791\xa6ia67 \x13\x86\xb7ZX\x84Vs)s6X\x9d\xab\x99\x1a\xee \xe7\xa6\xc1\x103\xc16\x95eG\xe9\xad1,\x16\xe3E>\u05dfh\xea\x14:\xa8-\x03,-\xd8uh\u0200z\xafy\x96\xe1\xb5q\xb4\x9emIa\x00\f\x92|JhB\x04\xc6\x10\xc4S\xf0\xad\x11$\"z\xf5\x1a\x86\x83G\t1zA&?`\x84\xac\xd8\u007fK\x88\xd5\xdb&\xc0yH'\xa9\vN\x9d\xa1Wk\x04\xbfe4\xceN\ua409lD%\x95=\x80\x14\x97\x89\x90-\xd2E\xb0\x1a\x9e:\f\xc5NB&_|-\xca0\x93\x14\xa76\x0f\xc8\xcci\u0232!H\xe7\xe1\xbd\x16cb\r\x880N0\x99{\xcc-0\xef\x19\xfd\xb1\xc3\xc2\x03\xdbSFk\x92\xa7b\x15=\x17\xe7\xaf\x1e\x80\xf3@\x17a\xbeP\xec!z>\x122\xc9z7v\xf8\xd5\xcc \x88\x8d\x98\xf4t\xc5\x1b\f:!,\xe7\xe15\x16n\x8a\xb6\x9d`\xb2\xb9\x8b\xa3k/\x17\xf7g\xc0\u052d\xa5\u00e3c<\xf9\xd43\xc7\x000\x99L\u05dd\xf9z\xbd\x13\x98\xf9\x11\x00\xe0\u018d\x1b\xe8\xbae8FF\xa3\xa1\x10\x1c\x11d\xfa\xde\x00\u0796E\x83\x82;\x1eV\x83$R\a(\xb9xq\x0e\x83(a\x1a\x06\x06f\xe2\xf5\xb7\xa8+I\xc1/\x1e|>\x86\x1bD\xa8M\x86\x01\xbfc\xe1\x9d\xc0\xec\xf7\xabX\xbd\xf7@\x17^\x9f%\x15\xb8T\xa7\x05\xa9g\xac\xb1\x9b\x17\x0e\x02*JA\x13\xa9\xd8\r\xbcu\x89sDY\xe93\xb3\"\xfb/\xc25\x88\xf5\xf8/\v\x01\x0e]\x1d\x8c\x03\xa8\xc8\b\xac\u0664!\xb7BLm\xcd{Z{(\xc5\xe1\u01e10L\x93\x02\xf2\x1a\xfb\x9d\x8e\xbc\x17\xb7W/\x929\xf7\x80\xb2WN\x1c\xd0I*\xe2\xce\xe7\x0e96\xf5\xb2\xf4h\x16j\xbb\xe0\x9c\xe0h\xee\xb0\x14`\xa3!lZ\xed\x9e\xe3\xe5\xe4d\u02dc\xb3F\xbd\x10\xe6\xbdn\xca\rS\xa2\xcc*\x13\x86\x835E\xf9\xeb\x94<\a\x15\x9f\u007f7.$\x19\u016f\x11\x93\x87\xe0a\x9a\t\xa6\xbb\xe7B7\xae?K\x14\xd5\x1acpxt\x8cW^~e\x02\x00\xff\xf5\xaf\xfc\x8aH\x05\xfb\xad\x8b\xf9z\u0742u\xe3\u018d\x00\xb3\xbc\x81\xbe\xef\a\x1e\xe4\x91\u007fl\x82]\x88\x87\x04Iv|\x9e\xcd\b\x9e]i;$\x91\v\x92\x89T\x8d>\x04\x18\xa0d\xa9\xe0\x14\x0f\xa9\x9a\x8e\x81\x9aK!\xa3]z\xb2@\xb7\xcap\x91N\xc0\xfb.\x83\vqo\x12\x01\x16\x02,=\xc4\x0e\x1aT\xa9Y\x119\x9c(gE\n\x17\x8a\u039c\u021c\xb22A\\Y\x11\x94Cd\x91\xb2\u04e3\xcc4Ah=\xbd^w\n|\xca\xc4$\x89\xb5v)\xf0\x8d\xa07\xd9>\x9c\a\x9e\xe5\xb2\n\x85\x87\xce\x14\xd5\u0303\x8b\x80\x90\xe8\xdcX6\xe7\xe9\xc7K\u009d\xdc\xf5KTkF\xab\x85\x99\x83\x9f;\xb8\xa5\x87[xx78\xa8\x84\r\x9f\x1d\xd0\xcc=\\C\xe8'\f\xf1\x82e\xef\xd1y\u00bc\x03\xb6Z\xc6\u0532b\xe4\xd1}\x92\xf2\u0463i4lZzI\x14E\x15\x101`\f\xe0\xbc\xe2\xe1\xc80\x95Gq\xd0\x11\x00\xbd\xc0\x1d\xab\x8b\xa2\x04\xee\"\x89\x86\x85;q\xa0\xa6\xc5\xf6\x99\v 29\x9d(l\\\xd6\x1a\xbe~\xfd\x06\xb6vv\xbeOD\xdeMDO\u007f\xf9+\x8f\xf0\xea\x91m]\xcc\xd7\xebm\\\xb9[L\xa8o\x91n\x13\xdd\x00\x95\xa2\xe6{\x0f\x81WA\x87\xb6Ia8\x88\x8a\xafM%\xf6-\xd0\a\x89\x18Q\u0418fp4,\u0265:h\x00<\x14\xb4\xbe*$X\xc6Fv#\xcb+l!\x9b\x06|\xe4\x82JD*\xdb[\x9a{\xe0\x10\xc0n\xe9\x94\x18\x8a,\xe5\"Z\xee%\xe9\x8c\x10(\x10\xf92J\u0751\x97@6\u0556_\xa5\xf5oe*%\x80\x9cx\xd0\xccg\tyX\xbd\xa8\xb5\xac8\x0f:\xea\x01\x03\xb8M\x13\xca\aeXb\x18Y\x11\v\xb8\x94\x87\x88\xdce\nj\x1dV\x91\aR\xed\xa8\x12:\xd3\xf85bW\x1eA\x1d\xd7\v\u0731\x83\x9bi!G\x9f3Y%\xf8\xbe\xe4\x03\x00\xc1\xf4\x82f\xee\xe1C87y\xc5\u0717\x0e\xe8\xe7\x1e\xf3F\xb0i\x19\x1b\x96\xd4y\xb3\xb8\x8e\xd1q\u0449(\x1f\x1e\x02\x1f\xa5\xf6.\x18|1A\x1cU\xa7\b\xef\x83\xef\xcaR '\x0en\xe1\xd5\x0e\x80\xc2\rCq\x98\xeba\xed\x04[\xb7]\x00\xb3\x85x\x970\xf7\xc0\u28ae\xefq\xed\xda\xf5\xb3\u007f\xfe\xe7_9\a\xe0\xe9?\xfd\xd3?\xfb\x96rN\\\x17\xf3o\x83\xb5\xb5\xb5\x05\x00\xd8\xdd=\x83\xa6\xb1\x15\x94\x91Q\b\a\x17Zg\xf1Y\x99W\x9e\xe3%\f\v1\x92\xb5\u0245\x97\x85\xbeIb\x89I\xe1%=\x90\x0f\u055e +\x8aCY\x1dv\x9e\xd2\xcc{\xc9](H\xbbt\xf4\x12\xb3\x12\x10\xea\x06\x8c\x03\xe8\u062b\xf9\xd56\xe7\xe1\x1b\xe5\xee\xb9T|Fiz\xacR\x14v\xa7\x98Z_\xbd&\x1a\xf7\xa1\xc90\x05\xad\x82D\x9d\x00\xc7\x0e\xb2\x10\x90SJ^\xec(\x97\x02\xf4\xe1\x1d\xea\xf5\xf3=\x11x\xcahL\xceT\xa5\xe2\u0691T\a\xaej\x10-8\xd5Q\xb7\x86\xbe\x90F\x11\t\x1f\xf7\x92\xdf\aT\x9c5?v\xf0G\x0eXx\xf5l/TW\xb9\xb3\x0f\xc6`a\u00f0K\x81\x9f{,6\xb5;\xe7\u008fg\xd6\t:\xe7\xd0y\xc6f\xc3hM\x11\x10R^\xe0b\xa0\x1c\xd9A>X\xf8:\x0f\xf4\xbd\xa0\xef\xbd\xd2\x1d\xbdW\xe8m.\xa0.\x04\x0ez\xa9g\x0f\xa44\"\xdbN\xb0}\xf6\"\xda\xcdm\u030fn\f\u0384D]\xef\xd0\xf7\xee]\x8f|\xf9O\x1e\x04\xf0\xa5\x97_~e]\xcc\xd7\xeb\u05ae\xf3\xe7\xcf\x03\x00.^\xbc\xd0L\xa7\x1b\xa6\n\a\x0e\x0f]\xdf-\xe1l\x0f\xd9d\u040c\x13\u034b|\xf4\xba\x88\xe7\xe7,d)\x8b'\xb3\x0e\xfe\xa4\xc8\n\x95\xa0^\fv\x86`\x89r\xf2\xec\u0597\n\x10\x8d\x90\xd1O\xa9\xdc2\x98\xd9\xc5\xe3\xbf\x10@N\v\xa4\x84\x82.\xbd\x84\x87\x1a\x80\u04ce\u03c8\xc0\x1c(\u03dc6\xa3\xf2\xb2\xe8\x9c\aa\x10R\x85\x86Rm\x8aM\xd9\xe50y\x84\xa0\x8c\xb2\xab\xc5P\x05D\xaf\x1d\xe6Qx[\xc6\xe0\n\x86\x17\xe5];\x0e~[>lT\xc7\x0e\x1d\x01\x1e\x16\xdc0,\x11\x1a\x124&\xe4rV\xae\x88X\x11\x14\xc5\xea\xee\xb3\xcd\x14\xa8\xc4\u05fddc4\t\xb1|\x11s\x0f\x17\u018b`\xe9\x05\xb3\x85G\u007f\xe4`\x97\x02\xeb\xea\xefYN;d@Ae\xa7\xddy\xdf\x10\x1c\xa9\x1f\v\xdb\xfc\xba\x9d\a\x0e\x17\x1eK'\xd8j\x18\x9b\r%\xc36?82\xa9s\x80\xa0\xf3@\u702e\x03\xba\xa5G\xb7\xe8\xe1\xfa\x108\xed\x05f\xee@}T\ud19d\xaa\xa0\u062a\xca\x13\x90\xdeac\xeb,v\xce]\xc6\xec\xe0\x9a\n\x95(:4\x12\x11\x91;8<4_\xf9\u029f\xee\x01\xc0\x9b\u05ee\U0007a62f\xd7-]\x97/\xdf\x05\x00\xb8\xff\xfe\xf7\xbch\x8c\xb9\xd64\xed\x9d\xfd\xa2\xcb\xcf\x05\x11\xdcb\x8e\xc5\xec\x00n\n\xc8\x06CN\xfaB\xf4\xa2\xc5\\\xbcW\xe2o\xe9~H\xc8\x11k\x94'\x9a\x12\n\xba\x17\xc0\x05\u05fb&(\xf6R\\[=\u007f\xac\xbaX*\xf1p\xac\u01b6y\x19\xf4\xbf\xf1k\xb9\x80\x89\xfb\xe8\xa1\x12\xbbv\xfd;\uf05e\xb5\xa80<\x98\x18<\r\xa1\xc8Bu\xb0\x05Q\x96J\xa6\x02?\n\xe9Ws\b*\xe2\u0688\xea\xd7.\xe9\xe8\x0f\xc8\u0703\x0f\x1cp\x02\xf8>\x9ayi\x01\xf1\x84\",B\x92\xa8\xca\xce<\x16\x8d\xc7b\x9b\xd0A\xb0\x00`z\x825:\x18l\r\x05\u0719j%\xfe\x00W\x8f\xa9N\\0c\xbcD\a\u00c8YQ\xc2\xfe\xbd\bz'\x989\xc1\xac\xd7\xeez2w0\xbdO\x1d9\x95\x1e\xe0X\xfd\xfdD\x1c\x9a;\xa0\x9dy,\f\xa9\xf0H\xa8\"\u0590\x00\x8b^\xd09\x8f\xce\x01\x1bF\x1d\x19\xf1\xb2~\x8c\x00\x00 \x00IDAT\xbd\u03ff\xf7\xde\x01\xb3\xcea\xd1y\xb8^\xe05\xd5\x0e\xce\xeb\xc9\u0487\x19\x89]z\xd8pj\x90h#_\xdcx\n\a\xe9\x89\xcb-\xe6\x98l\x9e\xc1\xd9K\x0f\xe2\xb5\xe7\xfe\"5/\xf1\x04\xd6\u0616\xbf\xfe\xf5W\xf0\xe0\xbb\xef{XD\x1a\"Z~+\u0541u8\u0177\xc1z\xdf\xfb\x1e\n\xb38z\xcc;\xf7\xfc\xf6\xf6\xb6\xfa\xaf\x14\x86L\xbe_`v\xed58\xe9\xd0oY\xb8\t\x87pa\xed\xd0<\xbc\xfac@\xbd\xc3\xd3s\x1bR\\$\xfc9r\xc0L\u0228,:\xea!\xfb\rU\x06\xc0\xcaG\xd5\xe7\u0240\x12\x87S\x86~]\xe8r\x9d\xc0\xf7\x02\xd7+\x03$B\xdc\xec\xf5\xe3\xae\a\x163\xe0\xe4\x06p4\x03fN\xd0I\x0e7F\xe2<\x97\x85\x9aV\xe97$\xab\xaf\xbcP\xe6D\xf6\x8b\x17\x81s\x0e]\xef\xd1y\x8f\xbe\xf7\xf0\xc7\x02wBp\x1d\xc1{J\x9f\x1b_'\xc5\xea\x1a\xac\x03\xd8\x01\xdc\t\xda\x13\a\xd3\xe7!d\xe7\x05\xb3Np\xbc\xf48Zz\x9ct\x82E/\x8a/\x17\xb8\xb9H9\x89(\nZP\xcc\xc6k\x9c\xdf\x04\xcey,\x9d\xc7q'\xd8_x\x1cu\x82\xce\t\xcc\xc2\xc3\xf4\xa2F`R\xc8\xe7e\x04\f\v\x03W.\x14\xbev\xe9\xd1\u0303\xaf\x8a[\xa5\xd3pPv\x1eu\x0e7\xe6\x0e\a\v\x87\xe3\xa5\xc7I\xe7q\xb0\xd4?;Z\n\x16N}\u1f68\x8a\xd9Z\x03n4$\xda:\x82\xedC\x01\x17\x02\x87\x1f\x8cD\xe1\x1d\x16$=\x82x\a\xe9{X3\xc5\u079d\xf7c\xb2\xb9\v\xef\\:\xbfH\xd8\xd0Ofs\x1c\x9f\xcc>p\xfd\u06ab\x97\xbf\xd5\xea\xc0\xba\x98\u007f\x1b\xac\xe9t;\xbd\xff\xc0\xbb\x1f\x90\xdd\xdd\x1d\xb8\xbe\x97\x12\xb7p}\x8f\xe37^\x86_.\x81\xb6A\xbfi\xe0Z\n\x1d\"\x82\x90\xc3\xd5>\x18a\xe8T\xce2)\x14p\xa6p\xf4g\no\x99;\x1c\xe1\x199e\x92I\x83\x02\xee\a\x03\xd8!\xf7\xbdt\x00t\v\x8f~\xe9\u0447\xff\xfb\x98\xff(\x9a\u00e9\xff\x97\xf0 \x03\x98\t\xba\x03\xe0\xc6\xc2\xe0ZG8\xea\t\v\x1f%\xf4T\xc0%\xa5 ^*+\xf0\xe8\xab\x18i\x9a\xf1T\x12\xdd\b\x9d\xf3\xe8{\x87\xcei\x81u \xf4\vF7c\xb8\x9eV\x87\x8f\xe1\xd8\x1fO\ni\x18\x1d^\xbbY\n\x9a\x93\x9c\xd1Z\xb2F\x97N\x8b\xfa\xc1B\xdfN:\x85,\xa2\x16)\xbfI*R^j\xa6J,\xee\xcb^p\xb8\xf0\xb81\xf78X8,\\\xa0\xeby\x80;\x0f^\x86k\xeasW\x9eSz\xa4\x1e\x8bH\xa9\xc2\xd7\xe1g3\xf3\xb0s\u5967\xc3P\xf1\u03e3\x1dD\xe7\x04'K\x8f\xa3\xa5\xd3\xcdj\xa9\x1bX6\x0fCrP\x8c7\x1a;\xc0v!{\xd5\xd7\xe0\xbf\xf4\x1e\xe2\xf4M/\x8c$\x9bg\xdf-\xb1{\xee2\xce\xdcy?|\u07e5P\xedxk.\x97=\x98\u0307\u007f\xef3\xff\xfa\x01\x00x\xf4\u047f\xfa\x96\xc1\xcd\xd70\u02f7\u067a\xfb\ueedfi\u0513\x99\x04\xd9\xe0\xd9\xf7=n\xbc\xf8,\xdcb\x06\xbb\xb5\x8b\xaea\xf4\xdb\x04C\x0e4\xcfT1\a\a\x8e\x14<\x92\x14zL\x05K,\x9d\x95\ubf00\x02\xad\xa0\xf4\xac3j\xd6\u02f0\x1b\xc7\x00F\xa9\x1a\xdfb8\xd7;\xc1\xe2\xc4A\x0e: \xf0\x9e9\xc8\u096a\x109L\xc2\a\x8c\xdd\xce<z\u00d8o2\x16\f4\x0e\u0630\xc0\x86\x01\x1a\n~\x88\x15\xb6\xe2Wp\xe8\x01\x05^\x8b\xa4\x0f\xd8wP\x18\n\x18=\x18]G\xc0\xb1\xa0Y\xa8\xa4|\x84\x89\x9e\xe6\rTX\xf8\xa6k\xdd{4\u01ea\n\xed6x\xc5\xd4\xd0\t\xe0D\xb1\xe4\xb9\x13\x85_\x98\xd0\x18Bc\x82M1Q\xf6='d\xf6\x87\xd3\u03bbs\x82y/\xe8\x00\xb5h@a\u0535p\xb03\xddTHj\u06c3\xd1\xe94\xb279\x87_\x98'\xc0t\x82\xf6$\xb0[lpQ\x1c\x8a\x82\v\xae\xb8/\xf4\x11%\xd5R\xfd\x82(\xc1~\x1cpr\xd3\xf9\x9c\x13\x1d\x8a\u007fd\xd8\b\xa5@\xbc@\x99U>\xbf\xef\x97\xd8\u07bb\x80\xf3w\u007f\x00\xaf_\xf9j`\xf4P\xe2\x9e\v\x80W_\u007f\x03\u007f\xf5\xb5\xc7.\x02\xc0\xfb\xdf\xffAY\x17\xf3\xf5z\x87\xba\xf4\xe9\x1f:\xe7~\xa1i\xec\x8ew^}\u02a1\xc7\u0323W_\xc2rv\x8c\t\x1b\x104,\x19[\xd0\xe0\x85\xb9\x1e\x93\x1d{\x80\x03\v!\xa3\x00\x11W\xc9L\x16\u0285\x8e(\x17x)\xcdGiP\a\x06\x05{\u0629\x97\x03\xd0\xe4+\xee\xb5\xe8\x1c\xcd\x1c\xfa\xfd\x1e\x93\x13\x8f\xb6\x0f\x1d#ibO5[\x95\xb0\x01\xf9\u8ded\xf8\xf9\xf4\xa4\aL\x83~\x83\x94E\xb2\x04f\x860eB\ub056\x05\x86\x82*r\xe8\xeb\x8e2\x18\"\xc0\x13\xa1\xa0\xc6b\xe2\xc0\xe8`\xb0\x14\x03\xcc<\u06a5\x86YP\x1c\xbe\rmi\u00b55^\a\u007f\x1eY|K\x02P\a4'N\u00ec\x1b\xca\xf4\x93\xc1\xcb\x12\xd1n\xbds\x02\xe34\xf9\xa7\t'%\x13\x8d\xc8\u0086\xd8-=\xbaN\xf9\xdf.\xea\x97L\x10|E\xcfz/\xb0s\x0f\x13^\xff0R\xaf\u0718\xaa\x01w\xf8\x05s\xfcf\x81'n\x96\x1evF\xe8ZN\xa7<ZqX\xf6yn@R\x8b\x83H\xb7f\x82@X\xc0\x8e`\x96\x00\xbb\x1c\b\u0392\x95\xc0\x948\xec\x94N\x89y\b\x1f\xaf/\xe1\xcc\xf9wac\xfb,f\x87o\x82\x9biz-\xc6\x18\\}\xf1%|h\xfe\xc1\xef\xf8V\x13\r\xada\x96o\xb3\xf5\xc9O~\xf2\u037d\xbd\xbd\x05\xb3J\u0563k! X\x1c^\xc7\xc9\xf57\xb2\xc0E\x00g\x19\u0766\x81\x9b\x10\x9c\x01<\xc5\xd2\x12n\x10\x8fp\x94\x95\x14\xdb\x06_\xfb\x84\xa4\x87RV\xc5\xf3\x83:[\x9b\xf6\xa1\u019d3.\xadP\xc0\xb2W(\xe1\xc6\xc2\xe1d\u1065\x87q\xb2\xf2}V\x1bFI\xd49\n\xbc\u0148E\xdb.s\xab;'8X\b\xae\xcf\x04\xd7g\xc0\u0452\xb0\x14N6[\x92\u07b40y\xaf\xc7\xffX<E\x00'\x84%\fN\xa4\xc1\x89X\xb8\x0e\xda\u057a\x9a\xcaW\r'\x91\v\xa7\xe9\x14\x9b6N\xaa \x10\x88\xc0,\x14n!\x87\xcc\xef/\a\x8e\xc5\xff\xa1\x89uX8\xc1q\xe7q\xb8p8Xx\x1c.\"$\x13 \x8c\x85\x87\"p\x83IF`\xf5p'\xb0s\a\xee\xe54\r\xd7Hg\x9eOF\xc9Z\xc1k\a\xcd^\xd9-f\xe6\x14;/\u0607\xb9\x1f\xf0\xc9?eh%\x11\xe3\xf0b\u03a7Y\nl\xef\xf3\xcf\x1e\\(\xa3ChY|%\x85r\xf8j\aq\xcb9vo\xbf\a\u06f7]B\xdf-\xc2\xd0\u06a7(\u0093\x93\x19\xf6\xf7\x0f?\x02\xe0N\x00\xf8\xf2W\x1eY\x17\xf3\xf5\xba\xf5\xeb\xfd\xef\xff\xe0\x17\u039d;\xf7\xc6\xce\xf6\x0e\x9c\xebU\xea\xac\xe7N,g\xc78z\xf5j\x90?\x1bm\xb6Az\x9c\u07f6\xe8'\xac]\xa2\xb8\xf4`\xc6\xeeRz\xed\x92{\xe5\xbd\x148l\xae\xc2%t24&\x1c\x1bl\n\r\xc2*\x82\xc4z\u953ev}\xeeq\xb8\xf4\xe8|P\xe9{$\fw\x05Q\x1f\xa8;S7\x16pt\b`\x16\x1e\u0371\xd2\xd8\xe2 \x17\xd0\xc2|\xb4\x10\\?\x11\\\x9f\x11\x0e\x97\x8c\x85#\xb8\x98?\xeaU\xe0\xd3y\t\xb8\xb8\x1e\xfb;0N`q\xec-\x96PFh3\xf30]\xc0\x88]\x06\x94ip@I#WO\xa1\xf0\xe9\t\x82b\a\x1c\nU\u011d\x87\x85|Xai\xf0u%\xc01K'\xe8\xbd$H9\xf9}Sa$\x16\u007fg\"03\x9f\u1555\xcd2\xb3\xf3\x05\xa7\x15z\xc9\x03^\xe8\xef\xca\xf6\x1e\xed\u0301;\xc9\x12\xfd\xf0\x83\x88s\x90\xde%\u06a4\x1f1<\x8b\u0775Y\uad48\xa9D\x14\u007f\xb7T\x06\x87\x87\xfb\xc8\xc7B\x1emh\xf2=\xe2\x9d\xc3\xc6\xf6Y\xec\xdeq\xb7f\xb9\xa6\xcdDW\xef\x1c\x0e\x0e\x0e?\xfa[\xbf\xf5\x9bg\x157\u007f\x9c\xd6\xc5|\xbdn\xd9z\xf2\xc9\xc7\xc2\xf3A\xb3\x87\x1ezh\xbe\xbd\xb3\xad\x0et1a\x86\x80nq\x827\x9f\u007f\x02\xde\xf5\x1ay\x96\xfcE\b\xd22\xba-\x037\t\x93\u007f\u7a87\x99\x9c@\x16\x1e\xae\x0f\x98-\xb2\xce9u\xe8\xc9f\x16\u0574k\x14B\x89\xb2\xf1\xf0q/\xda\xf1\x1eu\x1e\xd7\xe7\x0e\xfbK\x8f\x85\x93\u031c\xf1\xa2\xec\x8a\u00a8\x8ad\xd0\xfb\v\nA)\x15Gp\xfd\xbb\xd8%\xdaY\x96\x84S\x81 9/\x98-=\x0e\x16\xc0\x8d%\xe3\xa0c\x1c\xf7\x8aK/\x1c\xd0{RL\x1c\x063\xb18\xf6\r\x16\xde\x04a\x8b('{\xe1\x146\x90|\xe4\x90\xc1IE\x068\t\xf9\xf0Vp4\xd3|\xd1iwn\x96z\xc2\x12)q\xe6\u054d\x92\x06oLyp\x9dy\xf2\x94<\xc5\x11\xf4\x02B\x00/=\x9a\x99\x039\x19d\xac\xd6g\xad\xfc_\x19?\x19\x85\x91\ny\x81\xf1\xca\u04b1s\x81=v*\u02e7|2\xf4\xce\xe9\xfd&\x1eU<w\xa8\xec\xe9:t\x1ev\xe6\xd4`-\xc4\x05r\xf8\xbf!\xceXy\x11\x8f\x17\x1d-\x93\x127u\xec\xfaL\xec]x\x00\xd3\xed\xb3\x10\xd7W?\x87\xf7\x82\x17_zi\xf2\xda\x1bo\xbc\x17\x00\xfe\xb3\x9f\xffyY\x17\xf3\xf5\xbae\xeb\xc1\a\u07d7\xde\xdf\xde\xde\xfa\xacZ\xb4\x1a=>\x86\xc1\xa0[.p\xfd\xf9'\xd0\u03cea\x8c\xa9\ak\x02HCXn\x19\xf4\x8d\xde\xf0\xa9\x93\x11\x1d\xf6a\xee\x80\xc3\x1e\xbe\x93@ P\xec\u060b\xfa\\y_\x02/\xf5\xf0-\xf1@\x12\xcb-\xc0\x14\x01\xb68\\\n\xae\xcf=\xf6\xe7^y\xceR\xe6F\xaa\xf5*\xf7yhXU3\xd4\xd9qe\x1c^\xb4\"H\x96\xb1\xbd\xa0\x9dy\xd8P\x1c\xd3\xeb\x93\x1c\xf2 \"Xz\xe0\xc43\x8e\xbcv\xde3o0\x13\x83\x13X\x9c\x88\xc5B\f\xfa\x98\xfaN\x00\xf5\x80\x9d9p\xa0\xe1\xb1S\x88a$ \xaf\n4\xa3\x04I \xb1q\xaa\xd0\v\x00\xa6\xf3\xb0\x01n)\x9fX*\xf1w\u0526\u00e8\x1b\xd5\x02\u007f\x1e\xf9;\xaf\x83\xe2&\f=\xd9\xcb*l5J0\xad7\xd2*z/\x15\xf4\xf0\xf3\x85M\x89\xe7>o*\xde\x01\xbe\x0f\xf7\x1a\x06\xe1\x1b\xf9\x9e\xa1\b\xd5t\x92\xba}v\x8a\x93s\xc4\xca#\x93%\x06G\xa7$\xa2\xb0\x891U7\xa3w\x1d\xce]x\x0f6vnG\xdf-\xf2s\x02\xed\xe2_~\xf95\xbc\xfe\xfa\x9b\xffa|-\x8f>\xfaW\xebb\xbe^\xb7~=\xfc\xf0w>\xb2\xb3\xb3\xdd5\x8dzP\x88wZ<\xbd\xc7\xc1+W\xb0\u007f\xf5\xa9\x9c\xb2\x1e\x8bvx\x13K\xe8\xa7\f\xc7\x01;\x17U.\xf6^\xd09\xa0?\xec\xd1\xdd\xe80_(7\xf9h\xeep8s8\x98\xf58\x98{\x9c,\x04\xb3\xdec\xde\v\x96}\x80%\x9c\xaa4]`\u007f\xf4^\xa1\x94y'8Xx\xbc9s\u061f;\xccc\x11G\x9d\x0fMPf\x04\xfb1,gP.\x05+JL\xf2\xc1\x9b;*\x06\x97\n\xb7p\xef\x87_\xa4*\x86\x19\x13g,`\xb0\x80A/\x9c\xb4\xb2E\x9e\x05\x9a\xb9S\b\xc0\xe5b\x13\x8f.C\xf8\xa3*\x95\x9c\x95\xba\xe4\x03-\xb0\x1c.\x04\xaby;\xf7h\xe6\xaeH\x10\x02\x06\xc9\u007f(\xffKE7N\xa5\x9d/e+\x83\xf2E\u0645v\xe5\n\xf5\x10\x86{\xf2*\u06fe\x18SS\u044dK6k\u031b\x8dRE\xcd\u00a39r\xaa\u0685\x069\x8b\xf39\x1c\xa3\f\x9e.6\xaaf\xe1`\x17>\f\x8cI-\x1bb\x17\x1d\xe9N\xd5\xc9 \x8cU}Q\xd0\xc3&\xedS5\x17l\x9d9\x8f3w\xdc\x03b\x0e\xf3\xa5\xc0.\x12\xc1\xa2\xebppp\xf0\x9f\x1f\x1d\xbcy)\xc0\x97\xebb\xbe^\xb7~}\xfa\xd3\u007f\xf3_\xbd\xfb\xdd\xef\uedb7\xb6 \xe2\xc5\xfb\xc8Y&\xcc\xf7\xaf\xe3\u0367\xfe2t\ub738\xd2U\xe0/\x03\xdex\x1d\x86\x86\xa1gT\b.\x17\x1e\xcb\xeb\x1d\xe6\xd7:\x1c\xcd\x1c\xf6\xe7\x1e\xfb3\x87\x1b3\x87\xfd\x93\x1e\u05ce{\\\x9b9\\\x9f\xe9\xe0\xf2\xc6\xc2c?\f\xe1\x0e\x97\xf5\xdb~\xf8\xb3\xa5\x93\"\x86\xae\x1e\x98\"tb*`\x89\xbe\x1b8\u0144\xe4\x94a\x9dW\x00\x99|\xaeLv\xee\u041c\xf8\"<a\u012e\x97\xea\xa24\x9c\a\xc4a\xa5Y*\xd6\x1c\x8b\n\x87\x82N#\x18D\xad1\x1d|\x14:d\xf8\"\xb0)\xc2\xee\x0e\xb0'\x1e\x1c\xad\f\x06\xa7\x90\x95\x92\x9b\xe6\x19y\xa0(E\x98\xb1 \x17M\xee\x05\xf6D\x19,\xec\xe4T\x81\x00U\x00}\x9d\x105\xac\xef\xd5\xec\x02\xb9C7'\x0e<s)d\xda\a\x88%u\u5147<\xc5k{\xe2\u00c6L\xba\u0641T\xd9K\x9c\x99Ta\xf8\xc9E89\x0f\x9d@\xa9T\x88\xea/\xf0\xf6\xcb\x0fa\xbay\x06\xe2\xfbt\n%f\xf4]\x8fG\x1f}|\xeb\x9f\xff\xce\xef>\xfc\xad\xf2\u072f\x8b\xf9\xb7\xd1\xfa\xdc\xe7\xfeM|\xf7\xf0\x81\a\x1ex,\x18\xec\x93\x1e=\x1d@\x8c~~\x8ck\xcf?\x0e\xd7-S\u01e2\x1d\xa0\xd3\xf3\xb6\x0f\x82\v\x118xx\x1e\x1c\xa8\xc3d\u035c8\x98\x13\x95\xd5\xfb\x88\u007fz\x95a\xf7=\xb0\U001024e0Z<(D.\x87\v\x8f\x93\xa5`\xdey5\x99Bf<\x8e\xc0\xb3\t\x1aQ\x88%;\xf5\r\xd9\x18\xc3\x0e\xbb\xa2\u03608\xf2\x17\x1bDs\xa2\xdd4R\x105a5e3\xe3\xd442\xc5$\xafE\xd6tR\r.\xc9I\x8d\x8d\xaf\xbc\xea1P\x04`\xef\xf3\t\u0123\xb0\x8b\x11e\x9a\u031c\xfa\xe9\f.\x97/f\x18\xe3_y\x14\x91\x82\b`\x16N!\x90N\xf2fYz,\xa4\r`0\x9f(\xbd\x04\u02bc\xcd\xc4%\xaf\xbfQ\xb4\xca5'\x0eX\xf4\xa1y\xf0\xa9\xa1\xc8\n\xd6p\xedz\x0f;\xeb\x95YS\x0e\xbf\u3f03\xa0\x05\x9d9\xf1\xc4KK\xa2tm\x12S\x86R\x8c\\\x9c%\x9d\xbb\xf8^L\xb7\xf7\u0f7e\x9eHGt\xce\xe3\xf5\xd7\xdf\u011f\xfd\xf9_\xfcd\xfc:\u007f\xf0\x87\x9f_\x17\xf3\xf5\xba5\xeb\xe3\x1f\xff\xe1\u0521L\xa7\x93\xff}cc\n\xdb4\x19\x13\x06\xe0]\x8f\xa3W_\xc4\xec\xcd\xd7@\xe0\u0288\x88\xc4\xeb0\xc8\xf5\xc1|\xcb\xc3G\xbb\\\x1e\x84\xe0:\xf5\u07d8\u0303\xcf4\xe5#u\nA\xa6\xfa-w\x87\x92\xa9u\xc3r\xb7\x12@\xa4t9v\x12X,\x85\xb0u\xd8\x05\x16\xb62\u547f\xac\xca$\xb9`q\x1f\x86\x8b\x9dO\xf4MB\xa6>&D\xa7t(\x1c\x90<x\xe9\xc1\x91S\x1ep\x17\xf2\xc3fYN\xd9t\n\xbb\xe2\x02xfW\x84\x82\x94?c\xe4\x80/|\xbd\xd9\r\xdeV6\xa3\xd3pn\xa2\x84e\x9b\xa5W\n\uaa7b\xc0\x88)\u02d8A\v\x06\x9e\xf6\xc3Y\x86\bx\u0583\x8f\x97jX\xe3\xa5\xe6\xa6\"_|;\xd7\xd9F\xb2\n\xf0\xf5\x04=\xbf+\x03S\xb6\xda\xf63R\x16c2T\x9a\xb9x\x87\u035d\xdbq\xdb\xf9\xfbA\xa4\xac\x96b\xfe*'\xb39\x9e\xbfr\xe5'\xfb~\xf6>\x00\xf8\xc1\x1f\xf8\x18~\xff\xf7?\xb3.\xe6\xebuk\u05f9s\xb7\xff\xf1\xddw_F\xdb4U\xcc\x16\xb1\xc1\xec\xfa\x1b\xd8\u007f\xe9Y\x95H\u01ee(\x98ei\u01e2]\xbab\x8c^9\xce<pA\x14e*\xb4s\x8fvQ\xc3\x15\xe2W\x9fu*\xe0\xe1*\xc3\x19\x18\t!\xce\x0f&y\x81\xe9}`X\xc8\xc8\xe0M2\x1c1L\xd5I\u07ae\x19;\xa7\x00x\xc7\xee\x91{\x8ff\xa6Xw\x15\xcb&\xa8L\x14\u01ecz\xc9\x03\xcdB\xd2\xd03\x16\u0728F,q\x109-\"o\xe4''\x0fp\x1f\xe1\x1a)\x95\xf3Z|g\x1e\xa6\xaf;\xechx,2\u019a\xa9\xf7\x8f2T\xa3Yz4s\t\xbcr\x19\x84\x84\f\u01b6oa\xaf\xbb\xe2r\x9c\\\x12\xa8\xb6F\xee:-\xe6K\x97\xe62)\x05%|W\xc5\xf0%\xcd;8x\xac\fO\x00\xf1\xd4\"\x85\x1d/E\xf8%n'Q=*R\u51ca\xf7\xb0\xb6\xc5\xc5\xfb\xbe\v\xcdd#\xe5\x8c\n\x00f\xa6\xa3\xe3\x13<\xf5\xd43g~\xe37\xfe\x97\xbf\x15\xbf\u0697\xbe\xf4\xa5u1_\xaf[\xb3\x9e|\xf2q\x00\xc0\xe5\u02d7\xaf\xdeq\xc7\x1d/\xd8@A\x8c\x8f%\x1b\x83\xf9\xc1\x9b8\xb8\xfa\x14L\xc8pL\xc3\xcfp\xa3\xfbP\xd0)\xc0.>(>c\xaaL\x1c\xf2A\xa0\x05}\xe6\xd1,\xfch\x81\x1a\x0e\xff\x860\x80\xc88\x04\x90:{\xa7\xf0\x822=h\xe0'^\x1f\xab\x85\xea\xae0\x16\xfe\u0215O\xf3\xba\x82sL.\f\x17\x17\xbeN\xfe\x19r\x97\x87\xb5+\xe2\xb9K\x9f\xd9\x1f\x82\x84\xeb\x0f\x85Q\xd5\xd7\xe128{\xac\xf4\xea\x06\xc1\x9d\xcf\xd0G\xb1\x91f\xbeuY\u0425F>\n\xf8E\x06\u05d7\n\xf8J7\x86|Z\xa1*\xf7\x94\xea\xd3C\x89\xa6\xac\b\b\xa4\xee\u02a5t\x12\x0e\xb4J\x0eZO\xe7\x82e\x80\x0e@3\"\x13\xe4\xfa\x9d\xc0\xc4al\u0090d\xe5^-\x19WD\xa5NW*x'\xb2[\x12\x9b\xcaG\u058a\xee\xe0g/\xbe\a;{\x17\xe1\xbd\xcb\x1b\x1a\x11\x8c\xb1x\xf3\xcdk\xf8\xccg>\xfb)\x11\xd9\x01\x80\x8f}\xec\xe3\xebb\xbe^\xb7f=\xf8\xe0C\x00\x80\x9f\xfa\xa9\x9f>\xd8\xdb\xdb\xfbB\u06f6`\u00c1k\xabC\xcf\xe5\xc91n\\}\x1a\u02d3\xa3\x18\xe1[\r\xb1H\x04pN\x99\x06\xe2A\x81s.&c\xc2%\x83\x80{\xc1$\x14\xf4lBU?\xectJSJ\xa7\xb4\x91\x11\xdePJ\xe2\x00.\x19<\xb0\x82\xa2\xed_\xc9o\x96\u02ba5}\xdf\xc2@\x8a\x05hO<\xec\u04af4\xa1c\xf0/8\f\xf3\xe6n\u0415\a\x16\x8b\x8c`\xf9E\x9b\x1f\v\u06eaD\xb6\xa8\x86\xc1k\x9e\x9d\xa8|\xdd\xe7\x93\a\xf9P\xec:\x19\ud307\x8c\x9c\xea4Q|\xbe]ze\x89\xf8b\x17\x93\x9b\x1f \xe8\xb4\x1dz\xe0\x9f^}\xef\xb8\x11\x10\xb41\b\xc3\x15\x9e;U\xbe\x96\xa6\x95\xc1K\xc7v\x92\xbfF0\u02eaY/>\xe1\xec\xbe4v\v\xc5\xdb\xc7\x01k\xf8\xfb\xf8\n\x15\xf2\xe3tO8\xd7a\xb2q\x06\xe7\xef\xf9\x0e\xdd\xfc\xbcK/\x9c\x8d\xc1\xc1\xe11^}\xed\xb5\xef\xfbG\xff\xe8\u007f\xfay\x00\xf8\xc1\x1f\xfc\u063a\x98\xaf\u05ed]Dtt\xd7]w}\xfe\xe2\xc5\v`f0s1\xe8\x13\x1c\xbc\xf4,\xf6_z\x16\xc6\xd8,n\xf1>)\xe7\xbc\xf8<\x14r\x1e\xe4\x94\xc6\xe7M\xb0\xc1\x1d\u0d26WS\xa5f.\xc1r\x14+\x18\xab\xa1\x12\xd7\x1cU\xe1\xd7E \xc8\xf0\xb9\xf7\xa7\x88XV\xc1\xe1\xb2\x1e\x92\x14\x03O\xc9\xe3M\n\x02\"vY\xb0c\x9c`\x12\x06\x99+\xf3\xbf\x016-\b\xdd\xf1R\x8a\x84\xc8\xe0\a\xe3d\xe5\x1fJ\xb1C\b#\xa5<\xd5s\x88\x81\xca*b\xfeN\u007f~^\xe6\x0e\x9dD\xa9\x9a6\xc0C\xa3\x90\aF\xfc\xb1\n\t)\a\xbe\xbdv\xe5\xa7\u007f\x8dr\x97X1\x1b\x1b\xd1l\r\u060ai\x86\x13?\xe2 J\x10\xa7\xb3\x06\xbb\x90L\xf1\x14\x9d\t\u0639\xb2W\xf4z\x06\x8f\x98\xd8]\x87\xc1e\xa4\x12\"D\xcb\xd5\xd9\xdbTZx\xe6{\xae\xb8\xc1\xa2\xea\xd3\xf5\x1d\x8cmq\xe9\x81\x0fcs\xe7|\x8a\xac\x8b\u0670ll\xf7\xd2K/\xe3\v_\xf8\xd2/\x8a\u021d\x00p\xe5\u02b3\xb4.\xe6\xebuK\u05af\xfc\xca\u007fE\x00p\xe9\xd2\u017f\xb8\xef\xbew)\x9c\x12&\xfeD\x00\x1b\x8b\xa3\xd7^\xc4\xfe\x95'\xc0\xd6Vj\xf8H\x0f\x13x\x888\x888\xc0\xb9\x14\xa8\x9b\n:\x90\x94\x82\x1c\xe0\x05\xd3\t&'\x0ev\xa6V\xba\x15d1H\xa5\xf7#X\xb9\f\xba9v\x81\xc5\xe2O\xa3\xcb\xe5b(R\b\x8aV*L\xd1*\xa6D\x8b\x88o\xe7\x0e\xddvZ\xe0J\xd1\f\r\u00c4(C3\xe4jy+W\xcaI\xaa\x8bz\xd9\xd9S\xd1\b\x13%\u07d5\x15nf\x81\xfbG\x0f\x97\xf8\u0112\x00v\xe1t\x18:\xac_\x18H\xff\xa9 \xf6\x84\x8d\xc4v\x82&Z\xc8V\xf0\x04\x9d\x82w\xadZ\x13\x97\x11v\xe9\xe4B\x83kV~\xc5\xe81\xe0\xf3\xa0\xd8,\u00bc\x82\tv\xa9\xe2 .6\xad\xfc&\x89:\x1b\xa3\xb2\xa9\xb0\u01e5p\x8f\xb31\xfa1(\xdd\xf3\\\xec\xf4\xe2k\x1e;\xe0\xe1\\\x87\x9dsw\xe3\xc2\xfd\x0f\u00c7\xe1\u007f<\x1a6\xed\xc4^\xbb\xbe\x8f\x17^\xb8\xfa\xf0o\xfd\xe6?\xfeY\x00\xf8\xd1\x1f\xfd\xeb$\"\xebb\xbe^o\xff\xfa\xb9\x9f\xfb9\x01\x80_\xfa\xa5_|\xf5\xce;\xef|\xacm\xda\xec\xdd\x1dD\x14\x8b\xc3\x1b\xb8\xf1\xc2Sp\xf3\xe3\f\xc1 \xe3\xe6)\xbd\u073b\u0421\xbb\xc0\xb4\x90\xba\xdb\xf5\n\x85\x18'\xb0\xbd\xa0YxL\x0e\x9dJ\xe6\xe3\xb16\u05e4\xf1\x8e\xfa\x94\x8e\x90]\xe0\x97\xdf\xec\xe8_\xbd\x16I^\x1d2\u0491&\xaf\x16\xd4dA*\xbeV\xbb\xd0P\x05\x19qy\x8c\u03efYFW\xc1\xac\xd8DE\x9b\x1b\xd4\xf3\"T5\x16:\x1e\xebn\xe9\xf4\x83\a\x05\xf6\x8dv\xe2\xc1\xaa\xa0W/\x18ve\x98\xf7MN<\xc5&\xd9,|R\u050e\xa4\x88\xdc\xf4ZW\xaf\xad \xa3\xa4\x82=\x8c\x8d\xca^\x0f\xd9K\x05ZX\xa9Sv\x8e]\n\x9a\xc0\xa3\x1fK,\x11`esY\xb9FD\xf5\xe6\x01\xaa\xff,J\xfb\xe3\xe6D:\x87\xe9\xbb\x05\xect\x13\x17\xde\xf5\x1f\xa0\x9dn%\u07bb\x04\xdeU\xd3N\xdd\xd3O?\x83/\u007f\xe5+\u007f[D.>\xfa\xe8c\xfes\x9f\xfb\xb7f]\xcc\xd7\xebm_\x1f\xfa\xd0w)&j'\xcf\x186\xff\xfc\xfc\x9d\xe7\x035\x91**\xd7\xf5\xe7\x1e\xc3\xd1+/\xc04\xadv\xe0\xa5\xf1G\x14S{e\x1c\b<\xc8{eYt\xf1\x18\x1ce\xeb\xc1;\xc5i\x81\xb1\v\x8f\xc9A\x8f&J\xb7\x8b\a\xcf\xd7\xf4o\fO\xc7\xe5\u0411:\r:&\x8f\x82_>\xa80\x05$\x91\xeb\xd6\bH?\x14\xfc\f\xb1\xf3\xa285sI\xf8\xb9\x14\xafKB!\xb4\xa1\x9b,a\x1d\xf6\xa5(iX\x1d\xa9\xda`(%\xe2\x14\xf0\x0f\x8d\xb4\xd3\xe5F\x83<\f.C\xb2\xb9\xd3\xd7s\x1a#h\xac\xb8\x9b\x0e\x85@\xe8\x94O:\xfd\x0fW\x85U\x18\u02cf\x1a\x9c\xc8(\xc7\xe3\xb1\xcfg\x01\b\xd0\xcc=\xda\xc3\xc0s\x97b\xd0.\x999U~C\xa9\xe1\xc4B\xec\x15\xa1\xc2\xc8]\x8f\x12\xfd \"\x8a'T\xa6,\xff\x0f\xafD|\x8f3\xe7\xef\xc5\xd9K\x0f\xc2\xfb\x0e\xd9\x17\x18h\xda\xd6\\\xbb~\x03O<\xf1\xe4w\xfd\x93\u007f\xf2\xbf\xfe4\x00|\xe2\x13?,\xebb\xbe^\xb7d\xfd\xea\xaf\xfe\xb7-\x00\\\xbct\xf1s\x97.]\xd2b\xce\xf9\xcc\xcd\xc6`\xff\xc5gp\xe3\xca\x13`c\xab\x87(S\f3\xd4\"\u0781\x9c\x16\x00vJ\xe9\xe3h\x0eUL\xdd(\u061e\u0699C\xbb\xdf\x05N\xf4\xc0s|\x80qc\x88\x84\x14\xf6\xb0\xb4\xc2C\xce_\x84N\x03\x89cM-\xe3L\xab\xe8\xa2\"^,\xbc\xde\xe4\xa5\x12\xbeo{\x1c\u0516\xa8\x94\xf0\u02b4XH\xe5\xd8WBN\xe3\xfc\xbcS^\xa3d:\xa0\x10\xad\x0e\x15\x8bo\x1e1e\xd3\xf9`C\x90\xa9\x8a6\rC\x05r\x93\x1a\f\xd2Aj\xb3Tj#\xc9i\a#y\v\x10\xfe\x94\xfa\x9e\xb8\xfa%\xf5\x05\x95\x8fx,\xd2\x19W'\x95\xf9\u03dc\xdeO4vB\xc9>\xe8\xc4\x14f@Y\xe1)\xc3\x13\xc9\u0419\x11\x12\xfc\x85\xa2w\x8b\xcfP\x1f\t\xd80\x9c\xeb\xb0y\xe6<\xce\xdd\xf5\xbej`\"\x04\x80\x19M;\x91\xc7\x1f\u007f\x02\x9f\xff\xfc\xe7?%\"[\x00\xfco\xff\xf6\xffA\xebb\xbe^o\xfbz\xf7\xbb\x1fp\x00\xf0\xf7\xfe\xde\u007f\xf3\xdc\xed\xb7\x9f\xfb\xea\xe6\xe6\x14q,\x19\x8d\xb7\xba\x93C\xbc\xf9\xd4_\xa2;\xba\x0e\xd3\xd8|T%$\xc3\"}\x8b\xb8\xb9\xcb<j\x8f\x94\x0f\x19\xbdT*\x8c\xd3++\xa1=\xec\x83(\x87\u01a1\x84A\x8dM\xf4\xe4^]\x12\xcb\xd82:\xad8W6\xae\xa7\x15\x1fZ\xc1{\xd3\xd7\r\x83\xcbx\u02a0\xe4\xdf\xe2\x03\x8b$p\x8f\x03V\xae\x94\xb9\\\xa9J\x19~]~j*\xcf\n\xa4!c\u00c2\x12`\xc6`\xf8JI\xec\xa4\xddm\xf8\xb8\xd3\xc1\xa1HM\r\x94\x91Q\x83B\x1a\xc5F\fy\x8b\xe6{`t\x83\xdaO^\x863\x89\x91\xe6>\x0eX)am\xd10Ka\x16\xea}\xba\xa7J\xe8>\x9d\x98R\xfa7W \x8a\x14\x9b\\:qb\x10[\x18\xed\f\xbc\x87w^\a\x9f(\xf3R)\xcd\f\xc8Z\x9c9\u007f\x0f\xa6\xdbg\x83\xcd@\xaa\xf80\xb6\xa5\xfd\xfd\x03<\xfb\xecs\x9f\xfe\xb5_\xfb\x1f\x1f\x06\x80\x9f\xf9\x99\x9f\x95u1_\xaf\xb7}\xfd\xc2/\xfc\xa2\xbb\xed\xb6\xdb\xccd\xb2\xf9\xc4m\xb7\xdd\xf6\xff\xdd}\xf9.\xf4}/\u027c?@\x02\xaf=\xf6e\x1c\xbe\xf4,l3\t\xceq>\x1dU\xe3\xfb\xd2;\xf8\xbeW[\\\xef\x8b\xe3o-\x91/\xebP,\x90f.h\x0e\x1dh\u9aeeQd\xbcq\x8d\x0f\xbd\xe9$u\xbb4Zf\x8ax\f\xa1\x9a7\"7\xb9\xbb\xa5\xc6\xc8++\\\xaf\x05\x9b}\x10\x13\x85anb\xb0,\x94}\x81\"H\x19\xa1;&?\x024\xc8\xc8&\x82z H2\b`\xae\x03W\a\xf0svd\xa4\xbe\xe0\xca{\xa8\x0f\xf9|\xd5\xd9+A\xc4\x1c<X\x82\xfb\xe0\u0403e\xf8\x9e\x8c\xee\x9bTAF\x84\xa1\x9fM.\x8c\xa3\x9e4)\u03142S\u01c5T\xab\x18\x80R$\x03Qq\x02+1p\xfd\xbfY1\xd3)\x83)\xca\xc6$\xaa>\xa9\xf8s\x1a\x9c\x16\xc5\x10z\xe9\xb1{\xe7\xdd\xd8;\u007f\xafr\xe1#t\x13\u00a4A\x8cg\x9e}\x16\xaf\xbc\xf2\xea/\x8b\xc8\xc6\x1afY\xaf[\xb2\x88\b\xbf\xfe\xeb\xbfF\x00\xf0S?\xf5\x93\u007fy\xf1\u2965xob\"\x9a@\r\x85N^\xff:\xae_y\x02\xd2/a-g\xe7\xba8\f\r\xc5\\\\\x0f\xdfw\u06a5\x8fv\xd6T\b\x8f\x8a\x01\x97\x17\x98\xb9\xba\u5c53\x95;N\x06tD\t\x1d\x99\x06<\f9\x8c\x94\xc3\x1bP\x93\xffd\xb5\x8dEYOO\xeb\u042b\xfa\x19\x1c\v9t\xda\xe4\x94niB\xb0\xb1Y\x043\xad\xd8\xfe\x06j!\xb9\x01\xbd\xaf\x1c\x02\f`\xa5\xea\xba\xd1\xc8\xfb\xe5\xbf\x1b\x1c[\xa4\xd8\x00L\x1f\x95\xa7\x92\xb1\xf3Y\x98e\f\xf6\x85\xb8!\xc488._\xaf\xe4L%\f\x83\xa7Qdq\xa6\x8f\a\x89Ho\xb9Jf\x8aT\xc1\xcf)t[\n3.h\xf0\a\xa1\xf0\x81\b\xefk\x016 6A\xe5i*A\x9c\xe6\xd4r\x1a\xf6S\xe1\u0260\x11\x82yc(\xad\x99=A\x83\xa2\x8d\u00d9\xf3wb\xef\xfc]yK\x8a\xd0\f1\x88\rn\\\xbf\x81\xabW\xaf\xfe\xf0\xff\xfd\u007f\xfd\x9f\xbb\x00\xf0\xc5/\xfe\x11\xad\x8b\xf9z\xbd\xed\xeb\xe3\x1f\xff\x98\x03\x80\x1f\xf9\x91\x1f\xfb\xfc\xe5\xcbw}\xf5\xfc\xf9\xdb\xd1w\u02fe\xa4Uy\xd7\xe3\xfa\xb3\x8f\xa2?\xdeGc\x1b\x18\x04\xaaWY\f\xc5C|_\x99\x11U\x02\xf5\xd8\xed\xc6G\xa5\xe8\xdac\x92\x85\x9diA''\x95\xd7I*(\xa5wy\x18\xf4\xd1P\xc82R@\u0190\x02\x19\x83~G\xf3\u03a4\x1a\x8cR\xb4\x9c\xf5\x99\u007f\x1e\xfd[\x9a\x90\xc0\x13\xa7\xadTl<\xa3\x85\xfc\x1b\x01\x9aO\x83(d\x84:B\xb5\x1b\x19I\x18\x86\x16p\x8bYJ\xa2*\u018d11X:\x81\r\xb2\xfd\xa4\xac\x8c[aAK\xadh\x84+\u01bac\x9b#N\t|\xaeOP+\xe8Q\x18jK\xf5\xb3\x87\u035b\t\x9eB\x91M]=\x03\xc4\xca\xfd\xe6P\xd0\xd9d?\x18\xc4 \xf1\u0708\x94\xe6]\x12/\br\xb7\x1d\x87\xd7>\xd0:M#\x98\x9e\xd9\xc6\u0785{a'\x1bJS,n\"6\x16\xce{\xbc\xf0\xc2\vw\xbd\xf0\xc2\xd5\xef\x02\x80\xef\xfd\xde\xef\x97u1_\xaf\xb7}\xbd\xe7=\x0f\xc9#\x8f\xfc1\x11\xd1\xebw\xdcq\xfb\xffs\xee\xecYx\xef\vb\xb9>\xba7\xae<\x89\u064d7u\x10JE@3\xe7\aL1G\xa5(\xd2\xe0!\x1d\x01Z\x93qU\xc4H\xe1\xb5sl\x8e\xbd\x8a\x8a\x06Tp):6re\xfaN\x81O\xd3\xd8\xd9\xff4\xcd=U\x8a\xc8\xf4\xc5eDj*\x03)}`\xe7D\xb7\xc6f\xe6\xd0\x1e\xf5)\r\x88J\u067f\x94\xd4I\x19\x96\xbd\u0453\xc0J!<EQ\x996G*`\x87\xf8y^\x031RwN\x12R\x89\x94r\x88\xc0\xaf&\u039ctv\x01+\xf7\xab\xbf4\x19\xa3\xff!\xe7r\x96\x19Cc\xe3QZ\xf9\x93!\xec\x94\u007fPR\xe5T\xa1)\x18H\xd0\b\x10\xcb\x19R\x01g\u061c\b \x03b\xabPKpWK\x97\x8fJ\x1b\xe0\x9c\xf4\x14\xedv}\x14\xc6E\n\x14k\xf3b\f\xc1\xb4\f\xd7\b\xce\\\xb8\x17[g\xce\u00c5\x04\xa2\xf8:\x8cm\xe0\x9d\xe0\xd5W_C\xd7/\u007f>\xfel\xff\xee\xdf\xfd\x9bu1_\xaf\xb7\u007f}\xe4#\xdf#\x00\xf03\u007f\xeb?\xf9\u02fd\xbd\xbd\xeb\x8d1\x94\xe4\x99\xe1f\x9f\xddx\x1dG\xaf^\x85\x13\x0f\xb0Y\xc1\xbf\xd3\xc3\xe9\x1d\xc4wY}W\xe9\xb0\v\x81J\x10\xc2\xe815\x14\x1e\"\x8dm;V\xbbU\xc8\xd0]\xaf@/\xfa<8,\x83\x12\x12\xaf\xbb\xc4%\xe4\x14\xfa\x9fHE7\x8c\x83\xac\x92Yq\xca>TI\xe9\xb9W\xf6\x8aI\x83\xcf\xfc\x99\x9c\xe4\xfbC\xea\xdc\x18LR\x1f\bV>(m$\xcbPb\xbay\u05df\xbas\n\x19\x9e\u02c0\x9d\x17\x97\xc8\u012e<0\x91\xeaW*\x83\x83K,\xd82\u0632CQ\xa7!\x14\xb4B\x16\\9\xfdH\xc1<\x1a\v\xd2(0\x12\x80\t$\xa1|3\a\xf8#\x8a\u07b8\xf0K\xa6*\xfb4m\xac\x83\x81\x01\x11'\xd4=J\xffS\\!)\xa4c\r\xc1X\x02Y\x82\xa0\u01d9K\x97\xb1s\xf6N\xf5t\x8e\u015c\t\xcc\x16\xc4\x16\xf3\xf9\x1cO>\xf9\xd4GD\xa4\x01\x80\xcf}\xees\xebb\xbe^\xb7n}\xe8\xe1\x0f\xff\xdb\v\x17\xee|~oo\x17}\xdfgO\v\x01\xdcb\x86\xebW\x9e\x84[\xccA\u01aaA\u007f\xc1Z\xa0\xf4Pz8\xd7\u00fb\x1e+\x86\xaaT\f\xa0\xca\xce8P\xfe\xa2\xb8\x86\x9d\xa0=\u0283\xc5\x15\xf1G\x90\xf0\x93_\xc5P\xa4\xea\xe2\x8a?/\xa8\x8bc\xf6-\xabX\xb6\xa4\x0en\x98@A\x85\x95\x00\xb9\x82\xe5\xe2\xf2\xd00\xc1+.G\xd9}ckL55\xb0\x88\x1d\u0242\xab\x93DkH&BA\u9be3Mnd\x10\x85\x84\x9f(\xf6\xa2\xb7\u45a7\xe9\x04\xc6\u0520r\xcaah\xe8\xb78\xfc)e\xa4\x87\x8fT\xd9\xf45\x83M\x04\a\xa8\xab<\x19\u0122\x9aN\f\x12\x06\xf4Qz_@Q\xb2\x12*\x121s\xb5\xb5\xe0\xb09H\xe8V\x98\x04l\td\b\x04\x87\x8d\xdb\xce\xe0\u031d\x97al\x1b\xf2I\xf3\xa9\xc2X\x8b\xc5b\x81'\x9ex\xe2\xd2\x17\xbe\xf0\a\xef\x06\x80\xbf\xff\xf7\xff\xfbu1_\xaf\xb7\u007f=\xf3\u0313\xf1A8\xb9x\xf1\xe2\x1fmnm\xa6@\x80\xe8$\xd7-\x17\xb8\xf6\xdcc\xe8f'*\x87\xa6\u0324\b'\xe2\x80C\u01a3jW\u3728\xbb\xd3\xe8R\u0211\xd5\u0485\xa2\xe7\xb2_\xf7\xe4XM\x9ed\xa5\x98K\xea\x80\xe9\x14%\f\x8d1%\x86\xe1\b7+-C*x]*\xb3\xfa\xb5\x80Q\u022b\xcb`\x12\xfb\xf8\x82\xc5\"\xa7\x15lz\x8b\xc2.\xab\x88\xd1h\\\xdb\x10Y\xa2jf\xaa8x\xac\x87\x14:q\x1fT\xa2\x8a\xa3\x93\u01ea-\x02\x9dVvW\xdfJ\xa0\xa5R8\t\xaa\u079ep\x9a\xccW/:G\u0604\u00b9\xa0l\x00L\xf8\t\x9cT4\xcfT\u04a5(\xe2\x12\xc2\u028bBO#\x9d\u007f\x86Z\x8a\xcf#\x82p\b\xb1f=\x8cRCZ\xe4\x01\x90e\x9c\xbb|?\xa6[g\xe0\\\x9f\x8b9\x11\xd86Xv=^~\xf9\xe5\xc9g>\xf3\xd9O\xc5\xd7\xf8\x87\xdf\x04\xc1\x15\xebb\xfem\xbe\x1ex\xe0\xc1\xf4\xfeG?\xfa\xe1?\x9aN\xa7G\xc6\xc4\xcc\xc30\xc8\xf3\x1eG\xaf\\\xc5\xc9\x1b/\xa9\x10\x83\xb9\xa8)\x02\x1f\x06R\xca=\xef\xe1]\a\x1fb\xb6\xea\xb0\xc7\xfc\x9c\x97\xf1g\xe4<\xa8\xf3\u025f\x1b\"\xe0e\xe8\xd0\x175\xf7\x90\x12\xcd/\x17M\x00xK5\xcc\x18>]\xb8\x14\xd6|\xc4\u0560\x86R\x0e\x9e\xads\v\nf\x18\x8e\xa6H\xa8M\xd9\xc2\x00\x00 \x00IDAT8)\xbcXV\xeclO\u00cf\xdf\u00bf@\xc6\xe1\u007f9\xa5\xa9\x97B\xa8e\x96>\xd1\x15c\x88E3\xd37\x13\x95\x95n\x84\xbb=\x80\x97\x86.\x8b\xa5W\xfa)\xad\xf6MN\x1e#\u007f\x9d\xfcS\f\xc8\x18u\xe2\fp\x8a0\xa7\xcf\xe3\xa1\xe3b8\x19\x96\xfe\xfbz\x8f\xac\xc2\\\x91\xd1R\xe3\xff\xf9\xdeT>>\xc1\x18F\xc3\x046\x04j(\xcfTHp\xf6]\xef\xc6\xd6\xde\xed\x99\tC\xf1k[x\x0f,\x16K\xbc\xf0\xc2\xd5_\x14\xd1v\xe4\a~\xe0c\xebb\xbe^\xb7n\xfd\xec\xcf\xfe\xed/\xdc\u007f\xdf\xfd\xf3\xad\xadM\xb8\xbe\x17}(\x14W\\\x9e\x1c\xe0\xe0\x85'\x01\xef\xc0\x86S\"\x83\x88\x04?\xf3l\xbe\xe5]\x97\xa0\x96\x88G\xe6\u0388*\x0eu6\xb6\xaa\x8b#\xa0I\xf0\xed\xb1\x03\xf7z\x02PEcY@G\xf0\xd813\xaa\xb1z)\x03\u0339\xc4\ah\x1c+\x18\xfa\x81Se\xbaNE\x96\xe5[\xe1*CE\x10\x9d\xf2\xe7\xc5\xffK|\xa8\xe2\xce\xe5\x01\xb0\x9c\xe67&\x85\xd4\xdf\x15)J\xc9#\aI\xf1\xba\xf2\xbdG\xf6\x17\x19\x19gF\xd8'\xceB\xeaKwsc\x97\x15\x9az(\xe4\xc4\f\x84\x82\xee\r%\xd8#\xcd-\x80\"\x14e\xe0\u0452\x9c\xc5hpU\a\x11t\x81\x01C\x05\xc5QBRJct\xf0\u026d\x01Y*\xf2p\x1d\xb6\u039d\xc7\xf6\xd9\xf3`b\xddD\x8a\xae\xde6\xad\x1c\x1e\x1e\xe2\xd1G\x1f\xbd\xf8\xf9\xcf\u007f\xee\xaf}\xb3<\xdf\xebb\xfe\xef\xc1\xfa\xa7\xff\xf4\u007f\x8b7\xe3\v\x17/\xdc\xf9\xc5\xe9\xa4\xd5\xea\x1a\x8f\xad\x04t\xb3c\xbc\xf9\xcc\xd7\xe0\xbb\x05\x989\xfb\x92\xc7F,>\x04\u041b\xdd\xf7\u02d0+\xba\xda1V&V\xa5\xb1\xb6HV]\u01b4\xa2\x85Gs\uc495k\xeavob\xfe$\x83.x\xd4\xc1\xae<\xdd\xcbi8\xb5TM5%\xbea\u0465\xcap\xa3\xa8\xf39+S(\x19:\x0f\u07ac\x1b\x1f\xf9{\x1a\x9e.$'n\x14\x19\x11\xa7\xa62E\x05k\xf8\xa7v\xa16\xb3\x11\xb2\xc8\tH\xa74\xd3R\xf2UJ)S1\xad\xa6\xb7\x8a\x1b\x1a\xf9\xf3\xe1) u\xe2\x041\f\xd70<S\x1d\x18]\\\xeaZ\x80\x95\u00da\xa9\bo\x8e\xf7A\u2167\xaf%\xa9\xc9\xd0_\x9b\xde\xef&2X\x1a\x03\x9ed{\x00\x0e\xa6sv\xd2\u2d8b\xf7\xc0\xb6Suk\xa4lf\u01a6\xa1\x93\xd9\x1cG\xc7Gw\xfc\u025f|\xe5\xa7\xd7\xc5|\xbdn\xd9\xfa\u0407>\x94\xde\u007f\xf0\xbd\xef\xfd\xe2d2\u0566&\xa5\xa2\x13|\xdf\xe3\xe0\xe5+\x98_\u007f]\x99\x03\xc9\xf1\x90\xaa\"\x1d\xe5\xfd\xcew5v>\f/(\x9eB*\xe2\xdbb\x06g\xe4g\xb3\x13\xb43\x87\xf6\u0105\xa1\xe7M\x9c\xb8V\xa8\u007fr\xd3#\xbe\xd0\xcd\x0e\xffR8\rR\r\xe5$\xde{\x194\x11_\x17UFP\xdf\x10\xbcpj\xbd\xa3\x9b\xd7\u0081mp\xec\x8c\xc7\x10\x9c$\xc4q\x92=r\"\xa7<\u57be\xb5\xbeE\xc6@\x962TCd\x10\xed<\xdc\xedFH\xe8%\xbd4P\x01\xc50\x84\b\xce0\x96\x1b\x16\x8e\xf5c\x98\x907;2'\xc8E\x9c\x03\xee\x1e\xf9\xe6\x8cA\xe0g\xb5\x1b\xe4\xc6D\u007f\u007f\u0094\xbd]\x02\x8b\xa5\x1a\xf6\x8aR\x16\xcf\xdcu/\u068d\xcd\xc4j\x89\xd1sD\x1c\u063a\x1eW\xaf^\xfd\u8e98\xaf\xd7-[\xef\u007f\u007f>\t>\xf4\xde\xf7\xfe\xebK\x97.\xa1i,\xbc\xb8P\xd4\xf4A\x99\xdfx\x037^x*Q\xc4R\u0716\xe4\x87\"&O\x88\xef\xe1\xfb%\x10x\xe7\t\x8fL\x83\xa6\xa2\xc8\xfb\x12w\x8e\f\x11\x9f\x86\xa2\x14\x859\xc7N\xa9s\x812&\xb4*\nO\x1e\xd6TW{Yq\u05ebq\xf3\x9a\x98!#\x85\x8b\xc6an)7\x10\x1a\xc0\xf7rJm\x96Uo\x93\xb1#\xc3J=\x1c\x16w\x1a$,\x15\xc5\xea4\xdb\xdc\xc0\x8fO\xc3\xda\x12\x16\"T\xac\xf1\x15\xcf\xf2JDu\x9a\x1b\x1a\xa1\xb6>\xa7Qx\xa9d\xe0\xd0\xe0\x8ax\xd6\xee\xdc4\x16\xbce\x80-\x86oY?\x87\x00c\x18&\f\xe2I2\u03fb\xf6\uf29b\x8c:\x03Q\u0237\x8dM\xc8*\xbd_\xcd}\xa2\x10\x89\u00d1\xcd\x1b\xf5\x04\x8d3\xa4\b\xd1\x10\x11v\xee\xb8\x18\xc4C9\x10#\xfeLM;\xa57^\u007f\x13\x8f?\xfe\u0136\x88l\x03\x99l\xb0.\xe6\xeb\xf5\xb6\xae/\u007f\xf9K\x04\x00?\xf6\xd7?\xfdg\xf7\xde{\xf7\xd3M\xd3hDVR\xdd1\xe6\x87\xfb\xb8\xf6\xdcc qJ\x1b\xa3\x82\xf6\a\xa9\xfe\vx\x88\xef\xe0]W\xe0+EWKuzs\xec\xce5\xe5G\x87q\xa5l\x9ez\u581bE\xf0p\x19\xaad\xe8f\x92\x14Z%\x91\x8c\u06a7\x8eu\u01b2\x8aU\x8f\xd6\u027a\xf3\x8b\xf8\xf9P\x12Ie'\xff\x8d,\xba9\x12S\xa6\xf7\xe4\x14zI\x85o\x05\xe2\"*\x04WaP\xeb\xc7\xe0\xa2\xe1\xf5\x93\xe2@D\xd5\ub5caX(\xab\xf6\x03\xdf\u0211$\x16X\xd6n\xb8\x9dXl\xec\xb6\xd8>\xdb\xe2\xccn\x8b\xad-\x8b\xa6a\xb4\x86\u0476\x8c\xa6UL\xdb2\xea\xc0\x0fP\x96\xebG\x8f\x97\xb2s\xe7\x12#\xaf\xef\x17\x1f\u031c\r\v\x98\x00\t,\x96\xd8\x1c$\xff\xf3P\u0427{g\xd1n\x9d\xd1\xe6\"]\x1f=c\x18cp2\x9b\xe1\xf0\xf0\xf0\xae\xbf\xfa\xab\xbf\xf8\x00\x00\xfc\xf1\x1f\u007fi]\xcc\xd7\xeb\xed_\x1f\xfe\xf0G\x03M\x9b\xbaK\x97\xee\xfa\xbd\xe9t\xa2\x8f\xacw\xc1\x82\x9f\xd1\xcd\xe7\u063f\xfa4\x96\xfb\xaf\x83\xad\xc9).T\x14\xe4\xf8p\x06E\xa8s]H5/\xf3\u054ad\x9a\x92\xba\xe8\x8a.\xdd\xe70\t\ntE\xd3\x05VF\x80\n\x84Vs2\xa5\xd0\x12\xae\x18\xb7~\x03\xfc\xe9(u\x8c\xc3\u0541\x04i\xd0c\x16'\x848\x00\x1cD\xbfU\x1c\xc7R\x19:\xdc0d\x9c\xaaBc/\xa0\x1c\xfcRNQ\x1a\xe2\xff2,\xfcaX\xb8\xaa\x16- \xa14\a\xac\x8d\u0525\fz\xc6MR\xb6o\x86\xb9W\x1ba\xcd!\x8f~\xf0d\bm\xcb\xd8\xd80\xd887\xc5\xd6\xde\x14\xbb\x9b\rv\xce4\xd8\u07b6\xd8h\x19\x8d\x01\x8c%\xd8\t\xc3\x1a\x0e\x8c\u0161\xd9\x0eU\xb9\xaf\\\x9aq\x85W\x10\xa3\x12\x95\x88$A\xb6Oh,\x83\x1b\x02O,LksFn\x81\xbd{\xefa\xa6\x1b\xd8>\u007fIgH\xdeW\x87\x18f\x03\xef\x81\xfd\xfd\xfd3\xbf\xf3;\xff\xe2\x12\x00\xfc\xb3\u007f\xf6\xdbx\xea\xa9\xc7\xd7\xc5|\xbdn\xddz\xcf{\xde\xf3G\x0f<\xf0\x80>`\xde\x05\x99\xa6b\xc8'o\xbe\x82\x83\xaf?\acl\xa8\u0252\x1f\xcb\x02\x1fW\xec\\\xe1\x96\xca|\xab:\xfe\x8f\xb4\x99\x1e\x95?y\xea\"]\xc6\xd1c.e,\x9c\xe9\x9fS\u0462\x0f3\xccV \x90\\\xfch%\x03\x13\x99\xdf|\x1a\xb6]R\xe2X\v\x87p\x8ex\x13*,\vF\v\x1c\u0764 \xca*\x82Q\xc09\xaa\xf5\xa1J\x81+\x18\fr\x19\xc5F\xa3\xd7\xc93\xe0\rF\xc83\xc5FC#\xdb\x16\x8d\x1b\x98\xd1\n5h\xc0R\x1a3\xa3/60\x1a\x02YD\xb0\x960i\x18v\x8baw,l\xdb\xc0\xb0\x81\x99\x18\xd8M\x06\x1bE\xc4\r+\xef\x9b-\x83,\xaf\xe0\xe1U\xe6I:\xb5D\x99~hB0 \x06\x91\x84\xd9+\x81\f\x83\xdb\u0415\x97A\xd8\xe1g\xf0\xe2\xc1\xd6b\xe7\xceK*\xa4\xf3!\xa0%\u078fLX.\xbbn:\xdd\u061b\xcdN>\x01\x00?\xf1\x13?\xce\xefy\xcfC\xebb\xbe^o\xff\xfa\x87\xff\xf0\xd7\t\x00\xee\xbd\xf7\xdeG.^\xbc\xf8jc\x9bp\xe3\xea\x83\xc0\xd6\xe0\xe4\xfa\x1b\xd8\u007f\xf1Y\x18\u02e3]nv\xec\b\x959\xf0\xce!\xbe\xa0r\xdf\xe4\x1c.\xb5\xba3\xc2.)\xa9'\f\xeb\xa2\x19\x94\x84\x81\x95\u0400!B\xb4\xc2\x1d\xaf_\xaf\u0704\x0f]\xff\xd5X0\x03\r`\tg\x19\xae\x89\xafC\x8a\r\x81\x060\r\x12\x1bf\x15\xe2\x19\xf1\xfd\xa5\xba\x97EQP\xca\x19\xc4\xf0\u02d4\xbe&\xf1\xfa(;$~\\\x0fM\x85W7\x9b\xb1\xba^l)+\x93\x05\x1a\x83\u042bv\u007f\x15\x98!\xca\xef3k17\x13\x02\xefZ\xa0\t\x12\"c\xd5\x17h\xa3\x05\xb5\x16`\x1dp2\xb4\x93G\xfc\xfd\x13\xaa\xe3I9\xbbH\x94\xc4D\x81)\xb4\xa3\xe1\xda\x10)\x83\x85\x98@\r\ubc35\x8a\r,\f\xf5E@\xc6`\xe7\xf6\xf3`k\x03\xa6N\x15m\xd36\x8d\\\xbf~\x1dO=\xf5\xcc\x1e\x00\xfc\xf2/\xff\x1d\u007fxx}]\xcc\xd7\xeb\xed_?\xfe\xe3\x9f\x16\x00\xf8\u0527\xfe\xa3\x97\u03de\xbd\xed\xf7766re\x10\xbd\x1d\x96\xb3\x13\x1c\xbc|\x05\xdd\xecP]\xe9Rvc=K\f\xed9\xe0{H\x10\x11\x95\xb2\xe7\x8a\xee7r&\u03f0\x01\xe5\x80\xe5\x10t\x11y\xd3\xe4D\v\x91\x89l\x8a\xd3<ds\x95!\x19)\xd1R\xcf\xeah\x80\x03\x97\x90,\xa1\x86O\x84\xf5\xe5\xf5\ra\xb9\xc1\x81\x0f]\f\xe3\x88\u01bboz+\\y\x05W)\xe0eZ\x05\xb9K\u0590\xe4\xa1f\xec\xca]\xa3\\m\xcf\x04grO,\x85h\xaaJB*\x06\x895\a\xb5\xf4\xbf\xc1*]\x92\x86\x8a\xdb\u04f0\xad\xfaZ\x90!\xd8F=Px\u04c2\xa6&\x83Y\xcc0M\x03\xbb5\x85\u0759\xc2\xd8&\xd9\u0732!\x98\x86\xd57%\xe5\xf7\x15\x82\xa9(\xe5/\xb3EC\xb2\x90@\n\xd1\x18\x85\u0344U\xb6\xdfj\xd7\u03e7x\xb0\x8b\b\x8c\xb1\u063e\xedv\xd8v2\xfa\xb3\xc589\x10\xbdWD\xee\x05\x80\xa7\x9f~\x86\xd7\xc5|\xbd\xde\xf6\xf5\xe0\x83\xef\v\xdd\x12\xcd.^\xbc\xf4\xe5\xbb\xef\xbe\f\xef\x1c(\x8e\x86\xc2\xc4~\xff\xe5+8|\xed%\u0626\xa9\x8e\xcb5\xc3!\n3\x02\xd4\xe2\xba\x11\xbe7\xad\x1e\xc1\xa5\xd8\x1cbw\x9e\xe8\x8bH\x1e`\xc9\xe40\xc0\ae\x9ed\xecH\x890\xda[\x8f\xa5\xe3\xe4\xd7P3+F\x91!\xa9s%\x84\x00\xdad\u041e\x05My\x80\x9d\xbfU\xd1\x1e\xc3$V\xedmW\x93\xe6J\x04x\x10\xedS\x86'\x93@,\xc1[\x86\xb7\xe1\xf5\x18(\\a\x8aN\x1d\x94\x8a\x9a0\xadz\xbc\x9fB6\x1c\x8d\x8e\x1b\xaa\x80\xb0z]\x87\u078a\xb1(\x9b\xa9\x01m\x1b\xc0\xd2\xc0g\x8c`\xda\x06vw\x03vk\x03\xb6\x9d\x80m\x03f\x03\xd3\x18\xd8\u05a4\xa1<\x06\x8c\xa6\x12\xaa\x12\x91\xaa[\x8fX\xb9@\x87\x9e\x86\xb5\x90SC\x85\xdch\xe8p\xa9\x1b\x9ca\xc2\xc6\xd6.\x9av\xa3\n\xc8\u0223!\xa2\xae\xebqrt|\xf6\xd5W_>\a\x00\x8f=\xfe\u013a3_\xaf[\xb3\xbe\xf6\xb5\xaf\x12\x00\xdcs\xcf\xe5G\xee\xba\ubbbe,\xb0\"\x0eD\x8c\xfdW_\xc0\xe1+\u03e3i\x9a\xe0c\x11\x1e\x8eA\u01d6\xb2\x15\xc5\xc3E\x9f\x8c\x02\xfa .`\x05\xa2\xd5\xe3\xfc \u0693nR\xefF}\xb4G>Y\x04+_|\xb5\u03d5S\x99}eU\x8f\xe2\x92\u0254\xb1\xb9\xa7C\xba\u026e\x85m\xb9:\xfa\v\x8d\x9f\x16\b7{\xd1R\xc3\xff\x03\xa3/*w4:E\x84\x946\xa7|\r\x1a&lX\xc6dj\xd0L\x18\xb6\xe54|\xf4\xa1\xb8\xa7\xa4 \x1a\xbb\xe8\xe3q|+\xe3\x8f\x11\xf5\xd1\xc0\xb9<'%\x05\xac\xdcX\x02m\x19\xd0\x06\xe3\x14!+hja6\x1b\x98I\x8bf2E\xd3Na\xdb\x16Mka\f\xd73\x0f*7\xf6\xe8\x8cX\x9b~I\xc8\x00U\u02a3r\u02a9\xd5\u007f[Z\xd5P\xe0\x9e\x97,\x18\x81\xc0\xb4-\xectR\x04GSv\xdc\x14\b\x11\xe1\xe5W^Y\xfe\xee\xef\xfe\xee\x12\x00\xbe\xf4\x0e2Z\xd6\xc5\xfc\u07f3\xf5\x81\x0f|\x87\x00\xc0/\xfc\xc2/]\xdd\xdd\xdd\xfd\xc3\xcd\xcd)\xbc\x12\xcaCm ,\x0e\xf7\xb1\xff\xea\x15\xf8\xff\x9f\xbd7\x8f\xb7\xac*\xafE\xc7\xf7\u0379\xd6\xde\xfbtTA!\x14\x05\x04J\x1a\xa1 \x16\x02\u04bdkh\x05\x1b\x82\x1a\x8d\xb9\x11\x8d\x06\tQ\xaf\xbe\xe73\xd7\x17\xafz\x93\x97\u0118\xe4&\xf16&1/O\xa3\x98\\rc\xf3\x8c\x82J\xb0\uf0c8 J+}[\rU\xa7\xdf\u035as~\xf7\x8f9\xe7Zs\xad\xbdNU\xde\xfd\x03\xcab\xcf\xdf\xef\xfc\xaa\xea\xd49\xbb_c~s|\xe3\x1b\xc3\f\xfdh\u007f\xfa\xf1\x16$\xa6G>$\xd7\x06\xaf\xe8\xaa\x11Jc\xd2i\x8c\x01\xb6\xd4*\xd3v\xb8\x93\x8a\x86AK\x15\x9c\x0e5\xa1\xc9SW\x8d2i&a\xb4\x13\x1d5\xb9_\xa5\xbc \xe4\aht\xa6\x15\xba\x8a\u0419Q\xe8L+hE\x15\x17\xbd\x06\xc3R\u056bM\x93\u0644\x8a\x925\x0e\x13\xf1\xb5^\x83\u0268\xf9e\x85\xc6q\x87\x19\u074c\x90+\x82\xee1\xb29\x8d\xbc\xc3\xc8\x14\x95\x94QJ\xbbT\x81\r\xb2\x16\x19^\xc7\xf5V\xdf\xf5&]EcL\x98\xe2\x00\xa4=\x06\xcd(\u007f2h\v\xe0p\xfeT\x81N\xa0BXC\xe9\x0e\xb2\xbc\x87\xac\xd3\x01)\xed\xfd[h\xfc\xb5\xabT3TV\xfa\xc4\xec\x11Ny\x1f\x16\xad\x19\x943\x90q\u057f\x89\xb2D\xa1\xaa\x91\x1a\xab|\xeb\x13\x86T\xd6I\x9e\x8e\x84&h\xa5\xdc\x1a\x8eF|\xff\xfd\xf73\x00\xec\u0639s\x02\xe6\x93\xf5\u052d#\x8e8\\\x13\xd1#\xb3\xb3\xb37\x1cu\xd4Q0\xa3\x91Kydk\n\xcc?z/\x06\v;\xa1X\xfb\x12&U\x12D\x02@\xa8\x94qE\x99b*\xe1\x1a\xaf\x9c\x93d\x99\x9a\xe2\xa4}n?\x1eiym\x81t{\xc4|\"ii\x83\xee\xb5\x15\x8c\x95\xcf5sP]Li\xe8\x0342M\xd0\x04\xe4\x1d\x86\x9e\xd3P\x1d\xdf@s%\x8d\x91p4h\xabZe\x1c\xf2ZmZb\xe5\xdc\xd0\xc0K\x8b\x9fJ\xc2\xd9g\x8a\xd0U\x80V\x04\xd6\x04\x9e\u0460\xf5\x19t\x87\x91k\x86V\xa1R\xe6\u051aaO\xc6_\xe3\xc8\x1d\x1d\"\xd3MZ\xf6\xd0\x12\x90\xa0*\xc92\xf2\u0291i\x05\xear\xcdc\xb1^\xea\x87[\xeb\x06\xc0-\x15\x88\nJu\xc1y\xc7\xef\f@K\xb0E\x9d\xb6\x8a\xcf\u0445MKE3\xad\x9c|`\aPV\xe2>\x1d\xd1\x17&\"\x95\x04\xd79\x81\x90\x82\xeat\xab\t\xe0z\xe6(\x0f\x87#\x1cr\u0221\xcf9\xe6\xd8cO\x04\x80\x13O<\x91&`>YO\xd9z\xc3\x1b\xde@\x00p\xcc1\xc7\u0733n\xdd:\x80\xa0D\x9cP\x19@\xc0\xd8\xf5\xf8\xfdX\x99\u07c6,\xefTC\x87\x89\xee<\x1e\u0465\xf4\x96\xb65\x99\"\xb5\x8d\xaaK+\xea\xd6\x12\u071b\xd3\xf5.\x02\x9b\x932\x84\xbaV\xd1\u05fa\x9aT\x13z\x8f_\xe6k_g5W\x12\x02\xb2,4\xecf\x14\xb8\xabj\xb9\x11\xaa\xa7\xa0\xa6T5\x04\xd3\x12j\xbc7xlS+J\xed\x90\"\xf5\x83K\xa4\v$\xc5s\xffxY\x13::\xd2\x04\xf0^#\xd3\njZA\xf5\x14t\x06t\xb4\x0fapLp\u068f\xd3\xd7\x01y\xcf\xc3U\xe5\xc9E*\x8bc\xec\u1d4d\xbd\x06\xa5\x00\xce\x18\xd4U~\u0693\x9atT\x83\xaa\x12\x01r\xf2\xd59\xa7\x16\x02\x04h\rR:\x8c\xf3S\x12#\xc7`j\x8e\xf4'v2\x01\xb8UG\x81s5v\x1ap\xceS\x85Rg\t}\xea\x96R\xe0\xbc\xeb\x1fZ\f\x9c\x8e\xb1\x89DT\x14\x85\x9d\x9d\x9b\xa5^o\xea(\x00\x98\x9b\x9b\x9b\x80\xf9d=uk\u077a\x03\x1c\x00\x9cw\xde/\xdc\xc1\u0337\xe7y\x0eg\v\x11g|\xe3Gi,\xee|\f\xbb\xb7=\xe8\xa5cTM\xdd\u0544\x89R\xc9\x1a\x05\xce'\x11\x89\tm\xa7\x14\x8c\u0491Lj\x16\xeb5\xee 6\x16IU\xe0\xe4\x1d\x15\xa9\x15\x1c\xc7i\x1a)9\x01\x19s\u05d6\xf6\f\xe5\u06a4\x8f\xe7vuF\xe0\x0e\x81g\x94\x97\u01e5A\u011a\xa0\xa6\x95\a\xa9\xb4\xa1\xb8FSQ\u068e\x0e-\x9e\\5\xb9dT\x0fQ5\x06Z{\x96\xe4\x1b\u00e4\x80NF\xd0\u0468J3h\x86A9\x81\t\xe0\xf085\xc3\xd3/Lp\n\xb0:q*DK?\xa39\xfa\xb4\xc6ND\x8d\u05d6\x92\x97\x93\x13\x9e\x9a\xa7\x14\xa8\xc3\xe1\x90Q\x97s\x96\xbe)\xb1\x9bA\x04\xea2D\xa1\x8cx\xf3AQ\x1cr\xf0\xc2\u0670T\x1f\xb5\xbc\xfe\x8cr\xa8\x88\x99\xa02\xdf\xf8\x14\xaa\xac\x1f\xc4:\xd8d\xb2\x13hX\x00K\xa8\xcc\xf3^5\x80\x84T\xc1\xe4\xc7\xfe\xad1\x18\r}\xf8\xeah4\x9a\xd0,\x93\xf5\u052ds\xce9\xc7\x01\xc0)\xa7\x9cv\xffA\a\x1d\xf4\xc0\xec\xcc\f\x8c1\u039a\"8!\x12\xcch\x88\xed\x0f\u078ea\u007f\x1e*\xcb\u00a7%H\xbe\\\xe5']\x06\x068\a\x17\xc2+\x90D\xb6\x8d\x83\x9c\xd4t\xd4\xf5\u03a4\xd7\x00\xebL!\xef0\xf2\x8e\x1f\u91a2\xbaN\xba\xea?%\xf2f\xaa\xf3\xc92>u\xb9&\xa9\x90L\x161\x03:\xf3\x928\x9a\u0460\x9e*G\xe3\xd3\xdf\xd5S\x8c\u038cB\x96yN\u0595\xd4\u0178;\xc9\xda>-\rZ\x02i;\x93jz\u9997U\u0515g\x9a\xa1\x94\x97\xfa0\x13\xb8\xcb@O\x87\x14\x1f@r\x0f\x8c\xc4\x04M\x9e*\"\xf2rO\xa7\xa9\xee\xf9\xbe'=\xa5\x94n\r{5*\x88'\x18\xad\xbd\x82\x85\xbb\xcaS,\x94\x06\x83H\t\x98\xfe-Hc\x00}u\x9e\u6002Q\x99c\xc5Sa\u0512K\xf2z5\xe8'o\xc9\xe2)\x16Q(\xbb\x9eN\x04\xce\xf9l\xd0\xd42\xb7\xa2p<\xedb\x84\xa0:\xbd$\x90C\xea\xc2\"\x11\xb2\u01a2(F\x02\x00\xa6(&`>YO\xdd:\u3333\u4aab~C\x11\xd1\u02a6M\x9b\xee:l\xd3a0\u0188\xb3\x06\u058c\xe0\x9c\x05k\x8d\xc7\xef\xfd!\x16w=\x06\xdd\xe9\xc2\x11`\x93(\xb3\x94\xe2\xf0 \xee\x12C\xa2\xb5\x9cI\xea\xf6\xb3c\xf0!^>\x96k\xdf\xc0\u02fb\xaa\xa4\x0f\\\x18\x84\x19\x93\xc6Q\u04c0\xbc\xc1?\xb7\xc8\xeeh\rV_\xe0\xc7\xc8YyP\xe4Y\xed'-\x93\xe9\u0258\x80\u011a\x90\xcfhtr\x86\xd6\x04\xd1T\x8d\x9c'\r`\u0683\x9aE\x92>\xa04\x8d\xbbHZ&\xe5+\xc0\x12\xf6\x01\xc4Y^\xa90\xa8C\xa0\x19\xe5\xb9\xe18\x80\xc5\x04\xf4\xfc\xf7\x88\xc3\xfb\xe7\x82\x12I\x13D\xb7\xbc\n\xb2vw\x81\xd0\xf4\xea\x19\u007fK\\\xb2)\xaa\x0eC\xcdy\xae\u0725\x80\x99(^\xa89\xd1+~\x03\xa7\x8c\xc0\x91\xdf&\xa0\xa3\b:\xba+&'\x99\xca\x06(\xbc\a\xa1\x8a\x8e\xbf\xc8\xda\xd36\xfe\xbd\x8c6\xb9\xa86\x11\xa9'\xa1\xc6\x13\xd1\xc8:Xbd\u0769R\xcbN\xa9\xe82\xa6T\x85\xd4#\xff#n\x02\xe6\x93\xf5\u052e#\x8f<\xd2\x01\xc0\xb1\xc7\x1e\xfb/Z\xeb\x95,\x8c\x83\xfa\xe1\x1f\vf\x85\u015d\x8fa\xfb\x83\xb7\xc3:\x03\xce\x14(\v\xfa\xdc\xd8LK\xa4g^\xa2hC\xac\x9c\xad\u05ffM[\xd2\x16 M\x1cR\xbdicO\x81\xa7\xb5\a\x84\x10\xc2.k\x17\x8d\xb5\xbf\x8c+\xa6\xa5\xb5:o\xdeH\xa4\x05\xb4\"\xcf\xefv\xa8.QoT\xa6\xdcc\xa8)\xafl\x81\"X\x15\u031b\x90\xd2Bk\xf9\x9cP\xc3tP\xd6d\x9fR\x18\r4/\x90\x11\xb2\x8e\xaf\xca\x19!\xfalZ\x81z\xaa\x9a\xf0\f\x0f\x96;\f\xee( $F\x95U\xb6\n\x03F\rm\xfd\xf8\xabKk\x9cp$y.u\u0263V\xecO8=\x06\x05\xae\x9c\x92\xa8\xc2\xf4P4.\xa4\xf1o\x06u\xd9[\xd4R\x10\xa5\xc0\xf7?)\x9eLP\xc9\x05k\x9f5\xf6\xef\a8|V\u00c0\x10$-\"\xa8Rf\xc1\xf7c$\xfa\xbd\x130\xb4\x82\u0551\x83\x03\x81\xb3,\xf9]i|\xac\xbc1\x1c1=\xed\xd7\xf4\x04\u031f\xa1\xeb\xcc3\xbd\r\xf3\x9b\xdf\xfc\x96\x1f\xaf_\u007f\xe0\x83\xddn7T-\x0eb\x8d\u709d\xc5\x03\xb7}\r\xfd\xe5\x1d\xc8z=pF\x90\x8e\x82\xcb\bN{\x1f\x10\x1f\xfb\x85r\x04\xdaK\x14M9\xfe\\\xa75\xc6\xf5\xe0m\x93\x98\x1c\x9ax4\xa7\xc0=\x0e2\xc0\xa4:o\xd8\xd9\x12\u045e ~\x8f\xf5qZ\u1ae0\x85\xe6\x1e\x033\x1e\xfc\xaa\r\"5\x8f\rn}\x99o4R\xee\x83\x16\x9c\n\x13\xa2\xdc\u019f7\x06i\xa8v\xa6G\xb3!\u062c<\u02de\x02\x00\xc9\b\xbaC\u0202\xf2\x8e\x18\xa0@e\xa0\x1e\xcf\xe9\xa1J\x11\\\x8fa\b\xb0\xae\xbe)\xfaf\xe8\xb8\u007f\xb8\xb4\x9e[\xc6#D\x9b\x9a\x17\x9f\f\xe77\x1a\xee\x12hN\x039\x97\xbf(\x8d\x91\u007f\xa1\x9a\x8d|\xf5w\x11\xff{\xe1I\x92?\x02\xfa\xd70\xce>p\xdd\n\xd8KE\xeb\xd6\xc9\x16\xe13\x8a\xd8@\x0f\x95x\x00\xef\xf8\x15\xef\xdcY\xa0?tX\xec[\x14\xd6\u007f\x86I\xe9F\x82\x91\xff\U000948cf]t\xd6w\u775bT\xe6\x93\xf5\x14\xaf\xf3\u03ff0\xba(\xfed\xfd\xfau?\u0770a\x03\xac\xb5\xe2]\xf7\x83\"\x85\x15\x9e|\xf8n,l\xbf\xcf\x1b\xf8+\xf6\x13\x99D\xb0\f\xd8\f\xb0\xba2w\x92\xc0=W\x9as\xc1\xda\x01\fI\x85\x9aL\xceHP\x88P\xa0:hFA\xe5\fR\xf1\x18\xbd7\xb6\xb6\x9c)\u017f\u05a7U\x82\xdeXi\x02g\x04L)\xa0\xa3\xead1\u01951\x04?\x19*]*9WI\xc3\x15\x924\xf9\xba\xfa\x1c5\x0f\x1bYc\u04d1f\x0f\x00\xa1\x19\x9c\xb3w\xfe\x8bw\xa1}\xf5K\x19\x97A\x105\x13*\b\\N0\x8a`\u04f4\xa1\x14`\xa9\xfd\xddi\xdb\xf4Z\u01cc\u04aa<#p\x87\x81\x19\r\x9aR\xa8\xfb\xa47NQ2~\x88+\x1fZ9\xe0\x13\xdfW\xff\x99\x92`(\u6e1a\ue31f\x1b\x0f\u8f8fc\xadC!\x82\u00b9Z\xfb\xa44\xe5\x92J\x8e(\"0\xd6ayP`a\xa5\xc0\xa8\xf0\tMD\xde;\xc67\xfc%\t\x88\x0e-Sq\x12\xa8\x97,\\O\x135\xcbd=\xb5+\xfd\u031dx\xe2\t;gg\xe7 \"\xe4-C\xfd\\=+\x85a\u007f\x05\x0f\xff\u4ef0v\b\xd6Y\xa9.\x88\xb2A\x973\xac\xe6Ro]F\xd1%\xdaqjuR\x94\x1aE[ce\xb9\xb27\xa5)\x055\xa3\xa03\xafn\x88\x95W\x1d`\xd0N\u153a\xe0\xbd\x1bps\x1cl\xe9\x84\nW%\xb7/T\x9bq\xa9\xf5\"\x15\x83\xa75(\xe3j\u011f\xd1Ry\x03k\xfd\x8d\xd6<Q\x8c\xd3,\xa2}\u056b9\xe8\xa5\x15\x01S\f\xf4T\xf3e\xadQX\x96\t\x83\x8c`y\x8di\u073d`P\x05a\x82f\xeef\xfa\xa8\x15\x03y\xe6O74\xa7\xab\xb1\xfd\xe6)\x85ZB\xa4\xa4\xa5E\xd0\t\x05D\xb0\x9appp\xe4`\x15\xc1\xe4T:E:\x9d\x18\x8cY\x813\x0e\x16\x80!\xc0\xd80\xa1\x1cA\xbc\xe4\xcc\xfdk\xe1\x9c`0tXZ\xb5XZ\xb506\x1e4%\xc4\xd4E)dj\x17\xe0\xdf\\fR\xd6YdZ\xef\xda\xcb\x1b8\x01\xf3\xc9z*\xf8\xf3#~\xa0\x14\x17Z\xab\xa4j\xb1 Rp\xce\xe2\xf1{\u007f\x84\xfe\xfc\x0e0\x14\xb8\x1c\xfc\xf1U\x95U\x84\xd14\xc3LQHm\tIDh6\xbb\x1aR@\u051b~5\xea3p\xcf>4\x83\xa0f4\xf2)\x15T#\t\xf0T\xa7\xe6\x86lN*\x15H\v\xc5\xd36d\u009a\xc19{\xf9\\\x87\xf7\x98\x19\x9dn\x16\x04\x01\xf7\x18\u064c\xaa,\xb7\xa3\xbae\xec\xfe\xd6\xf2i,\xdd\xd3\xf7t|\x800Aw\x152\xe5\a\xa9\x88\b\x92\x93\xaf\x80U5);v\x86\x10\u007f\xa5\xbb.\xc3i\xef\xc4S\x029\xd57\u023dGUS\xe3tB5\x1f.\xad\x83zeV\x83:\x9c\xf0\u048d\xd3F\xe3\xce\xd6\x1cD\u04be\xdav\xe2`\xc5\xc1\x88\xf8\u07ae\x02l\x87\xe1\xf2\xe0K\u00fe\xf9\u8303-\x1c\x9c\x15\x18\b\xac\b\xcc\xc8a4t\xb0&j\xca\x05\xd6\n\x8c\x15\f\x06\x0e\x8b+\x06\v+\x06\xab\x03\v\x91@\xb7\u01e0\n88[T\xba$\xaaNP\xd69\xe9t;\u0635{\xd7\x03\xfdA\xff\x16\x00\u063e}\xbb\x9b\x80\xf9d=m\xeb\xdcs\u03fbijjzg\xa7\xd3A)7ta\u0519\xbd-\xee\x8e{o\v\x12=F\x9a,L\xd6\a\u43ba\x04\xa3\xf7\xe0\xf3]K\x9f\x97\x96\x01\xa2\xca\xdd\xce{\x88$\x84j\x87\x90\xcd)\xe8\x8e\x0f\xfeu\x8d\xea\x97\x1a\xb7O\x89- \x89\xec\x85b\t:\xe4\xdc7\u0290S\xf0\xcfn\x199\x97\xf6\x84#\u0584,\xd0Aec\x98\x81\xf1\x12to\xdc\xfe\x9e+c\u02bd\u05ca\x8a\xaf?\u00df\nr\xaei\xb5\xdb6\r\x06!\xeby\x8e?\xea\xf7K\x1fo^\xab\x12o\xb6\x8eS9\u0478\u007f\xbc\x1f\xcca\u042c\x02\xa6\xfdI!\x86HS\xb2\x11Hi\xd4YW\xec\xa4\xf3_\xf1?\x1c\x93?\xf9\xc1\xf7s\xac8\xff\xfeG\x13\xb1\x98\x02d\x1c\xc4\xf8\x11|'\x9e\x901\x01\xe0\v#\x18\x0e-F\x85Aa\x1c\x8c\x15\x14F\xb0\xb2j\xb0\xb4R\xa0?p0\xae\x1a\xed\x0f\x01\xb9\xfe\xb1:\a[\x8cJs8F\x95\xc0%\u21080\u055b\xeao\u077a\xb5\x0f\x00\x87\x1f~\xf8\x843\x9f\xac\xa7o\x1du\xd4\xe6\x1b7m:l\xc7\xdc\xdc\x01\xb0\xd6W\xd5\xe2,\x98\x00\x9de\x18\xae.\xe1\xf1\x9f\xde\x02\x16\v\u036aVF\xb1\x03\xf4\u04075\xdb\xcc\xf3\u65774j\xe0\"\u0524F\xa4V\xa9I\xfcD\xaa\xc47<\xdc\x17Okd3\n\u0228\xd6\xe8\xaa\xe1\f\x8d\xfbp\xcbX5\x99\xd6\xc2\xe1\xd7\x19\xe0\x8c@\n\xbe2\xe7\x16\xf0M\x1c\x1e\x9b@\xcc\xe4\x95-\xd2\xe3\xaa\u008d\x8a\x8a1\u007f,\u00b8\xba\x86Z\xcf\x0f)\xb0\x11\x13\xb2\x0eA\xa9\xa0\x9bf\x02z\xec\xf9}\x1e\xefC\xa4\xb5\xb3\xf8\x97\x14\xbd\x8c\u045b\xd3\xe8L)t\x82>=\x866H\x8b\x87\x0e\xed\xf5TQ\x1f\xb5\xcd4AM\xb1\xaf\xca3\xaeM\xbf\x8fW\xf4\xf5\x9eE\x9bs\xbb\x930\x18\x95y\xf9eMJn\x05\u0537\x90\x91\x85\x14\x0e\x12\xaa\xf1H\xa1\x18\xe5i@\n!\xcc\u00d1\xc3p\xe80\x1a9\x8c\n\x87\xfe\xd0a0r\xb0\x12\xde\u007f\x92@[\x95.\xbb~\xc3q\x16f\xb0Z\xf5\x17\xe2)\t\x028G\x04\xe0\xd0C\x0f\xd5\xcf\u007f\xfeY\x1a\x00\xb6n\xdd:\x01\xf3\xc9z\xea\xd7\xea\xeaR\u025fo\xde|\xf4\xad\xddn\xb7\xf4\x87vA/\xabX\xc1Z\x83\xf9m\x0fb\xb0\xb2\ryG#SA\xcd!\x04v\x04e\x04\xd9\xd0_\xfdN\xc5\xea\u070d\xe5\x97Q\xb3\t\x88(WKR\x85\xd8?\x1e!\n\xd3\xfbA/\xad\b\xd9\\\x06\xdd\xf5\xd5y=\x88\x81\u01a3gbe-\xcd\u071b\x06\xe5C\x04\xce\xd9Osj?%X\xaf\x14\xc7C\x96K\x15\rR\xab^\x82\xe91l\x90\xff\xf9F\xf1\u06aa\x90\xf4d\x10gU\x05kW\xc8\x1c\x1c\x10uTph\x02M\xd79\xe9\xb5n!\x16\x9b\x19y\a\xc8|Z!\xd7\u0790K\x01Aw\x1e\xbcT\x80\x16E\v\xb5l\x9a\t\x81\x15$\x9dY7\xf0\xe4=\xae\x01\xb4\xb4%\x12\u057a\xa6\x91\xb0\xa1\xb1S\x013Aw\xbd<\x95\t\xd0\xca\a\x99\xa0o\x81\xbe\x81+\x9c7\u010a\x95\xbe\x00\x8e\x04\x96\x91\x18i\x01\xd6\t\x06#A\u007f\xe8\xd0\x1f8\x8c\x8c\v\x1e\xe7^\xd6\xc9YH5\xe2\xaa\x1f\xa0\x14C\xac\xc1p1\x04N4\xe4\x87\xceY\xd7\xebu\xe1\x9c\xdc\x0f\xe0^_\x99o\x9ap\xe6\x93\xf5\u052f\xa9\xa9\xd9\xf2\xef[\xb7n\xfd\xfa\xd4TO\xb4\u05be\xba\x15\a'\x16\xcc\f\xa52,=\xf9\x04\xb6=r;\xf4\\\x86,gh\x95\x00\xad\x13pa\xc1C\vK\x0eF\t\f\xd5u\xbbcQo\xa9~;\xb5\xa4\xd5\x04d\x1e\x10kV,\"\xd0S\f\xbd>\x87\xed\xb2\xd7GG:\xa6v\x1f\xd2N\x8d\a\xb4\x8a\xfe&e\x85\xa7\x025\xa0B\xfa\x8c\xa65=KJxJ\x9bg%\xb5OP=\x05\x9aU\xde\xe8JUz\xfc\xb6\a\xd4v\u016fE\xc2pF\xd0S\f\xa5\xa9<I\xc4\xe9\xd4:\xf7\xd3\xc2S40Xk\x82\x9e\t\x92O\xae\u0326\xa2\x13\xa1$\x00M\x8d\x8a|L\x16\x9aL\x8ef\x9a\xc0\xb3\x9e+\aS\xddL\xad\xa5\xd3Y\u007f\xc8U\xd5-\x8d\x82_qP\x181\x81!\xc8\bPp\x80q\x80\xf1\x9f\xb1\xd2R\"d}\x9a\xa8\xa7\a\u00a0\x92\u007f\x8e\xd6\t\xac\x00.9m(\xe5\x03+\x94R\x89j&l\x80Z\xc3\x16C\xf4wm\xf3\xb2HR5%\x901F\xe6\xe6\x0e\xc0`0\xb8\x8b\x88\x16^\xf4\xa2\x17\xd1s\x9e\xb3e\x02\xe6\x93\xf5\xf4\xae\x8b.\xba\xf0[\x9b6m\xea\xfbJ#L\xb39\u007f\x06\xd5Y\x86\xc1\xd2\x02\xb6?t7\xa8\xe7)\x0f\xa5\xfdD\xa1\x8b`\x12\x02\x9a\xc5:X\x16/\x0f#i\xaf\xeej\xff\xac\x1aK^\x06\x16\xbc<\x12<\x90\u0130.\x9bUP3\x1aN\a\u007f\x91\xf0\xe5\xe5\x80M0\xa3J\xcb^\xa59\u051a}\xde\x03&\\\xc7\x1d.\xb5\xe5cvYM\xb5E\x02\x98\x04@30\xdde\u032e\xcb05\xad\xd0\xcd\u067b.\xeax\x8c\a@\xe3\x94\n\xed\x85='\x00\xaa\xe3\apJ\xaf\xf3\x0e{\xc9\x1f\xb7\x19\x8b7\xa7\u007f\xa8\xc6r\xabP\xe5\xa3\xe7\x03\"4\xc3\xdb\xfc\xe6\xec\x83\x1bH\xb0\xc6\x1b5\xf6\xc0\xe2T\xacb@\a\xf5\n\xe5\\\xdf\\\xa4\nInk_4\x03M< \xfb\xa5\x90\xf4<4\x95\xbe\xe5*L\x8dJ#W\x02\xc1\xb3\u0769\xd4\u0650\xab\x93\x14\xa3\xac\u0115\"?\x90\xa6\x19\x14\x9c\x18\xabM-(X\x98P\xf4\x971\\\x9a\xf7\x8d\xfd\x84\xa4\x13q0\xa6\x90\x03\x0e\x98\u00f1\xc7\x1e\x93\x01\xc0\x05\x17\x9c\xff\xb4N\x0eM\xc0\xfc\x19\xbe>\xf5\xa9O\x00\x00\x0e<\xf0\u0ece>\xfa\xe8Gz\xbd\xa9\xd2\x19.\uabd9\x15\x9c5Xx\xfca\xac.<\t5\x97\x83\xa7\x95\xf7\x87\xa6DE\x1d\x91\xd7Yx]\x8b\v\u0375Jw\u0754\xe1J\xf4\x91F\"\xb5K.\xc6t\x06C\xc4k\xc1;s\x1a\bt\x8bS\x1c\x86\x98\x1aa\xcb\xdc\xc6\xf3\xd63D}\xfed\x18\xf7\u05be\xf9\u064c\x19m\xe3\xaf\u04d4\xe5X\xf12\x019\x13z3\n\x9dYo\x99\x9be\xdeF\u05c3\xba\xf7\x15'\xaa\xb3\xe5BX\xdb\xfa@<_\xac:\\\x05\xd1k\xf2~1\x19\xd5\xf8\vj4&\xdb\xe8\xee\x9a\xe8\xa7\xe7\xd5;y\xc6\xe8jF\xce~\xf2U1\x1a\xfd\x8c\xb4\xdf\xd14m\xa7\xd2aR\xcdi\u040c\xae\xc8\xee\xb4\x0e\x97\xba\x15\xa646E\x8a:\xf1\x04\x94H\xbc\xc4\x10\x85\xf3\x95wF\x1e\xd0\u00f0\x90K\xbd\xde\xc5\x1bq9\x82\x9f\u007f\xa8\x92\x80|SVy\xb5\x92\xf7\xdca\xbfy+x\xbf\xf3P\xb4H\xd2]\xf1\xc6\\\f\x81C\u007f\xd7v\x14\xfd\x95\x00\xf0\x15\xc5\x146!\xdd\xe9\xe48\xfc\xf0\u00df\x04\x80\xa5\xa5%\x99\x80\xf9d=m\xeb\xe7\u007f\xfe\xe4\xf2\x83\xbfn\u077aok\xad\xc1\xcc\xe5 \x85\x04/sR\nK;\xb6a\xf7#\xf7C\xe7\x19x\x9a\xa1\xa7uh\xa2I5=\n\xf1\xde\xe6\xc5\b\u018d\xe0\xc4V\xa1\x01\x81\u05e6\xf4\x9c\x9e$u1{p\x85\xae\x18\xe4\x88\xe5Qz\xae\x00t\xa7\x14\xbas\xba\xfc\xf4\nU\xea\x86X\x02S\x8b\xdab\xac\xc2$T\x94H\x1e\x00=\xa1d\x9aus\x9b7z\xfc\xa9r \x91\xbd\x15\x80\uacbf\xed\x8c=8\x04\x1d\xbbR\xe3qw\xed.\xe8~\u0491;\xde\xf5\u0417\xaaa\xc4}\x8a\x13\xfb\xef\u0118\xab1\x8b\x19\x03\x8e\x13\u0303u\x1ek9\xf7\xb6\xb4J\x91\xa72\xbc=;\x14q\u02f3\x8b\xef\x1fjS\x9b\x9e\xd3\x06\xf4\x8c\x02\xcde\x1el\x91p\xea\xd4n\xddPg\x81\xa8\xfcQ\n\x95\xb8\x98\xa0N\xb1\x0e\u0384\x93F\xb0\xc5\x15M\x18\x8a\xc3\x10\xe2O\x80\xe1\xb9\xfbA6\x82\xe1\xc4m\x92P\x16\b:X\x003W\x95\x81\x88\vCC\u0276\x15\x1d;\xb5\x82X\x83\xc5\xc7\x1f\xf4j\x16nD&:\x8b,\u02f8(\xcc\xee\xf5\xeb\xd7\u007f\x1f\x00\xf2<\xc7\x04\xcc'\xebi[\xc7\x1e\xfb\x9c\xf2\xef\x1b6l\xf8\xce\xd4T/\xc4sUm(\"\x06\xeb\f+\xbb\xb7c\xd7#\xf7\x81Y\x815C\xcdh\xa8\x9e\xaa\x86)\"\u8245\xb5#\x98\xa2\x8fQ\xb1\nk\x87\xde\xf3\xc5\xd9\xd2]Q\u0106/W~1I\x98\xf4\fc\xd2\xc1x\xabn\x80\xe4\x03\x18z\xebrds\xda\xda\x15\xcc\xfa\x00\x00 \x00IDAT\xcb\xd4J>\xc2S(\xd4\xdauk\xe14\x94\xaf|\xbd\x8b\x93o\x80\xd6+Q\x19\x9bYoU:\x96\x9bT\x00\xa5.{\xfe8\xfa\x83D\x80\xe1\x90A\u065a\xd8\xd3(\xa6\xa3\xe41\xe7\xb0?y\xfa\tS\u028f\xb8'`\xd8\u6552\x9ef\xcaSN\xf2\xbf\xc2~z\x95\xf3\x8af\xa2\xb1I\x9e\xda!\xa1\u04a4'3\xa6Y\x97\xc1\xeb3`\xca\xd3@\x92h\xddkV\xb7\x90*\x90[\u0195;\x12\x93}\x8ad\u01f1\x01t\x9d\xaf\xdc]NXq\x82%\xe30\xd4@\x911LF0\xda\xcbb\x8d\x06\xa0\xb8z\xadu\bmV\xfe\xab\fW\xa1\u051f?$\r\x05\xc7N\xef\xb2K \xa5`\x8b\x11\xe6\x1f\xbc\x1b\xb6\x18\xfa\x1f\f\xca\x18\t\xee\xa0J)\x1c|\xf0\xc1\xc3\xcb.\xbbt\x01\x00\xce>\xfb\u0327\xf5Z\xd6\x138\x9b\xac\xb8\xba\xdd\xfc\u01a3\x8e:\n\x8f>\xfa\x18\x86\xc3a\xe9\x12GD\x10(\x8c\xfa\xab\x98\u007f\xe2\x11\x8c\xfa\xab\xfe\xfb\xcaA\xcd*P\xe1\xe0\xfa6\xc4o\x95\x1a\\X;\x80X\x03 \x03\x93\x06\xb3\x06\x83\xcb\v>\xba\x06z\x05\x01Cu\x14\x9c\x10`\x05\xb0\x9e\x1b\x85b\by\xef\x13J<NX\x13:\xeb2\xb8\xa1\x83]\xb1~4\x1b\x16\x10Sj\x9b\x01\n\xa1\x05\xc1\a\xbb\x96_\x1a+0\nc\xe3\\\vs\x1e\xab\x96\x13\xa3\xf1T&\x1f\xd5?\"\x94\u0394xN\xba\xe3\x00c=\b\a\u037bP\xe5\xe1\x1d\xd52c\x03VqX*4f\x81@\v\x84\xfc\xcc\xd2\xc6\x11I\x0f I)\x82T\xf1~M\xd59%\x9b\x12\xe5\ft\x150t\xe5k\xe2'|\x1but\x12^MU+\x02Z\x13\xb2\x034x.4=\x1dJ\xce]Z\xec\u039c\xf3\x14\f\xd9T@*\x89s\xa4\x84\xea2q5\f7\xe0\x9c\xc3\xca\xc0`\xbeo0$\xff\x9e9\xe7sf\xbd\xef\xb9\xe7\xe7)\xe1\xc9#`G\xc6Mhl\u05b8z)\xc3\xc90\xca>Ii\xac\xee|\x02\xcb\xdb\x1e\x868\a\xa5\xf32\x95HB\xf3s\xe3!\u03e2\xf5\xeb\xd7\xfft\xfd\xfa\r_\a\x80\x13O<Q&`>YO\xeb\x1a\x0eW\xd1\xe9La\u02d6\x93\x16n\xbd\xf5G\xcb\xff\xf2/zf8\x18\x02\xf0\u04c2\x95\xf3-ai\xc7\xe3X~r\x1bf7l\x84+\x86\xd0\x19!\x9f\xd3\xe8;\x81\x1b\xc5\x04\x1a\x02\xc8\xf9\v\xac\x18\xa1pE\x88\xfe\xd2 \xe8\xb2\u05258%)\xe4+'\u0470\x8e\x80\x91\x03\x93?:\xc3V\x01\x10D\xf5 `\u0342l\xda\u00ac\x8e`\n\a\xb2\x0e,>$\x83\\\xa4\x05\x18\f\x05\"\r0WG|\xf6\xea\x15\"\x04\x8a\x85\x92\xe0\x04)C\x12j2:T\x91b\u0360\x8bT\x16)B@\xc6\x1ex\x87\xce\xff\xdb\x010\x91\xa2\xf0\x95\xa6OWJ#\x86\xaa\xe3>\xa9\xb0\xc1\xc4\u02b1\xc3\xc0\xb4\xcf\u03ecB\x9fi\x9cFj\x99\xb8tR\x0fe\x02\x00\x0e\xaf\x81\xf4\x18n\xd9\u07ce\x15\x81qRZ\xfd\x8e7=\x93\xb4#&d\xd3\nj]\xe6O\n\xae\x9e\xa3]\xca\x12]\xe0\xb3\x1dPX\x815\x0e\x19\x01\xb9B\xeb\b\u007f\x93\x11s\x02\x14\x85\xc5\xe2b\x81]\xf3#\f\v\a\x8aJ'?\xc5S\xc6\xd3\xc1Q2\x1c\xdc4O\xf0^+U?\xb7\xaa\u04a3\xcbbT\x1f)\xad\x01g\xb0\xed\xf6\x1b1X\xd8\x05V\xc1\x1f>\xbc\x98\xe2,\x9c\xb54w\xc0\x1c6o\xde\xfc#\n;\xd8!\x87\x1c6\xa1Y&\xeb\xe9]\x9d\xce\x14\x00\xe0\xbc\xf3.\xd8\x01\xd0u\x9dN\x0eV\\\x82m\xe4]\x89\x19K\xdb\x1f\xc3\xf2\xce'\xa0t\x06\x12\x81\"\xa0\xdbc\xe8\x19U\x1an\x95Lp9@d\xe1l\x01S\f`\x8aU\x14\xa3U\x183\x805C\x883\xc12\xd7\x02\xec d\x01k \xa6\x803\x05l\xe1\xfft\xa3\x11\xccp\b;\x1c\xc2\x0e\a\xb0\xc3\x01\xa4\x18B\xe5\x06\xbak\xe0\xdc\b\xd6\x19X\xb1!\xe9\xc8\x02A^im\x01kG~,[lI\u00b3\xf6\xc7j\u0242\u0331\u64ce\xb2I\n\x12P\xcd\xc3[\xea\xfcC\xb3\x92\x95\xa4:\xcf\"7\x14\x9c\x1f\x11}\xd1\xdb\u87e4Q\x9c1\xc2\xfe\xe3m\x87g5\x90Q-\x10[Z\x06\x98\x9a \xe6d<r\"\xd5\xd3HF>s\x93+\x93\xb3fFv(\xa8=\r\xe2\x04&4b\xb3\u0408\xf6z?\a8\xaf\xf9v\xf1\xcb\xf8?\xad\x11\xd80\u0623\x9c\x94\xdav\xa4^\xee)\xb1\x1f\xfen\x1d0\x18Y<\xb9{\x88\x1dO\xf61\x18\xda\xd0@\x0e\x8d\xf9\xa8|R\x04V\x9e6\x8b7A\xd2\xd0\rIr4\t\x96\xe7\xcc\xc1\xc6!\x86\x91\x10\xcaD\xa1\x9dw\u0782\xc7n\xfa\x1a\xec\xa8\xef]\x13\x9d\xc0Yo\xeflM\x81<\xd7\xc8\xf3|\xfe\xf4\xd3O\xbda_\xb9\x8e'`>YI\xe1E\xab\x87\x1f~\xf8]\xd3\xd3\xd3 f\xdf\xedOB\x1eH+\xac\xcc\xef\xc4\xf2\xce\xed\xbe\x92\x0e\x95\x17A\xd0\xe9)`JyYXc\xea\xaf\xdc\x10\x9c\x83u\x06\xce\x198[\xc0Y\x03gM\x00\xd8 gd$\xf3\xde!\x8a.\x06_XS\xe6\x8c:k\xe1\x8c\x05\x91 \x9fV\xc8z\xbe\x8a-\x9d\xf4\xa2\xe1W\xb0*ub\xe1\u0107N\x1bW\xc0\xb1\v`\xc0a|\xbf\x81\xadR\x85mV\xb4\n\xd5hri\xce*\xa5\xb1\xcde\xd3NU\u0464\n\xa5\xea\xa2\rZkWe\x96D\xf5M)\xcf\xe9\x97\x1cxJ\xe4\x8f;\x9f\x8c\xa9E\x9a\x8e\x84)\xf5\x92\xfa\x86k\xaf\xba\xa9\xf4\xf3!\xb2-\xe8\xb7]\xf4-d@\xf5\x18\x1cl\x82\xe3\xd4e\xf0g\xf3TMi-+\xc1\x06B\x90\xb1\x0f\x98P\x8d\x87]\xfe3\xa1\x87\x00`4\xb2\xd8=?\xc2\xee\xf9\x01\x8a\xc2\xf8p\x8d\x92\xd3\xf6@L*\x84N$\xc0]?\x85T\xb6\xb9\x84DvJ\x95\x92\x89\x98\xfdO\t@J\xa3?\xff$\x1e\xb9\xe9+Xy\xf2q\xb0\xca\xfc\t\xc1\x85\xfe\x8e\xb30\xa6\xc0\xf4\xf44\x0e;\xec\xb0]\x97]\xf6\x8aoN\xc0|\xb2\xf6\xc9U\x14\u0177\x0e\xdbxX\xe0\x1d\x15\xca(s\x02\x88\x14\xcc`\x80\x95\xf9\x9d0\xa3Q\xc0Z_\x8du\x14\u041bQp\xb9\x97\f6r\x18*\u03d6\b\x80%\xd8\xfa\xafJ\x96X\xaf\n\u02f3vR\xc6E\xa0\xf6Gg\x01\xe7\x84\u03acB7\x8c\xbb;F\xd9\x18\x15\b\x1c\v\xac\n\x80$\x0eV\f\x80\x02\x8a\x02\x9f\x9dq\xbdQW\x17\x0f\x06k\x01jt\x12\xa9\xe5\xe2I\xa3\xd0B\xdaE\x8f*_t\xf6\x80.M\fo\x94\xcd\xd1+>\x86P\xd0\x14\x97\x89GT\x93\x82\xd4E\xd6\u037e/5.\xf0Z*O\xec\xefR\xe0\xcecfh\xfa:\xa3\xca\x1a\x05|\xd5\xee\u008d\xaaYU\xf3)\x8f\xef\x8b\rU\xb9\xb5\xc1'\xc5\xfa\u05eb\x9e(\xd5|\xd5\xc2\xf0\x8f\x8b\x01I\x82\xfe\xd0b\xe7\xee\x02\xf3\v#8k\xc1\\\x8d\u06f3\xf6\xcd\xe1\xb2\xe3*\xf5\xbc\xd9J8%\x89\u02c3\x84\x8a\u07a7/)\xed7\x030\u0574\xee\xc4\n\xfd\xdd\u06f1\xf8\xe8}\x10k\x03\xd0Gu\x90\x83\xb5\x06\xa6(033\x83\xe3\x8e;\xf6>\"z\x12\x00>\xf9\xc9\u007f\x9c\x80\xf9d\xed\x1b\xeb\xee\xbb\xef\x04\x00l9\xe9\xa4\xe5\u00cf8\xbc\xac\u0309\t\x04\xf6U\"\xfcD\xdd\xf2\xae\x1d\x18,-&\x86C\xfeb\xec*`jF\x01yt\xe6\xab<?\xa2b\xa0\xf4\u00e6\x84\x91 \xef\x8bR\x02y\xb8@\tU2M\xac\xb0\xa5q4'\x110\t\xb2)Fg\x9a\xd1\xc9}x\xafP\xb0\xe8%\t_\u0443\u011f\x00\x98\x05\x10\x03a\x1b\xae\x82\xc6\xd0\xd1\xd8\xe8z;\x10U\x99\xa6\x8dvc\xfc\xb9h\xde\x15\xa3\xdd\xc2\xc8x\xaa3O\xd17\x86\\\x18'>\xa6o\xba\nBn\xe1e\xb0v\nP\xb2WP\xeb\xccO\xf5=E !P\x18\x8d'\x178\xf51J\u0217\xb5\xa6\xcb0]\x8eA;\xfe\xfdp\x80\xb5a\u042c\x11\xfc \xe2*Z\x05\rN[\x00q\x95n\xdc9\xc1j\xdfb\xe7\xae\x02\x8b+\x05\x00\a\u05be\xe1\xcd\x01\x80)\x18a\x89s\x15\x98\xa7\x13f\xd13\x87\xabH=\x0e\x95\xb8\xd2\x04\x9d{\xa9\"\x10\xc2:\x90\xc8E\x89`G\x03\x98a?)*$4a\r\x8ab\x88\xa8\xf0\u06bcy\xf3W\xe2\xcf\x1c}\xf4\xd1\x130\x9f\xac}c\x1dw\x9c\x97(\x9eq\u05bfy\xa8?\x18}+\xeftA\xac\xaa\xd4rT\x8d\xa3\x95\x85y\fVV\xc2\xff\a\b\vWc'gt\xa7\x19\xc4\xf0\xa6]e\xe8n\xc2\xf1\x12jy\x90e&X);\b\x00 \x15\x18\x94\x19\x8b\xb1rNS_\xc4\x1f\xbfy\x8a\xa1;\x04\x1d\x8e\xe3.1\xfdB\xd4\xc0\x97G\xec\b\x9e\x1e\xe0\xd3@\aB\xaa\x85oC\xf1*\xe0Y\x1az\xe9f\xba{\xa9\r\x0f\xf7\xe7\x0f\x02T\x02e\x19\x87GU\x15lC\x83\x8f;>v\r-@\xdc\x04ri\x99\x19\xaa\x019\xd5O;)xQ!\xa0U\vg=\xfd\x9d\bw\x12\x1e\xdd\u007f\x99\x0ec\xd0e\xac\x1a\x81)B\x90w\xe0\u015dqej\x0f\xa5\xe1\x13\xc9\xccB\xd4us\xa05\xac\x93\xe05\xee?\x13\xab}\x8b\x9d\v\x05\x96\x87\x16\xc4\x02V\xe2#\xf1\xcaCb\xf2\x9eK#\xfd\x87jm\x87\xca)\x80\t\xa4\x18:S\xc8r\r\x1d\xfcW\x1cE\xe9hu2c\xa51Z^\x84\u9bc2X%/\x9a\x835~~\"\x16'\x87\x1ez\xc8\xee\xf8:\x9fz\xea\xe9O\xfb5<Q\xb3LVmm\xfe\xb9\xc3W\x0e\u07b8iG\xa7\xd3\xf3i+H\xfdH\xfc\xc51Z]\xc1h8,\xb5\x85\x92\x14\xd4 \x87\xbcC\xa0\x19F\u007f\xb7\xf7\x97\xa6\x94d\x0e\x90\xcee\xc3-H\xc2\xe2\xd0OHN\xe7`mZ\x8dL\xfaQs!\xaa\x0f\u02e4\u0702\x06\xa8KP}\x81\x90\x85%\x01\a~\x87\xc4_\xbcB\x14\xb4\xeb\f\xb0@THV\xa2\n\u009bp9V\xf3\x8e\xd1\x04\x95d\xb2\xa6\U0010ea99)\x05\xacz\xa4d\x10\x14\x8b\al\xa0\x94\xf4\xc5M\xcdi\xff\x18\xb3\x9c\xa0\xa6\xb5\x0f\x8a\x96=m+U\x00H\xfaR4\a6\xd3\xef\x8d%\r\x15\x0en`1\n\xde%\x14\xbd\x14\xd2<M\x006#\x8c\xa6\x18\x05\x03\xcb\xcb\x06d\x1cf\xba\nq,\x81bt \xca~h\x99\xe6\xe3l\u0578TD\xe5\xb0\x12B\x0fAD0\x18Z\xcc/\x1b\f\v_\x89\x97\xea\x11\x9bn\xf0\xae|l\xc4\xfe\x9f\xd4\xf0\xe3\xe1Pi\xc7\x0e\x06\xeb0\x05J\xd1&\x82|\xcf6\u0775\xa2\xd6\\,V\xb6=\x84\xe1\xf2<tw\xaa\fhv\xc1\n7\xfe\xbb\xd7\xebbyy\xe5\xdf\x02\xf8\u043er\xedN\xc0|\xb2\x9ak\xe3\x99\xe7\xfc\xc2\xf3n\xb8\xe1+\x18\xec^\x00)\xa9\x1d\xca\t\xf0\xc7\xd0b\x94\x9cj\xa5\xacV$\\h\xd9\x14C\x8c\xc2p\xd1\xc0\x19_\x98\nU\xb3\x99\x02\x0f\x12\xc2\xe4A\x98\xbd\u04e2D\xb3\xc5p\x81ID\xbaRYR\xcf\u03d4\xb2J\x0e\ua64e\x80r\x01\x0f,\n\xe3e\x82\x04\x80\x8c\x1fqt\x01y\x9c\x05\x9c\xb0\xd7);\v\x04\xa3\xa5\xb4\xc8\x1b\xeb3\"\xd5i7\"\xdfd<yG\x82|\x8e\u00b4%\x86\x9e\xdea\x0e\x15\xb9\x15\x90P\xd5\xf4e\xae\xd4$]\x86\xebr\xb2\xc1\xb4\xa5%U\xe1\xc4\xcdnb\x9b\x86\x9d\x1a*\x15q\x80\x85\xa0\x00`\x00X+!1\u067fY\x92\xb8?\x1aM\x18t\t\x05\t\xa4\xf02\xc3\xdd#\x87~\xdfzj\x8b\x83\xb5C\xf0\n7\xc6\xc1F\xee\\\xa4\xb4e\x88\xaf\x9bV\xc0TW!\xcb\x18B\x04c\x1dV\x87\x0e\x85\x15\xef`\tW\xa3i<\x90W\x80\x1d\x9fY\xe9?\x1e\x92\xaa#`\xab`M\x10?\x1a*I\x1ct`8\xe7B#\x9a\u02a2Du:X\xd9\xf18\xe6\x1f\xba\xa7\xe6\xea(\xe2\x81\u071aQ\xad\xfd\xb0c\xc7\xceC\xf7\xa5\vw\x02\xe6\x93\xd5\\\x1b6\x1cr\xd8\xcfM\u036e\u00ee]\xbb\xa1\xda2\x1a\x9d\xf5\x04)b~f:\x89R\x01A6\xad\x00\x03\x8cV-\xac\xf5\u0382`\x02\xd9\x18\xca\xeb]\x0f\x89\x01\xe1\xd0\u0434\xfeZq\x89\x93_\xd9\x04%jL\xbe \t\x85\x0e\xa7\b\x06\xa8G\xc8\xfa\x04[8\x18\xe7*P3\x14\x1a\xae\xec+G\xb2Pb!VA\x94\xf3\xda\xeeXR\v%\xc0\x910\xe2\xd2R\xea\n\x1aTTR\xf5\x89\xa7\b\xa4\xab +6\u8ba5\xa2XJ<\xa1\x10\x8b\u7a56aF\xe8\xaad\x8a\x9ch\xccy2Up\xb4\r\x06\xc5\x1d\x86\x1a^\ua945\x8e\xf8\xb8\xb4\xc5\xc2\xc1u\t\xd3C\x82\xf2\u0495\xe0sB\xb0\x00\n%\x18d\x84B\x04(\xbc:%\u01ad\xad\x1a\xa0\xdf\x0f\xef\xa1\x04\x9f\x13\x97l\xb6\x89\xc1\x16Q\x95\x02e\x1d0\xb2\x02\xa5<%\xe7\xc2\v\xad\xf2 \x89uRN\bc\xcc\xf5>(e\x92\xa0\x13\nt\x8ab\nn\x90\u0546\x16\xdd\x12\xe3g\u060a\xef\xa1P\xa2,\x15'Py\x17\x8b\x8f?\x80\xc5G\xef\x87\xeet\xcaW\xcb9\vS\f\x83\x84\xd6?\x15c\f\xb2,\xfb\xf1\x04\xcc'k_^J\xb2\x1e\xf4\xd4\\\xf0Z\xf1\x17%S\f\x8c@p\x94c8i\xda\xc4V\xc0\xc6 \x9fW9\xa3\x00\x11\x8c\xfa^\xe2\xc6\xde\xc9\xc8W\xa4H\x94-\x11rC\x05fC,\x1a\xc7H\x9c\xb8W\u0109\xcbF\x8ee\x82 \x90\fP=B\xc7\x10\xdcH\xbc\xe2F\x82\x1b^\x11\xa7\x0e\t\x02\vk\rP\x10lt\xe3\x8b<)5\xaba\x97T\xb8\xf5\xd9~\u007f\"\xa9\xa8\x89V+\x98N\xb0\xf6-<\xdd\xe38\x86\x02KH&\"HHj\xb2\x9a\xd1\xe9*d\xaa\xf1\x1c\xc7\xfeY\xa5\xdeT\xed\aJz\vi\xd6k\xe5\xd3\x02\x01\x8a\xc2ai\xd5`q\xd5\xfaA\x9c.\xc1A\xa13p`\xeb\xfb\rF\x01\x85\x16\x8c2\xf2\xcd\xd88^\x1fv\xb5\xb8\xb9\t\x04b\x1a\x94\x17*0\xadK#C\x89\x1c6'\x87\xca\x10\x8b\x94o\x14W\x95\xb8\x94\xf7S}\xb4\xd2\xcd\x01e3\x99\x88\xa1\u0201(\x9c|\xe2\xe7\x96c\x8f&\xc8B\x9d\x8f\x92\xab\x1c\xd4}\xf3\x95\x15\u00cd\x06\xd8\xfd\xc0\x9d\x18,\xecB\x16\f\xe7\x9c\bL1\x825E\xf9\xd2+\xa5h\u00c6\r\u0634\u9c3f\u0717.\xdcI\x03t\xb2\x00\x00\x8f\xce\xfb\xa0\x8a!\xe0\n\x95Cwz(5\xbb\x9cp\xc2\"P\x9d\x1eDe(\x8c\r\x9ab\x8c!\x98\xa7?\x04\xdc!\xe8\x1eC+\u007f\xaeO\x8d\x8d\xa8\x9cLta`\xc8+\x14\x9c\xf3\x15\xb5\xb1\x16\xc6\x1aX\xe3\x1bm\xb1\x11\uab03\x18\xaf?/\xbf\x1f\xa5\x8e6<\x98\x1eCu\t\xb9\xf6r;\u07d8\x8b\x9e\x1fA\x12I^\u007fn\x8d\x81\x1d\x8d<'Z\xb3i$\xa4\u0588\xd1\xcc*V\xa0\xf1\xc9:\x91\x96\x89\xc3\xe6\x16\xe9\xf3:\x05\x04\xcb>\x0e-R$\xc2\xc1\xf5\x11\xde,J\xcf0f\xa78\xf8VI\xbd\xa1\x97\x10+^bQM\xf5P\xc9Q\a\xba*\xe9@F\xc1\x87\xb3\x82\xe5\x15\x83\xed\xbbGxr~\x84A\xdfxgB+X\xe9\x10\xe6g\x15\x16f\x18\xcb=\xc6j\a\xe8\xe7\x04\x13\x8f\x1aQ1b\x05b\x91\f\v\xc5\x10\x90\xcay\x90\x12\xba\xa7\xd4}\x97\xaed\x95N\\\xe5\f\x95\xb1\xf7\x92WTy\xe2\xc4Ppjf\xc4&\xee\x9a\xd1KE\a5\x14\xa5\xf9\x11\x1e\xe0]\x98\x9aR\xe1\xe4`k\x01\u05b1\xa0\x10\xe8N\x17\u02cf=\x80\x9dw\xdc\f\x8aY\xb8\x10?\xec6\x1a\u052a\xf2\x03\x0e8\x00'\x9d\xb4\xe5;\xaf~\xf5\xbf\xfd2\x00|\xf4\xa3\x1f\xa1}\xe1\x1a\x9eT\xe6\x93\x05\x00\xd8=\xf4\u0235\x00\x18C\x1aJg\xb59\xf5*\xf4\u01c2\xbb\xb3(\xa8\x8b\xa20\xc8\x1a\xc1\x03\xa9C\xac\x8b\x00\x94\x937\xb4\n\xea\x87\xf2\x8c\v\x01\xbb\xc8!W\xbc\xafq\xc0\xd0z\x10\u0395 S\xec+kix\x17Z4-I\x93O\xb6\x1fUW#\x87\xac\xf0\xaa\x898\xaa\x0f\x17=Fbc\xcd\xc2Y\x02F#@\x00\x95\xe5\xe5 \t\t\xb5\xba\xff\x95`\xbf\xb6J\xb0^F3\xbc\x9e]Y\xb8\"!=\x92\xecN\xc7\x00:\x8c\xee\xb4*#\xdd(\x95\xcd5n[\x12\xa9f\xa4\x87\xbc\x9dkh\x16\xbb\n\x83\xe1\x04##X\\5X^\xb1\x18\x8dl\t\xc2\x12\x1f_\xdc\x178\xfe\x9b*\xf0\x1e\ubf3a\xc6\xf7\xa4\xb2\u0265\x1a\xabS*JJ O\xaa\xf0*\x14\xa3\xf1\xbc\x82&<Rg\xf1=#\xe6d\xca\u022b`\xc8\x01J\x04~~\x88\u02ea\xbctc\x88\x9f\xc7t\x82@\xa4\xf2M\xcfr\xb8\xa2\xc0\x8e\xbbo\xc1\xf2\xceGAJ\xfb\xcd\xdb\x1a\x14\xa3\x01\x9c-jo\xe7\xf1\xc7\x1f\x87\xe3\x8f?\xfe\x0f\xe2\xbf_\xff\xfa_\x97}\xe1\x1a\x9eT\xe6\x93\x05\x008\xe9\x909\x01\x80\xc5\x02\x87\xac\x16\u05bb\x1c&\\e\xea\xe67}\xe0\xc1\x98=`\x0e\x19,\xfc\xc0\xa0\x8cM\x19\xa6L\x05g\xec\xdd\x15\x93\x19\x13?\xfa\xed\xbc$\x90\xaba!\x02\xa1p\xc0ja\xe1D\xbc\xcb P\x1b\xa7\xafM2\xa6G\xf2x\xec\x0f\x15\x1du\xfc`\x8b\n\x16\xb4%\xe7\xea\x82Wv\x1c,r\x02\t\x13\xa5\xa5\x85@\xac\xd0\x13\x80\xaa\xf5^\x81Z\xd5Xk,\xb6`\xb9 \x04EG\xdb\x00[\xe9\xa2)\x9c\x1a\x1c\x00\x97\x05\xcfsS9GJS\xb7m-\x9c\xf5\xd5t)\xd1s\xbe\x81\xe9\xa5\u05c2\xe1\xd0ai\xb1\xc0\xd2B\x81\u0565\x02\xf3\xf3#<\xb1\xad\x8f]\xbbF\x18\rmU\xd1\xd7N\x15\xb1\xd2\x0fQl\xc6A\x8c++\u007f\x89\xa7\xaa8\xa9\x19\x95LI\xf0\x06q\xeap\x15@[3(S\xe0\x8e\x86\xca\x158S\xde.\x82\x93\xbe\x04\xd59\xff\xb2\xefL\xd5f@\x9a\xbd\\3\xe3\u048a\x81\xe0\u9c4c\t\x8a\xbc\xdd0\az\xceIU\xad\xdb`\x0f \xa9\x1ds\xb8/\x95eX}\xf2\tl\xbf\xe3\ap\xd6\xfaq0\x11\x98b\b3\x1cT\x9f\x05\x00sss\u0632e\u02d7\xdf\xf1\x8e\u007f\xff\x15\x00\xb8\xfa\uafe5}\xe5\x1a\x9eT\xe6\x93U\x81\xce5o\xd6w\x14\xb8\xa0\xdf_\xc1\xea\xe2n?\x05\x1a\xab\xbf0m\xa7\xf3\x1e\xd6\x1f\xba\t\xb33S(V\x17\x92z\x80\x12n\x93\xc6\f\x94J3\xab\x91\xe7\xaf-\x00\x16\xef\xa0'\xd6gF\x92\xf8\xe6\u0520\xb0`\x89\xa3\xdfA\xd9\x10\xaap\"\x19\xdb5$\xb8;\x96\xd1a.\xc9\x15\xed2\xa8O`\xeb\xedt]\u0d8dq\u022c@9\a\x10{G=\xb1p\x96\x01\xb6 \x1b\xf8[\xa8\xa0\xac\xa9\xaah\x91\u0532@jsBi\xa5]\x9d\x16\xa8\fM\xb6\x10\x14Q\xb7\xe7*\xab\x00G\x80Q\x02\xeb\x04f\xe4\xfc\xeb\x03\x86\x0e\xbc\xb9+cM\xbd\xd4/N:FS\xb0\xa8\xe9\x87\x00\u00e1\xc3\xc2r\x81\xc1\xc0y\x8d6\x80Q\xe1S\xe9Yy~:r\xdd\xe5\xfb\x14\xabo\x97\x94\xdb\x12o]\xaa\xed<\xf5\x1dK\xacl\xab\xc9\u02eaI\u0351\x0e\xa1h\x18V\xff\xfd\xaa\xf9\x9b\xf4\"\xa8R3\tG\x99d\x90\r\x86\u03e0\xd8\U0001e2c0\xe1\x87\xc6R\x99\xb9$\x9b0\xc37zm\x943\x82\xe0\xca\u03ea\x80\x94\x02\xc4a\xf9\x89\x870\u063d\xdd\u007f\x9f\t\xd6\x14(\x86}\xdfSI\xda\x14\xa7\x9f~\x1a\xce<\xf3\xcc?'\xa2!\x00\xbc\xeeuo\x90}\xe5\xfa\x9dT\xe6\x93U\xae\xdbN\xfe\x8d\r\xfd\x81}\xc1\x13\x8f>\x8c\x9d\x8f<\bV\xaa\xac\u0108\x00g\v\xcc\x1e|\x18\xd6o\xfc9\xc0\x19T\u04cf\xa8\x94\vc\x9d\xbf0,B\xe2\xa9\x16\xf2a\xd1\xcez\xa7\x0f\x82\x83\x83\xf5^\xe7#\x83bP\xc0\x0e-z$\u0202\u06d2\x18T\u0561\x95\xd2B \xe9\xc0\u008a\x84\x01\x14\t\xb4\xb8\xaf8U\xc7Ws*\x84/\u0107U\x14\x82\xd1\xc0\x86\xa9R\xcf\u04fbh\xa6dm\xe0\x9d]\b\x9c\x96\x92r(\x9b|\x0ep&\x04)X?0\xe3\x8aP\xc9Z\x01Y\a\x8a\xa3\xed\x85\xff?\a\xc1\xd0\tL\x11\x86glh\u0205\xaf\x11\x01\u00d1\xc3\xea\x8a\xc5j\xdfb0\xb0\xe8\x0f\xfc\u07d7W\r\x16\x96\r\x96V\f\x86#\ak|pC\u007f\xe00\xbf\xe0\xc7\xde\x17\x17\v\xec\xda=\u0136\x9d}\xcc/\x8e0\x18\x85\xdb\xe8[X#\xd5LVt\u038a\xbd\x8exR\xb1\xc90VR\xb1\x8f\xd9\xc1DM6\x85\xca[q\u5552\xf9\x04#\xd5ap\xaeJ\xc30j&8\xa5\xf9$TQ/\xacB\xb0v\xbc\xad\xf0E\x89\xe3B\x9c\xfcd\x12d\x1c>'\xe1q\u0692\xc2\x0f\x15:3\x1c\bB\x89q\\\xe2\xb5C\xe4C\x9b\x8b\x95\x05 \x19\xdb/\x86\xab0\u5d27\xff\xe1\r\x1b6\xe0\x94S\xb6\xfe\xc3\x15W\xbc\xf1\x06\x00\xf8\x9b\xbf\xf9k\u0697\xae\xdfIe>Y\xf8\xf1\xbd\xf7\xe1\xa4go\xc6\t[\x9e;\xf5\xd5;\x1f;\xf9\x87\u07f8\x01K\xf3\xbb\u041b\x9e\t@\xee\xa7\xe4\x9c1\x98y\xd6&\xcc\x1c\xbc1|\u0429\x96r\x1f\xf4n\x151\x13)W\xeb`\x04@\x16\xe4w\u05abc\x14\xfb\xc6\x15\xe9\xc0\x8f:\a%\xc0\xb4\x06:\xca\x03\x8e\x8bMX\xc4\x01%*/0W\xfaySP(\x04\xa1DPL0\x93o\n\xc6)?[m>\xce:\xf4\x97-h\xca!\xcb\x18D\xde\x1f\x06\xca\xfbs\b\xf9\xd8;(\x8b(\x87p\x0e\x15\x9d\x13\x1eC\xc3\b\xb7\xa4\xfeK\xccw(\u04da\n#X\xb5\xbeJ\xcc\xc3\tB\xc2\xc4g\xa1\x01\x03\a3\x12\xac\x8c\b\xc5\u040f\x9cKR\x95\x1b'\xc8\x14a\xaa\xab\xa0\xd97\xf7V\xfa\x06\xab}\x13\xaa2\x82\xb1\xe15\xabq\xedR6n)\xf5+\x97\xd4i\x91\u01a8\xa1\xb4/PU\xbc\xd4H\xf2\xa9\xf2[\x91\x84;\x8c\xadTb\x99~+n\b\xd1@\x8b\t\u03b0\xaf\xa9-\xfb\r\xbcv\x18\xf3\x958\x91@IP\xef\xc4SS\xb0\xe8\x15\x00:X\x05\x1b'\xb0\xb1!\x90\x98l\xc5S\x8dw_\xd4\xe8\xce\xccy\x96\xc9\x1aXk1\x1a\xf6\u02e6\xa7\b\x9c\u059aO=\xf5y\x0f\\y\xe5\x15\xff7\x11\x15\xef~\xf7\xbb\xb2+\xaf\xbc\xaa\x98\x80\xf9d\xed\x13\xeb\xd1G\x1f\xc6\xf7\xbf\u007f#Nz\xf6f\x00\xc0*0\xfd\xc5O\xfcw|\xff+_\xf4MIq\xa5`W\x9c\x03\xeb\x1c3\x87\x1e\r5\xb5\x0e\xa6X\xf5\x95.\xb3?\xaa\x82@*\x83\x90*\xe9\x8e\xc28X\xe7+(f@2\a\xd1\x02\t\xc1\x13\xba\xa3||\x99V\x81&A\xa0W\xa2\x02\xa5\xba\xea\xa5\xd4LK\xe2\xc2\xe8\xabp0\u01d3|#\xc5GJn\xdc)@\x8a\xe8#\x13\xaa\xf3U\x83\xfe\x12\x83f3?5\xc8\x02h\x80\x11\x142\xe45\xe9\xe5m\x05`)\u00c4\xb9\n\xa2\xaf5\xfc\xaa\xb6\x00\xacu\x81\x86\x16,\xf7-V\xad\x03\xeb\u0434c\x81c`\xa4\b#\x0eC;V`D`FR#\x92)\xf8\xc7\x14\x00\x86}\vf\x82\xb5\x02\x13\xaai\x8bJy\xc3A@\xed\x15-R\u6596`\x1cOP\"\xed\xa3\xa2\x8d\xac\xcfR\xcd\x14\xab\xe8\x98e\x1a\xfdf\x12\x95J\xbd\xf1\xd9\xcc\fM\xfe+\x00\xb7\x8fi\xe3\xa4Z'\x90\xadxwo\xf8\xe6\xca\x17=>\xe4\xe8\x89n\x1d\xe0\x1cC3\x82\xaa(<\u007f\xf8M\xb3\x90\xa81\x0f\x1e-I\xaf\x85\xc2\xedw\xbb=(\x02\x06K\xf30E\x81b\u0507\rMO\xa5\x94Xk\xe9\xb4\xd3N\u0165\x97\xbe\xf4\x1f\x8f?~\u02dd\x00\xf0\xf6\xb7\xbf\u077c\xef}\xef\xc7\x04\xcc'k\x9fXw\xdf}\x17^\xf6\xb2_\x8a\xbcs\xefw\xff\xe0\x0f\xfe\xe8S\x1f\xbb\x1a;\x9fx\x04\xdd\xee4\x9cu\xa5\xc1\x96\x19\r0u\u0421\x98;j\v\x06#\v\xed\x1c\xf2L\xc3\x19\x83\x1dw\u07c6'\xee\xba\x15B\x8cl\xfa\x00p\xd6\x05\xe7=@\xf5\xa0\xf2.\xa6f\xa6\u041b\x9a\x02e30N\x83\x95F\xa65\xa8\x97Au\xbd\x91\x94\xb0@LQU\xf6\xc2U\x05(\x95\x9a\xa2\xe4Y\xa3a\x1e\x04\xcc\x0eJ\x92\xb0\x8b8`\x14mX\x15 \xdaW\xf2\xb6\xec\x82\xf91\xf6\xc1R\x01\xce\b\xdd\\{<J\a\r\x85R\x19O\xbd\x0f@\xf5\xc0\xb6\xd8\x0f\x95\xe4\x90\x12' G\x85\xc3J\xdfb\xb5\x1fxk\x06\xac\xf6)\xf1\x8e\bV\xf9\x1eBt\x87\xa4\xc4G7N\xcdzJ\xc4\xd7\xdc\u01a1\xee\xec\x18'o\xe3\x10U[\xa8(Qb\\F\xe3\x1e\xea\xad\xddk*\xc3\x1b*Y`RE\x03\xa9\x16\xb0\u04b4S\xfdu\x92\xf4wc3\x93\x13\x85Kz\x14\x88\xe1\xde\xd5\xee\x01\"\x15n\xc3\xc5t@\xa8\xe8\tI\x95\xd7\x01\x13\xc0Ba\u0407`d\u071f,nbD\x04\xa5\x14\xa6\xa6\xa71\u06bd\r\xf7}\xeb\v\x18\xae,\x95\xde\xf7~\xa2\x95`\xad5G\x1f}Tv\xfa\xe9\xa7\xdf\U00096dfc\xf5\xfd\xff\xee\u07fd\r_\xfa\xd2\xf5\xbca\u00f3\u073ev=O\xc0\xfc\x19\xba\xe6\xe7wa\u077a\x03\xcb\u007f\xff\xe1\x1f\xfe\xc1\xef}\xfes\x9f}\xf1C\xf7\xdf\v8'\xce\x19\xf2>\xe3\x1a.\x94\xb2\a>\xfb\xe71w\xe4\xf1\x18\fG`\x00\x86\x18\xa3\x95%\xdc\xf7\xc3\xef\xe1\x81\xef]\x0f;\x1a@uz`\xa5\xc1:\x87\xd29T\x96C\xe5\x1ddy\a\u0719\x02\xeb.\x14w\xa0\x14\xa3;5\x8d\x83\x8f<\x06S\a\x1d\x84\xa9u\ab\xf6Y\x9b\xc0*\x83s~\xf8\xc3k\x83\xebcIH\xc0:\x0e\x15Q\xe0\xb4]\x90\x1aV\xc7\xe8\xf0\xe3\x04\f\x01\x8c\"\xd7\x1e\xa4n\x10A\xd1\x17,,\x10d\x16\xe8u3Tv\xb2\x94\x1a\xf0y^\xb9\xdcT\xa8\x1a)\x97d\x80'}\x88V0\x18X\xac\x0e,V\xfb\x06\xc3\xc2\x05\xef\x9a \xcfVH@U\xca\u01a3$I=\xf5\xb1\xdb\x16\x8a\"\xfdy\xa97.\xd3a\x9dd\x8c\x145\xa3\x17Jm\x1a\xc2Tn\xec_\x06\xb9h\t\xe2T}\x0f\x8d\xe9Ij\xd9\x10R)\"S\xa5\vo\xfen\x93\xdbI\xbd\xc8%U\u0144!3\xcd@\x0e?\xbe/\xce\xc7\rJ\xe8$\xc7\u03c2\t\x83\xc4q\xaa\x94\x90\xbew\u1e72Bwj\x06\x18\xae\xe2'\xd7\xfd\x1d\x1e\xfd\xc9\xf7a\xc5\xc1\x98Q\b\xe6Vp\xceI\xaf\xd7\u034e;\xeeXw\xc9%\x17\xbf\x9b\x88\u6bff\xfe\xf3|\xe1\x85\x17\xbbo|\xe3kx\xc1\v\u039d\x80\xf9d=\xbd+\x1d\xc1\x17\x91\xb9\xff\xf2_>\xf0\xc1\xab\xaf\xfe\xf8ko\xbe\xf9\x87%\x8a8k\xabtwkp\xe0\xe6\x13q\xc4\xff\xf6R\xe8N\xd7\u02f5\x04\xb0C\x03\xeb4\xa66\x1e\x8b\r\xc7<\x01[\f\xfdE`\n\xd8b\b;\x1a\u0099\x11\xccR\x1f\xabf\x04gF\xb0\xa3\x01l1\x808\x03\xd69\ue7daC65\x8dC\x8f?\x15']r9\xd6\x1fz\x04\u020c\x82\xe1V<27\x89\x00jbQ\xa2\u0328\xa0E\x82\xb6:\x82*;It\U00061135\x9e\xb6\x98\x17\x82\x13\x8d\x19E\xd0\xe29[AU\u0757\x8d3\xa9\xc2\x0flc0&V\xd1\xe2\x04\x8b\xcb\x06\xbb\x17F\x18\x1a\a\xe7\u01bd\xbcSO\x97\x1a\xb2\xb9:pS\x92\xe99\xaec\x97\xba\x87n\x82\x92\xb4\x86\x85oi\xbf[\xfe\x117\x14.i#/1\x8c\x19\xc6u\x1bB*\xa5\x9a\x91\xf6\xa2\x1a\xad\x12\x03\x92c\x88D\u4f9aN\xc2$)\x9b\xdf\xe24\x1c\xf2d\xcb'\xc2\xfe}\xd1\u481c\x9f\xd8u\x89\xad\xb2\n\xdd\v#\xfe\xcb5\xf9\u007f\xaa\x14,L\nyo\x1an\xb0\x8c\x9f|\xe1\xefq\xdfw\xbf\xe4\xfb.\xc5\b\x90\xe0a.\"D\xa0\x13N8\x01/x\xc1\v\xfe\xf2%/\xb9\xf4\xf3\x00p\xf1\xc5/v\x1f\xff\xf8\xc7\xf69 \x9f\x80\xf93\x14\xc8/\xbd\xf4\xa5\n\x80\x15\x91\r\xef\u007f\xff\xfb\xae\xf9\xdc\u7bbd\xf0\x96[n\xad\x95\x83Q\x1b\xedL\x81\xe9\x83\x0e\xc5\xe6\v_\x8d\xf5\u01dc\x8cbe9\xd0\b\x04\xe7,Hi\x1cr\xd2\xd9\xd8p\xfc\xa9!\xb1\xd7W\xb4\xce\x14a\xaar\x003XE\xb1\xba\x8cbe\x01\xab\xbb\x1e\xc7\xf2\xf6\a1\xd8\xfd\x04\x9c-\xc0Y\x17*\x9f\x82\xcb7`q\x95\xd1\xe9\x17\xc8U2\xf5\u01c9\xa7GI\xe2F\xf8\x960\xd4S9,R\xe0\xaaG\xc6b8r\xe8\x0f\x9cW\u007f8\xa0\x13=;J\v\x19\x1f\xf8\u03240\xb2\n\xf3K\x0e#kq\x00\x1cz\x04\xe8tz\xb2\u00b32->r\xe5\xde\x0124e\x9d`a\xa9\xc0\xce\xf9\x11\x8cI%\x84\x187xI\xfe#\x86\x05c\xac9\xd9tkL\x01\xbce\x8b\x8b\xc8\xc8\xd5w\xa9\xa1\x1cI\xff,\xe9\x91D\xcf]\x01\xb8\x00\xcd,\xd0D\x96\x12y\xfc\x12\xf4\x03\x80\x97Fb\xb5\xe7<Nw\xa4\x93\xa2Mn=}\xec\x04\x82V\x04\xc5\xca+\x84b\xa5MH\x147\xfe3`\\\x90\x9f&\x8f\x9f\x13Y)\t\x90\xe7]\x14\vO\xe2\x8e\xcf_\x8d\xfb\xbe\xfdE8\xeb\xa3\xe0\u011a\xca\n\x00\xa0#\x8f\xfc9\x9cw\xdey\xdf\u007f\xf7\xbb\xdf\xfbV\x00\u0638q#\xfe\xfa\xaf\xff\n\xbf\xf8\x8b/\xdb'\xaf\xed\t\x98?3+r+\"\a\xff\xee\xef\xfe\xce5\xd7]\xf7\xf9\vn\xba\xe9\aHr}|S\x93\x18\xce\x14\xe8\u032d\xc7Q\xe7\xbe\x02\x1b\x9f\xf7\v~\xa0\x82\x01\xa8\x90\xbe\"\x1c\xf8G\x85<\xef\x95\xf7\x91V\x9b\x02\xf2#\xf6\xae2]rE\x81\xfe\xee'P\xac.\xa2;\xb7\x01zj\x16*\xebB\x84\xb0{\xd7*fg4\xa6z:p\xe0M\u08925\xa8\x02\xe9C \xb1\x01\x86\x85\xc5`\xe80\x18Y\x18\xe3\x81=R\x046\xfapH\xd4>\x13D)\xb0V\x10\xad!PX\x19\x12F\xf3\x0e\xbd\x91A\xb7\xe3\xd0\xc9\x14\xb4\x0e\xca\x18\xaa&2]h\x86:\v\x14\xd6z9\x9c\x13\fG\x0eK+\x06\xc6:\x9f'\xd9B%4\xc1\xb9\x1ct\xaaH\xe3\xf2\xb9q\x02\x88\x92\x00\x9c$X\x8b\x14\xc4S~\xba\xd9xL\x8c\xcb\xca\xe9Kj\xfcn\xba\xebPL\xbb\xa7\x8a\x87\xa7j\xac\x1e\xa1\x01Zj\xc8\xc3mH\xc2\x1e\x11\xd5\x19\xa3\x9a+e\x83M\x8a\x8b\u00ddH\xf0\xed\xc9\x19\xd0a\x1a\u05c9\xf2Q\x85\xc4\xc1\x06\x82\xc0`\xaf%\x0f\x12X\xa1\xf43\x18g\x13|\xb9\x9eu{\x18-\xed\xc6\x1d\xd7\x06 \x17\x17r=\x87\xe5\xfb+\x00fgg\xf1\xdc\xe7\xfe\xfc\xb6\xb7\xbd\xed\xadW\xfe\xe9\x9f\xfe\x19\x00\xe0?\xfc\x87\xdf\xdeg\x81|\x02\xe6\u03e0u\xcd5\u007f\x9fR+\a\xbf\xef}\u007f\x10\x80\xfc\xa6\xda%\xc5\xca'\x01\x883\u041d9l\xbe\xf0U8\xfa\xfc_\x02)\r7\x1a\x822\x9f2\xcc\xc6V\xa92\xd6\xc1\x9aA96^\xbb>%\x89\x14\x82\x97\b\xeaN\x17\xb3\x87\x1c\xdd\x18\xfcqp\u01a0\xb0\x84\xa5\x15\u03c1\xf6z\xde\xcat\fD\x02\xc00\x01\xc6x\xe0\x1e\x15\x1eH\x87A\xc3\x1dKiV\b\x8a\b\xcf\xdfZ\xf2&_D\xec7%\xad\x81\\A\xb1\x0e\t\b\x04\xe3\bK\xcb\x05VV\t\x99fd\xc1\x0f[\xc5\xd0\xdf \xf4\xb1\xce\u06fd\x8e\x02\x95\x02\x04\x90\x0f\xd5z\x15\xa9\xd6R\x9d\x02\x15\xe8\xb4\xf0\r\xcdP\x89FQ\x9c\x84f\xa3jDRjnE\xf5)\x9a`f\x85\x10\x9b\u059cl\xad)rP\x81x\xadzO\xe4\x83\u008d\xff\x8f\x15\xb74@\xbf\xad!\xba\xb71\x9b\x90p\xa5D\xa0)\x80\x94\x00V\xa8|n\x12U;>\x85\xd9\x039G\u03df\xf0\xb9\f=\x14'\x9e\x03\xcf;9\x06\xbb\x9e\xc0\x1d\xd7}\x1c\x0f~\xefKeM_\fW}\x97\x84|\x91\xd2\xe9tp\xdey\xe7\xe25\xafy\xcd;\x8e<\xf2\xa8[\x01\xe0w~\xe7\xbd\xf4\u05b7\xfe\xef\xb2/_\xe3\x130\u007f\x06\xac[n\xb9\x19\xd7^{m\t'\u007f\xfc\xc7\u007f\xf4;_\xfc\xe2\xf5\x17\xdc|\xf3\xcd5 \xf71q\n\xceZ\xa8n\x0f\xc7\\\xfcj\x1c\u007f\xe9\x1b@*\x87\x19\xae\x86\x80g\xf1A\x12\x91\xdaH\x06y\xe0\xc4\xfb\x83K\x92\xba#i\x9b,\xd2\x16\xa6\xfcW\x1c\xc0\x11x\x89\x1a\x93\xaf\xa0\x17\xfb\x0eC\v\xf4:\x82,\xf3#\xf9)7\xed\xac\xa00\x0e\xfd\x81E\u007f\xe8\x82D/\x02\x0e'r\xba`\x9b\u01de\x04\x96\x8c\xe1Dy)\x89\xd6\x10\xc5A\x1aWy~H\xa2\xcb\x1e\x8e\x1c\x86C_\x067)\x13)'$i\\\x14\xd2L\x91\x93:s\xd1`\x90\xbd,\x12\xa8\x95\xb2\x8d\x1c\x8e\x9a\u0084\x12:%275uH\xa3\ua9b1\f\xb9\x06E\x938coU\xd1\xd4\x00\x00 \x00IDAT\xa5\x96\xb9\x14'\xad8\xa1`\xa8QqK\xbd\xc0\xde\xdb$\rS\xbb;{\xb5\xefx\u007ft\x8d`\xc9+1\t\xaa2H#\n\ue4e0\xd0\xecDp\\\f'P.Mi\xa0\xb3\fYg\n\x8b\x0f\u0789\xdb?\xf71<z\u02f7\xfc\xe7\x01@1\xec\x03b\xbd+(D:\x9d\x9c\xce?\xff<\xbc\xf6\xb5\x97\xff\xd1\xcb^\xf6\x8a\xbf\a\x80\xf7\xbe\xf7=t\xd6Yg\u027e~\x9dO\xc0\xfc\x19\xb0\xbe\xf8\xc5\xeb\xf1\x9e\xf7\xfcG\x01\x80\u007f\xfa\xa7\xff\xef\r\u007f\xf6g\x1fx\u04cd7\xde\x18='|a\xc7\xec\xaboc\x90\xf5z\xd8\xf2\xf27\xe2\xe4W\xbf\r\xc49\x86K\v\x95\xec\xaf\x04\x8az\xa3\x91\x9c\xc0\x99p\xe1\tU\xae}\"\xa5\xc3\xe0\xd8\xc5\x1e\xffO\u01fcFU\xeb&\x8e \xb0\x05\xa0\xac\x83\x8ay\x8ep\xe5\x84gQ\b\xac\xf5\u0b78~\xeb\x14\a\x85XW\xe1\xd4J\x83Y\xf9\n,|\xf9\xdeZ](\x1e\x95\x13\x10\x80XB\xaf\x8fj4R\xd9L+u\x92\xb4VvD\xfd9\xa7\xfcu#{\xb2\xfa\x15N\ng\xa9R\x908\xe5\xbe\xeb\xbcu9\xdcEU\x95^\a\xefz\a\xb5\n\xaf\xa8\xd34\x14o\x93\xa9\x1d\xc0\xd1\u061c\x1a\x159\xd7\u0741\xeb'\x92=\xa8 \x83\x190\x14\xfc`\x94\"\x05)\xd8O\xe1\u0081\xc5A\xc4\u008a\xb7B`\x00\x16\x15u\x16K~?\x04D\xa5\xcc3\u02fb\u0419\xc6\xf6;~\x80\xdb?\xfb\xb7\xd8~\xd7\x0f\xc1\u02a7\xa1\x14\xc3\x158k\xbc7\xbbx \xbf\xf8\xe2\x17\xe2\rox\xc3\x1f\xbd\xe4%\x97\xbe\v\x00\xae\xbb\xees\xfc\x92\x97\\\xea~\x16\xae\xf3\t\x98\xef\xe7\xeb\x9e{\xee\u0131\xc7>'\x02\xd1\xcf]u\xd5o\xfc\xee]w\xdd\u0163\xd1(9\xd52\x98\x19\xd6\x14\u0226f\xb0\xf5\x97\u07c2\xe7\xbd\xee\xdf\xc3\xe9\x0eVvn\ai\r\xb1\xa6\xd2A\xd7xP\n\u0578\xb7\"\x8d\xbe\xe1\xa5\xc4\u03c6\xe0]\x17\xc2'B\xb5\x1e\xbd5@R\x1e\xab)N\xc7P\xacJ=\xd8G\x85\x82\xef|Q\xa5E\xce\x00\x9d\xc7n-%\x13\x8a*T\xe7\x1a\xa45\xa0\x14\x88t\u0253\x97\x0f?\x04B\xb7Z\x1f\xc6\xd9\xf0HE\u0101\x95\xb4c\x17\xef3\r\x9b\x93\xf1&\x1e\xa8\x85zH&dk\u07d2jH\xa7tMlJ\xfa\x12\x15\n% \\\x01r\xcd\xf0\xb2\xe2\xcb+\xcd_Y\xd1S\xfaC\t\x8dRo\xa26\x00\xbaI\xfb\xb4\x1f6\xea\x89MkT\xe1\u9a40\xe1\a\xc6r\xf6T\xcb\xc8*82\u07bf'd\x8d\x86\xb8W\x18\x01\n\xe7S\x83@\xc1\xfe\x98*k`!B\xd6\xed\x82l\x81\a\xbeu\x1d\xee\xba\xe1\x1f\xb1\xf0\xf0O\xa1t\x0e\x10\xf9\xc1 3\x02AP\x14\x85LMO\xd1\xc5\x17\xbf\x10o|\xe3\x15\xef\xbf\u4497\xfc\a\x00\xb8\xf5\u059b\xf5s\x9f\xfb<\xf3\xb3r\xadO\xc0|?_\x11\xc8\x01\xe0\x83\x1f\xfc\xafo\xbe\xf1\xc6\x1b\x8f\u0736m[\xedx\u034aa\n\x83\xee\xf4\x1cN\xbf\xfcmx\xdek\xdf\t\u05dd\xc2\xca\xe2\"T\xa7\x9b\\t\xae2\x92J\bQ\x0f\x8c\x14\x9aR\f\xd2\x1a\u0119\xbf<\x03\x90;k \xc6@|\x84{Y\x95\xc7x\xb8\xb1+>\x98g\x11\xd7K\xdd\nP\x13\xb8\xf3\x1dN_\x89+\rRY\xa0U\x94\x0f\x9d\xe6\xa4\u04a6\u0291\xd6S%\xd4Z5\x8a\xf8\tG)\xfd_\xfc\xe9\xc3k\xc5)\x89\xabk\x80T\x1aRD\ud217\x0e\u05d0\xb4P!@5\x0f\x934\x16+\x86\x84\xc6h\x97&\xa5CM\xfe=\x8d\x91\xe3\xea1D.\x1c\xc9@O\xeb\xd0\xd1^\x165\x03\xa3k\xdc~rPI\x8b\x80\x12\xed=\x90k\x06r\xae\xf8z\xa5\x18\xb0\n \xeb\xa78\x01(\x12\x14\x16\x18\x05-y\x15:\xc5a(\u0281\x88\x91O\xf50X\u0609\xfb\xbe\xfci\xdc\xf7\x8d\xcfa\xb0\xb8\v*\xcb\x01\x10\x8c\x19z;\nq(\x8aBz\xdd.\xbd\xf0\xa2\x8bp\xe5\x95W\xfe\xe1%\x97\xbc\xf8\u075e\x9a\xfc\xc1\xcf\x14\x90O\xc0\xfc\x19\xb4D\xe4\xc8_\xfe\xe5W\xbd\xe2\xe1\x87\x1fFEM\xfa)8\xb1\x16Y\xa7\x8b3.\u007f\x1b\u03ba\xf2=pY\a\v\x8bK`\xad\xa1:]\x00\x80eU\x19O\xa5\xf3\x87AM\xe0\x91@\x81\x02\x9dA\xac|\xe5\x1a~\x9e\xadop\x8a5\xc1\xf9\xceU!\xbd\xa0\xe4v\x83\xe1S\n\xe0\xc9\xc6C\xb5\xe0\xe5\x00\x82\xac\x00f(\xed\x81\\\x02\x1fZ\xa3\x02D\xea`\x1a\x13s\x9a\x00\x19\x833b\xa3/\xda\xc1J\x04?)O\x02\xe0\u069eR\x8b\xe1\xack\xb2\u06c1\x1c\xa8\xf3\xdbq\u04e1\x96\xffo%\xa2\x1b\x8d\u0438\v\xd5F\xec\x13\n\x05i\xa5\x9fV\xe1\xa8O\xdf\xd3\xf8Sjm\\\x8a\x8c?4\xda\x03aN\xb5\x83Ju\xa6\xd1\xe4e\xa3\xa9\xab\xa2\x04? &\x86\x8d\xea*r(,P\u0113Yz\xe2\x01\x00\xe7@\xa4\xa0\xf2\x0e\x96\x1e\xbb\x1fw|\xeecx\xf8\xc6/\xc1\x16#\xb0\u03bd\xe3\xa45\xb0\xa3!\b\x0e\xc6Z\xbbn\xdd:u\xfe\xf9\xe7\xe2\xcdo~\xf3\u007f\xbc\xe0\x82\x8b~\x1f\x00>\xfc\xe1\xff\x87\xb6n=\xd5\xfc\xac]\xe3\x130\u07cf\xd7O~r\x1b\xb6l9\x19\x00\xf0\xf1\x8f\u007f\uc94f>\xfa\xe81\xbbv\xed\xae\xae+\xf2\xb2\fq\x82\x93.|9\xcex\xfd;\xa0z\x1d\xac\xce/\x87\xa9z\x02\xe9\xcc\xdb\xd0f\xc6s\xec\xc1\xb1NP)\t(P#>E\xa0\xb2\xc0\xa52b\xcc\x03.g\f\xe8,T\xe5.\x96\xc6U\xa3TB|@\xb4aM\xa5\xe5TQ0\x15~q\x05\x86\xf0\xee}~s\t\u0380$e\x80p\x1a6\r4|\xc8\xd3L\xd14\xe6>z\u03d0\x94\xdar\x92\xca\u0696\xc6\xd0+\x81?\xaeZ\xbe\xf5*=\x89\x9a\xab=\xb7\xc8l$\xf6\xba2N\xba\x97\xda\xf6Fu^r\xe1L\x15\xa5B\x95\u007fw)gL\xbcV\x9ae\xb34\xec\n\xdah\x95t\x83l&\xf7\xd5\x1fI\xf3w\tQh\x1a7\x81\xb4\x1a\x8f\xcb\x06\x0f{\x0e`\x0e\u0170\x81n1\x8e`\x85\u0293\x0f#\x89\x00\x14\a\xad3\xa8N\x0f;\xee\xfc\x01\xee\xf8\xa7\x0f\xe3\xb1\x1f}\xcfG\xc7\xe9\f\x80O`\xb2f\b\x88\x85)\n\u0670\xe1 u\xdai\xa7\xed~\xfd\xeb_\xff\x9f.\xb8\xe0\xa2\xf7\x03\xc0\xaf\xfd\xdaki\xe3\xc6\xc3\xe4g\xf1z\x9f\x80\xf9~\xbc\xb6l9\x19\"BD$\xb7\xdd\xf6\xe33\a\x83!Dd\b\xa0\x13/0g-\x0e:\xfc(<\xffW\u078c\xe9\x03\xd7aq\xf7\nD$\xf0\x92\xc1\xc7C)\x90RP\xd1\x1e\x15i2=UG\xf8hO\x1a*\xeb\x18\xd2SzgST\x97\x04g\xc2\x04\x14\xa9\xbc0\xd3[n\f\xcd4\xea\xbc4\u0772TT8)\x15\x18\xe2Z@\xb1\x95\x17\xa8\xa8\x8d\x84d\xaem$^)!-\xdcx\x12\xa9W\xbb\x91\xe4n\xa4Q\xa9\u05ea\xda \x17\x045~!\xcd\x1aM<N\xa4\xbe\x89\x107\a\x81\xe2\xc0\x0e\x05\xd0G\u0660\xac\x85k\xc88\xeaR\x83\xc3/\xc1\x9b\u05ae\xc4\xeb\xa5:\xc6\x1d\b\xcawQjD\xba\xa2\x10\xbc\x14\x9a\xbc.\f\xfd\x94\x8aN\xf2\xa0.*\x00\xbaQpdC\x11\xe1\xfb-\xe9+\xaf;=h\xad\xf1\xd8\x0f\xbf\x81\x1f}\xe2/\xb1\xeb\u079f\x80\x94\xf2\xfd\x9e\xf0\xe1p\xae\x80\xb3\x05\xac)p\xfc\xf1\xc7\u0469\xa7\x9e\xfa\xf0\xa5\x97^\xfa\xab\x97^z\u0677\x00\u0aab\xae\xa4\x97\xbe\xf4\xa5\xf2\xe2\x17\xbf\x14\x130\x9f\xac}j\xdd{\xef=DD\"\"\xeb^\xf3\x9a_=\xe2\xb1\xc7\x1e\x03\x11\xa9\u0503\x9c\x00\x1c\u007f\u03858\xe4\u06130\x1ax\xcfVW\x83LI\xf0\x8eB\xb03j\xa0[N\xd7ES\xa60\xd2.\xae\xaa\x16K>9!\x97\xe3,G\u0273\n%tJC\u007f!\x82:\x1e\xc8\x18\xef\\\u06948\xa9\xe7\x12Q\v\u04a4\\uc*\xbe\xf93\xe5\x8f&\xf4\xcd8\xcd\xef\r\x00\xd2\xe0\x8a\xa6\x99\x155\xeaWj\xdb\\J\x95K]iRe`V\u07c8\x13\xb2\xad1O{\x00\xdbT&)mO{|@u\xaf\x8bQ\x97\xccsz\xa7\xe5f\xe2\xc1Z%_\x12(\x13\xc5\xde\xf91>O'\x02#\x04\x03\x05C\x0e\xa2t\xb0V\xf0fl\xf1\xa4E\x8a\x91\xe7=\xd8a\x1f\xf7}\xf5\xb3\xb8\xe3\u068fa\xf7\xc3?\x85\xd2\x1d\u007f\xaa\n[\x89\xb5\x8583 \u034c\x13O>\t\xe7\x9cs\xceg\xde\xf3\x9ew\xfd\x9f\x87\x1ez\xf8\xfd\x00\xf0\xaew\xfd6\x9d}\xf6Yr\u9957\xfd\xcc^\xef\x130\u07cf\xd7\xfd\xf7\xdfO\x00\xa4(\x86'\x0e\x87\u00e3\x17\x17\x17\xc1\xccdm\xf4iv\xc8r\x8d\u00f7\x9c\x8a|z\x16\v\xfd\x81?\xc6\x06\xdbP'M\x1e\xb5\x91\x05\xb9V\xc1\xab\x02\xa8\xc7\b\x1d\x97\xfc\xbcC9\x86/\xa5\xa7t%\xed\xa3\xa6\xa1\x9f\xa4\xbc\xb0\x946\"13\xa2\x04\xb4\xf8\bc\xd2\x10\u0575t\x94\xec\x0e\u049c%O\xe2\u07e9^V\xd6\xce H(\x8b\xb5\baj\x96\xe4q\x8a\xb2\xac\xdd\u00c8y\xed%L\x9e{\x94(rL\u04a31\xfb\x15\xa0\x95%\xa9\xfb\xbd4\x8a\u548f\x96\xd6B\x1a\x82q\x9a\xa5\xc9\xe2\xd4\xda\xce\r\x11P\xaa\x9e\xe1&M\x13$\x8b\xb1\x1a\xd7T\x9d\xb9\x84\xaasX\xf4\xcd1a,_H`\x89!*\x03\x87f5\xfb@(\x88#\xa8L#\u02fbXz\xe2A\xdc\xfd\x85kp\xff7\xfe\t\x83\xa5y\xa8\xac[\xebS8;\x92b\u0627^7\xc7\x19g\x9c\x81\xcb.\xbb\xec#o}\xeb\u06ee\xf8\xe0\a\xff\x02\x17]t\xa1\xfa\xe7\u007f\xbe\xc1\x12\x91\xfc\xac_\xef\x130\u07cf\u05ed\xb7\xde\n\x00\xf8\xeew\xbf3\u06b6\xed\t\x93$\x8c\xfb\x0f\xb9\x00y\xa7\x87\xf5\a\x1f\x8a,Wp\xcb\x16\xb6\xf4\x84\x1e\xbf\xc0\xc7*\xb8\xe4\xf8.i\u015ap\xc4\x12\xc0\x9dR\x1e=\x863KmN\xbd<-\xd4F\xbe\xa9Fs\x8f\xc9\xff\"\xf7\x1a\xdd\fE\xa4\x1a3O\xe5\x13\x89\x96\xbc\xae\x90\xa9N\r\t\x05_\xca\xdc\u028a|\u0301\xb0\xd1\xe0ly\x81\xa8\x85\x18\x1a\x93\"6\x90\xb9m\xf0\xa6\xed\xd0@\x18\xf7\xe8*\xc1vM\xe2\x1a5#.iT\xeac-\x80\x96;\u07db\u0405\x92\xcfN\t2\x04d\u0283\xb9&oM\x1b\u007fVQ\xf5Yt$p\xe2-~c\x18\x92\x84\xe9Md\xfe\xf5c&(\ue049Q\xf4W\xf0\xc8M_\u015d\xd7^\x8dm\xb7\xdf\bg\r\x98\xb3*l\x1a\x04g\x8d\f\a\xabt\xf0\x86\x83p\xca)[\x17.\xbb\xec\x17\xdf\xfe\xa67\xbd\xe5o\x01\xe0O\xfe\u43f2w\xbe\xf3\xb7\v\"\xda/\xae\xf7\t\x98\xef\xc7\ub847\x1e\x02\x00\xdc~\xfb\x1d\xb4\xb8\xb8T\xe5D\xa6\xd8\xe9\x04\x9c\xe5\xc1\a\xc4WK.a4\xdcZ\xc7p\xaa\x83\xebZ\xd7\xc3x\x85G5>\xb5f\xce\x1a\x80\xbef\xe9\x9aR\xda\x01\xa0]\xf2\u007fI<\xa9\xaf\xf6\x1458\xe0D_\xbd'\xc1s\x8b\x11UIaP;\xbe\xd5\x14\x1fi\x1a\x8fT\xd5p\n\xacT/\xe21\xa6\xf2\xdc\xd3\xc6\u0664J\xd2j\xba\xe1\u007f\x024\x95#\xed\xef\u025e\xda\a\xa9\x8c\xb3mci\xda\xd8R\vM\xa3\u0607HDJ\xc5\x03\xbd #\xf2\xa1\x12a\xe0G U\xe0r\x8d\u0593\xe4\xc4%Py\a:?\x00\xa6\xbf\x84\x9dw\u078c{\xbe\xf4I<\xf0\x8d\xcfbe\xc7c`\x95y\u014a\xf3\u039d\x9e\xa6rR\x8c\x86t\xc4\xe1\x9bp\xce9g\xcf_y\xe5\x1b_~\xdey\x17|\r\x00~\xef\xf7~\x976o\xde\\\xecO\xd7\xfb\x04\xcc\xf7\xe3\xd5\xef\x0f\x00\x00\xcb\xcb+0\u01b46\x02\xfb\xab\xcb\u8bec\x04\x06\x84a}\xf9\\S-H;\x05\\\a\xa5\xe4\xfbM\u0395\u05a8\xea\xeb\xf5jThP\"[\xab\x98XnX\x8dx'\x01\x81\xb3\u0271\xdf5C\u0712Z85\x9cj\xf3\v\xa7\x96J;\xf9y\xda\x03\r\x9d\xe2\x0e\xed\x85\xf2Hi\x8a6\x00LM\xb6\xa8\xc1\xd9K\xe3\xf5\x96\xc6\f}\xbbU@\x02\xcc2\x0e\xf6m?\x9b\x9a;\x8eY\xd36\xfe\xa4\x96\u075b \xa1\n\xf7i@\xf1\xb8@\x94\xf8\xd3\v` q\x0e\xcd[\n\xa3jZ\xa7Q\x81\x00!\x9f\x9e\x83\xd8\x02\xbb\xef\xbd\r\xf7~\xed3\xb8\xe7\x8b\xd7`\xe1\xe1{\x82\x0f\x0e{7c[\xf8\x01\xb8\x80\xff\xa6\x18\xd2a\x87m\xc4%\x97\\\xfc\xf0o\xfe\xe6Uo8\xe5\x94S\xbf\x06\x00g\x9f}6\x1dq\xc4\x11\xf2\xcaW\xfe2&`>Y?\x13kzz\x1a\x00\xb0n\xdd\x01\xd0ZS\x1b\x98[\x01\x1e\xbb\xfb6<\xfb\xdc_\x84\xca2\xc0\x8cJ\xa0qu\xf4+\x8bT'-<\xed\x1eh\x81\xf1\n\x93BVg2Q\x1a\xf9\xe1d#`\xd4\xef75dt\xf0\xde\x1c\xa4\xe3\b~x\xbc\x8d*\xb8\x1cj\xa2\x06O,\xb4f\xb5\xdd\x046\xa0\xddKdOTC:H\xda\xe4\xe2\xa9\xed\xa4\u04e0^\xa2\x94\x9dZ\xee\xdf5*zY\u3157f%\x1e\x01\xbf\xa5\xe1K\r\u03bby;{z\xee\x94\xd0m\x8a\xbc\u02e1\xa2\x94/\xf7wl\xc5W\xe2&\x98_9T\x8a\x14'\xa8\xbd\xd7\x11\u01f9\xd3\x05\xeb\f\xbb\xef\xbf\x13\x0f~\xe3\x9f\xf0\xd3/}\x02;\xef\xb9\r\xce\x14\t3\xe7\x1b\xf7\xc4\u02a3\xb83B\x04\u06b2\xe5D\x9c~\xfa\xe9\x9f\xfd\u0407>\xf8V\xa2\xfc!\x00x\xc5+^N\xaf}\xed\xe5\xf2\xf2\x97\xff\xd2~w\xbdO\xc0|?^g\x9du&>\xf0\x01\xe0E/\xba\xa4\xf3\x0f\xff\xf0?r\x9f\xe03~Y\xfe\xf0\xf3\xff\x88\xe7\\\xfcj\xac?~\v\x86\xa3\x11\x8ci\ua269\x9e\u053e\x06pK\xa3Z\x17\xa9Q\xcc\xe9\xa19\x0e\x1d6\xc2\x1a*q\x9f\xa2\xa4\"\x0e\xa0j\xa5Q\x95\xa2r\x1etX\xeb\u030f\xb1\xc6\xdfZ\x15\xf6\xbfF\f\xb2'*\xa9\xf5\xb4\xd22h\x93\x16\xff\xae\x85\xedi\xbb\u03f1\xf9$Z\x9b\xc7\x1es\x14h>\xb6\xa4\x1a\x8eT\x9a\uc067\xdf\x13\x8d\x1e\xe9\x16\u037e\x12\xd7\fd\u025b\xee\xc4\x0f\xf9\xd8p\x92\xaa;$W\x03K.\x8c\xe9#PcJi\xa8\xbc\x8b\xfe\xeem\xb8\xef\xeb\x9f\xc5=_\xbc\x06\xdbn\xfb\x1e\u0330\xdf\xf2\xfa\x87\xe9_\x888k\xe9\xc0\x03\xd7\xd3\xc9'\x9f\x8c\xf3\xcf?\xef\xaf\xdf\xfd\xee\xf7\xbe\x93\x88\x16\x01\xe0\x8f\xff\xf8\xfdt\xee\xb9\xe7\xca\x19g\x9c\xb5_^\xef4\x81\xbc\xfdw\xddz\xeb\x0f\xe9\xb9\xcf=EDD\xbf\xf0\x85\x17}\xfe\xc6\x1b\xbf\u007f\xd1\xc2\xc2B\x01 k\xfe\xec9\xaf\xfb?\xf0\xa2w}\x00\xab\x05\xb0k~\x11&\t@p\xad\xf9\xedT\x06\u8d8dl73}\u04df+\x81<\x06\u0460R\u03a4\xe0\xe5\x12\x00\x8c`\xdeVIK\x8b\xeap\f@\xf1\xaf\xb4_\xc5\xda \xb9\xa7\n\xbc\xf5\xff\xa5\xae\xf2\xc0\x1a\x8f3\xd5~G\xa0N)\x13Yc\xd3L)\x1d\xfaW<^iy({\xba\x8dh\xa6\x16\u04d5\xe2\xfb\xc0I\u04d9#\x80\aKZ\x1d\xee\xa0pQ^X\xf5`\u01ac\u0723\xc6'\xdc9\xb3\x02k\r\xcer\x10\x80\xc7~\xf0u\xfc\xe4\xd3\u007f\x8d\a\xbf\xfd\x05\f\x97\xe6[@<L\xff\x86\xb8\xb8<\xd7x\u05b3\x0e\xc1\xf3\x9ew\xca]\xafz\xd5+\xff\xecW\u007f\xf5\xf2\xbf\x01\x80\x0f\u007f\xf8o\xf8\x8a+\xaet\xfb\xfb\xf5\xce\x13\xc8\xdb\u007fW\x00r\"\"\xb3i\u04e6o\xcf\xce\xce\x02Q\xf1\xd6X\xffr\xcd_\xe0\xe6O\u007f\x14\x9c\x01y\xb7\x97\\\xe4\xf5\x9c\xb3(\t\x94\xa0Q\xe7\x16\x8a\xa2\xa6hi)\x96#?\xad\xaa\x10\xf6\x9a\"\x06 \x98p$w\x12+G\x1a?U\x10\xad\xcd\xddJ{UL{\xa1\x81\xfeU\u07e3J\xf3\x9d\x98 \x8e}I\xe3g\xc6B{\x9a\xaf\x1daL\u07ce\x96\xd7xlR\u007f/\x95\xf4\xda\xd56\x95\x1b\xa6j\xde>\xb5\xbcv\x84\x92\v\xcf\u060f\xe0w\x15\xa1\u02c4\x9c\xa3\xb4P0r\xc0\xc8\tFNj@.\xd2\xdc\xc0\xc2ILk\xe4\xbd\x19\xe8\xde\x14\x88\b\x8b\x0f\u078d\x9b>\xfc>|\xed}W\xe1\xee/^\xe3\x81|,\xfeNy\x97O\u71cb\x0e<p\x1dN;\xed4\xbc\xfa\u056f\xba\xfe\x03\x1f\xf8\xc0\v#\x90_}\xf5G\xf9\x8a+\xaet\u007f\xfe\xe7\u007f\xba\xdf_\xef\x13\x9ae?^_\xff\xfaW\x11\xf5\xb3o|\xe3\x15\u07f9\xe5\x96[\x17\x1fy\u4479\xb6\x13\xbd)\n\\\xff\xa7\xef\x84\xd1]\x1cs\xf1\xaf@\x15\x05`m\xb8\b+\rw\x8a\x95\xc1\x03z\xbc\xb1\xd6\xc2\x17\x8c\xe5\x05Q\x18\xb2I6\b\x97T\xf6q\xb3\x18\xcf\xf8LO\x002V\x1d\xcbZ\x1c\xc3^\xa8\x92\xbd\xfdl:\xc9\u0674\u007fM+\u0174\xc1\xd7\x06\xde\xd8\x03\x95\xd2<\xcdP\v\xfdB{\xa4=R\u05d3::Sku^\xd1]%\x9d\x95\x9a`\x05N;>\x1e\x9dLmj\xf6\xd6\x03L\xf5\n\xdc\x04N\xdco\x9cT\xdbW\x89\xc2\xe8=|'Ck\r\u055d\x02 \x18,\xec\u0093w\u0742\a\xbe\xfd\x05<v\xf3\u05f1\ubf9f\xc0\f\xfa\xcd]\xbe\xf2\xdcwN\xc4\x14t\xd0A\a\xe1\xb0\xc36\xe2\xc4\x13O\xfc\xf1\x05\x17\x9c\xff\x97W^y\xd5\xdfGZ\xe5\xdak?\xcb/}\xe9/\xba\xcf|\xe6\xd3x\xd9\xcb^\xb1\xdf_\xef\x13\x9a\xe5\x19\xb4~\xe37\xde\xf8\xd9\xcf|\u6cd7n\u07fe}\xbc\xd4\f\x17\xcb\xdc\u01a3p\xc9\xef}\x14\x1bO\xf97\x18\xf4W\x82\xd6\xd7\a\x00\xa4:\xb8V}s\vHq\nz\x01\x8f\xe3\xf0\b\xc1{q\xb8\xd0\xf8L\xaf[\xb7\x87\x90\x9eVj\xa7\r\x19[\xa8\x96\xff\x15:\x05-\x95jM~\xb8\x87\x8d\x80\xf6r\u007f\xf2\xff\xe3B\xac\x816%\x96\a\xb2\x97'\xd6\u0417\xb7\x9d\n\u048d$\xddDJks\xf2R\xc3\x0e\xd7\xe7W\xad\bF\xb6\xeag\xc4|\xcex/5\x9aM\x00\u07f7ad\xd3\xd3`\x01V\x9f|\x02\x8f\xfc\xe0k\xf8\xe9\x97>\x89\xc7\u007f\xf4]\xac>\xf9\xc4\x1a\xaf?\x05\x1fr\xb2\xb6\x18\xb9u\xeb\xd7g\xc7<\xfb\xd9\u063c\xf9\u87dey\xe6\xf3\xff\xdb\xdb\xdf\xfe[\u007fCD%\xa1\xfew\u007fw5]~\xf9\xeb\xe4\x99t}O\xc0|?_\x9f\xfc\xe4?\"J\xb0\xbe\xfa\xd5/\x9fu\xd5Uo\xfa\xd4\xddw\u07fdqO\xbf\xb3\xf5\x97\u07ccs\xff\xaf\xff\x86a\xbf\x0f\xe3\xbc\xe6\xc0%~\xdee\xf5\x89\xb4\x8a[;\x977\xf5\aA\x00s\xae\x02\xdek\r\xb8\xe8\u0451zm\xd5\xf4\xdb4\x0e\xf0.i\u6943N\xa9\x94\xb2)\xb3k\x1bY\x97\x96*|\xefT\xc5\u068a\x17\x91q*\xa6mx\x96\x1a\xb4Jk\xc61\xa1\xe6E\xd2\xd4\xfbKc6\xaa\x91i\f\x91\x8a\u058a?\xdf\u059c\xe6@\xa3\xa8D\xaa\x19u\xe21\xc4\u00c6\xc1\x1e\x13\xbc\xe6\u0460Q$\xd26Toz\x131\xf2\xde\f\xc4\x19,<r\x1f\x1e\xbf\xf5;\xb8\xff\x1b\xd7\xe2\xe1\x1b\xbf\x8c\xe1\xf2\xfc\x9a N\xac\xbc\u07fe\xb5\xae\xd3\xe9\xf0q\xc7>\x1b'\x9cp\xc2\xe3\x17\\p\xde'\xae\xbc\xf27\xff\x84\x88\x1eM\u007f\xe7\xc6\x1b\xbf\x87\xe7?\xff\xccg\u0735>\x01\xf3g\xc0\xfa\xad\xdfz\ab(\xed\u55ff\xe6=\xd7_\xff\u03ff\xbfc\u01ce1\xfca\xe5#\xe3\x9e\xf5\x9c\xad\xb8\xf4?}\n\a\x1c\xbd\x19+\xbb\x97|\xb2O\xe9\xa1RM\xbb\xa4\x81p\xcdj=\x8eo\xa3\x99l\x8fj\x80\xa4\xe4\xe0\x13\x80\xb1\rpX\x8b\n\xdf\xe3\x9c\u0358\u007f\xeb\xdaMPZk\x8eH\xc6\xf9c\xd9\x03\xc5Q\xbb\xfd\xda\xff'\u05b9\te\xd5\u49db\xf7W5\x1b\x83\xdb\xe0\x1a\x9b\x95\xb4\x9cL\u0186_\xd1p/\xa0\xea=\xe2F\u056e\x89\x02\x95\xe2}T\xac\xab\xde,\x17\x14)#G\xb0.\xd17%Fk\xe9f\xcb\xf1\x15P\ny\xb7\v3\x1aa\xe7Oo\xc3#?\xf8:\x1e\xf8\xe6ux\xe2\xf6\x9bP\xac.\xb5\x03Sp\xe3,CK 8\xe4Y\a\xe3\xb8\xe3\x8e\xdbu\xce9g\xff\x8f\xdf\xff\xfd\xf7\xfd\x15\x11\u0756\xfe\u03bd\xf7\u0783g?\xfb\xd8g\xecu>\x01\xf3g\xc0\xfa\xc8G\xfe_\xfc\xfa\xaf\xbf1\x80\x89\x1cp\uee7f\xf0\xc9o~\xf3\x9b\x17:\xd7\xeeq\xca\xcc8\xe1\u016f\xc1\x19W\xbe\x17sG\x1e\x8b\xe1\xb0@Q\x14\x10g\xe1b-^*Y\xa8\x01\xec\xd5m\xa9\x84\x03\x88\xba\xe3X\u5e44#\x8f|y\xe4mC&\xd1X\xc5\f\xaa\uedd5{nC\xf9\xa6\xcdk\v\xa72\xc6}\xaf\xc5\xe7\xa0e\x02\xb26\xfe)\xf5\r\xa3\xb4}\xa5\xca\u05e6e\a\x8aI;M@\x8f\x9e.\xdeU\xb0\xb1\xb1H;\xdf.\xc9k]\xbe\xee\u912bT NR\u077f\"Ow\xe9\xa0\x15\x8f\x16\u0205\x93\x92B\x89\u0787\"\xd28uH\xa9\x18W\xd1\x06\x81\t\xac2\xe8N\x0ek,\x1e\xbf\xed{\xb8\xef\x9b\xd7\xe1\xc1\xef\u0740\x9d\xf7\xfc\b\xb6\x18\xb5\xa2Q\x19p\x12\xde\xebLkt{\x1d\x9cx\xc2\t\u063a\xf5\xb9?\xb8\xe0\xfc\xf3\xdf\xf1\xcaW\xbd\xfa\xeb\xe9\xaf\xfd\xdd\xdf]\x8d\xcb/\u007f\xdd3\xfe:\x9f\x80\xf93d\xfd\xf8\u01f7\u2913\x9e\v\x00\xf8\xda\u05fe\xb2\xf5\xfd\xef\u007f\xff\u05ef\xbf\xfe\x86\xb9Vr84\x167m=\a[^q%\x8e8\xfbE\xe8\x1e\xf8,\x18\xe3P\f\ap\u0468\xab\x85w\x8d\x00V\xafM\xa5\xf4\xb3\x8eG\xf8\xf8;&\xe1\xc7K\x87\xbb\xa4t\x96\x96t\x84\xb1\xea\xbcm\x80\xa6\r\xe9Z\x88\xf65\xbdRP\xc9\xef\x9a\x00\xdb\x1c\xe0\u065b>[jS\xad\xd5\xf3\xad\x03yx\xf6m\x8a\x97\xb0\xf9U:q\n\x95\xaf\xff\xa6kP[Q:\x18+\xed\u0606\xb4e\xe5N5\xc0\x8f\xf7i\xa4\xd2RF\x8f\x1eA4.KN\x19\xa8d\x89\x11\xc0\xe3\xaeKD\xe0,\x83\xce;p\xd6b\xdb\x1d?\xc4\xdd_\xfe\x14\xee\xff\xe6ux\xf2\xbe\xdb\xd1\xf4\a\x8a\x0f\xa0\x04p?\xf5C\x9dN\x8e\x99\xe9\x19l\u06b4\t\xcf{\xde)\v'\x9ex\u009f\xfc\xd6o\xbd\xf3CD\xb4\v\x00>\xfe\xf1\x8f\xf1k_\xfbknreO\xc0\xfc\x19\xb9n\xba\xe9\xfbt\xdai\xa7\v\x00|\xe2\x13\xff\xf0\x92\xff\xfc\x9f\xff\xeb5\xdf\xfe\xf6wf\xdb>\x16\x14F\xfa\xa7\xd6o\xc0\xc6\u7783\x13^\xf6F\x1cv\xea\xb9PS3\xb0\xc6\xc1\fVk\x8d\xae\x8a\xb2\x90\x06\x8f^\x81I\x12(_\x1bXI\xc1\xbcf\xb0DU\xd5O\r\xad\xf2ZMKip\xed\xcd\x1fn\xca'E\x9a\xbe1\x82*\xbb\xa1\x82cn\xb1*H=l\u02a1\x9e\xf0}\xa6\xf1\xfec\x94]J\xa8d\u02f0\x86\xe4a\xa6\x1b\x9dC}X\b\t\xe0\u01e0\xb44\x06/Fw\xc6\u7903\xc3c\xf4\b72\xde\xe5\xb0R\xdf\\R\x8a\xab&-M^\u0426\xb5B\xa9Ya\x8dNo\n\"\x82'\xef\xbb\x1dw|\xe1\xbf\xe3\xbeo~\x1e\xbb\x1f\xb8\x13f4h1\x81!\x1f$\xce\f\x11'\xe2\x84\xe6\xe6f1;;\x83C\x0e9\x14g\x9f}\xe6\u03a3\x8f>\xfa/^x\xf1%\u007f{\u0496\x93\x1e\x04\x80W\xbe\xf2\x15\xea\x13\x9f\xf8\x94\xdd_\u0331&`>Y\xff\xcb\xeb\xaf\xfe\xea/\xf8Moz\x8b\x03\x80\xab\xaf\xfe\u0605\x1f\xf9\xc8G\xae\xf9\xcew\xbe\xbba4\x1a\x19\xa5\x94\xb2\u05b5\xea3f\x0f9\x1c\x87\x9dz.\x8e\xb9\xe8Wp\xe8\xd6s\xa0{3(\x06\xabp\x81\x96i~\xa4\u04a37S\x134+d\xa5\x06\xa7]w\xf1\xab6\x95\xa6\xba\xa5\xc9\x17\xd7\x14\x19M\xab\xdb=\xad\x06h1\x118\xdcge\x0e\x15\x81\x8f\xcaA\xa7X\xc4:Y;u'\x1d\xb2\xa9\x01:\u0579\xf9\x96t\xb9\xbd6C}\x90\x03\x95\x9bb\xc6\x04\x15\xc0\x19\xa5\xc5,\xd5\f\u04cc\xabd\xa4\xae\xda&\x91\xe4\x8d\xd4\r\xba\xa4\x02o$\x9b\x05\xa5\xd2$q0\u0387C\xe4\xdd\x0e\x96\x1f\u007f\bw\xdf\xf0I\xfc\xf8\u068fc\xe7Oo\x83\xb4T\xe2Q\x99\xa2\x98\xc59g\x8c)p\xd0A\af\x1b6\x1c\x8c\x83\x0f>x\xfb\x19g<\xff\xd6\xd3N;\xf5\u00ff\xf2+\xbfz\x1d\x11-\xc7\u07fb\xfa\xea\x8f\xd2\xeb^\xf7z\x99\\\xc5\x130\x9f\xac\xb0>\xf4\xa1\xbf\xe4\xdf\xfc\xcd7;\x00\xf8\xf4\xa7?y\xc1\xd5W_\xfd\x0f\xdf\xfa\u05b77\xec\xdc\xf9$\x98\u0649\b\xa7>.13\x93\x94\xc6\xf4\xc1\x1b\xb1\xf9\u0717\xe1\x84\u02ee\xc0\xfag\x9f\xfc?\u06fb\xd2\xe0:\xaa3{\uef7d\xbcE\xfb\xd3\xf2$\u02ca6\x8c\x1d/x\x19\xc6\xc66v\x8c\x1c\x98\x94\xcb\xd4\f\x1e\x02\xe4G25E\xe18\x19\xa8\u0250T`H\x82a\x96\x84Jf\x8a\u0270XP@\xa8\n!\xc9\x04\xc8x\x02\x89\a\x13\x1b\xe3\x15\x19[\xc6c\xcb\xd6bY\x8b%\xdbOz\u04b3\x96\xb7t\xf7\xbd\xf3\xa3\xbb_w?=9T*\x10\x92\xf4\xf7G\xb6\xf4\xd4oQ\xdfs\xbf{\xbe\xf3\x9d\x0f\x86a@OM[G\xe5\x1c\x8b\u05dc\xbc\xd3[\xfa\x83\x8bs\xc7\fj\x06\xee6}\v\xa0DNES\xe4aT\x80Y\\\r?\xc0\x02p\x80\xdb\x04t\x99\xda\xca\x0e\x93;\xd6sN\x10N\x929s\xd7\x103\xb2~GuCs\x00\xde\xfe\xbf\xbd9\xe4\xb6\xf0\xbb\xaf\u0248\xa3\xef'\xd9l\xdetx\xb4\u01aaZ\x80k\xda\xc9\xdat\x89\xbb{\x96{\x8a\xc1b\xc6)\x04\x96~\xdcV\xbc\x10\x97g\x8b\xd3[`\x1e\x9f$E\x85\x12\n \x99\xb8\x82\xc1\xf7\xf6\xa1\xfd\xa7O\xa1\xef\u0777\xf2r\xe2\x84R\xd3\b\x8b1\b\xce\rn\x18,\x12)CCC\x03\xaa\xab\xab/,\\\xb8\xf0\xf5\x96\x96\x96\xc7ZZ6\x9es\xff\xde7\xbf\xf9\x10Y\xb6l\x99\xb8\xed\xb6\xbf\xf6\x17\xaf\x0f\xe6~\xe4\u01b7\xbe\xf5M\xf2\xe8\xa3\xff$\x00\xa0\xad\xed\xf0\x86\x1d;\x9e\xf9\xcf\xfd\xfb\x0f,<w\xee\x1c\f\xc3\xcc\xef8w(Ij\xb5M\v\xc1A)Ct\xc9\r\xf8\xe4_\u074dO\u0738\x19\x81\x92\x12h)\x1dF&\x85\\\x8d_>s/\x01\xaf\u0305\xe4\xe4\xa2\xc45\x9d&\x97\x1b\xcf*'0\xcb88\u0324C\x90\x876\u0235\x17\x80\u02e6\xd56\xf9\xa2\xc4\xecj\x14 \x969\x94p\x81\"q\x15\x05\x85\xd7\u070b\xe4Q\x9c\xe4\xd0\x14\x94\x98\xf2=B\xecY\x1e\xf6\xa7 r\xe4\x92\xe6\xc6B@\xac\xe767\x99,\x03\xef\xca\xfeM\x1bY1\xc3\u01c6\xbbOA\xc8\xd5\xec\v\xab\x17\x80xL\xbe$\xd7H;\xee\xca\u0429e\x8aEe\x19JH\x8561\x81\xe1\xe3\xef\xa0\xe3\xcdW\xd0\xf5\xf6N$\xc7F\xf2\x838\xa1`\x8cZ\x9b\tGEy\x05\xe6\u039d\x8b\x9a\x9a\xe8\xb1\r\x1b6\xec\u07f6\ud2ed\x8a\x12<\xed\xfe\xbd\x05\v\xaeEkk+\u05ad\xfb\x94\xbf`}0\xf7\xe3j\xf1\xe0\x83_'\xdf\xfe\xf6cV3\xa5\xa8\u07fe\xfd\xe1\xc7\xf6\xed\xdb\xf7\u064e\x8e3\x88\xc7\u3982\u015d\xa1Sj6n\x00\xe0Z\x06\xc1\xb2J\xd4\u07f8\tsW\u0742\xaa\xeb\u05a00Z\vC3`his\xaa}\x0e\xaa\x12\xe1:\xda\xc3\xd5\xe5\txG\xb3[w\xa6c\x80\ubc3c\xd9\xd6~kX\xb4\x10\xb3\xdf\xd0\xf9\x9a\x98\xf2\xb5\xc5S7\x9dB\\\xad\xed\"\xbf.<WS\x9d\xcb\xc7\xcf\xec\xd1\x17N\x03\x15q\x0f7\x16\x96\xae\x9bf\xb5\xf7\x0e\x95\"r\x9as\x9cL\x99\xbb^\x89)\x17$Y\xe9\xa3]o0\\j#\x87\xa3\x9f\xf99\x10\x17}\"\x11\xa7\x11\x88YER\xcd\xdap\x19e`\x8c\x81\xc92\x88\x04L\x8f\xc6\xd1wd7z\xf6\xfe7z\x0f\xfc\n\xc9D|\x16\x10g \x14`\xcc\x04\xf4\xca\xca\n,\\\xb8\x10\x8d\x8d\r\a\x1b\x1b\x9b^\xfa\xcaW\xfe\xe1g\x84\x10O\x17\xdb\xf7\xbe\xf7]|\xf5\xab_\xf3\x17\xa8\x0f\xe6~|\xd0x\xe2\x89\uf8e6\xa6\x06\xf6\xf1U\b\xa1\xb4\xb6\xee\xd8x\xf0\xe0\xc1\xad\xdd\xdd\u0777\xf6\xf4\xf4`ll\f\x99\x8c\xe6\x90\x1d\xd61\x99\x10\nn\x18\x10\x82#TZ\x89\xaa%\xabP\xbfv\x13\xeaV\u007f\x06\xe1\xca90t\x1d\x86\x96\xb1\x94/\x0e\x0fmg\xb2\x1e\x05\x8a\x98Y,txh/Q\x92\xab84D~je6\xfa%\x17\xe0i\xce\x00\a\x96\xd3\xfa\xee\x06k7\xbfo\xcb\xf4`78!\xff\t\xc0\xcb\x15;\x198\xb1\u032b\x18\x00j\x8d\x88c\u01247l\x8bX\xf7k3r(\x9e\xecf\x92;\xddIx\u007f\x0e\xe4\x11\xf3\xb8d\x89\xcc\xea\xec\x94\b\xc9\xcaE\t1\xff\xc6L\x92!@\xc1\x98\xf9\x1a3\x93\x93\xb8|\xf6\x04\xfa\xda~\x8d\xc1\xe3\xfb1\xf4\xfeAd\xa6&g\xbcIJ((%\xd6\xf0n\x01\xc6\x18\xa2\xd1*\u031f?\x1fUU\x95\xad\x1b6\xdc\xf4\xcb/|\xe1o\x8e\x10B.\x02@M\xcd\x1c\xf2\xdcs\u03c8\x13'N\xa0\xa9\xa9\t\xb7\xdf~\x87\xbf8}0\xf7\xe3\xb7\t!\x04\x1eyd;\u06fe\xfd\x11\xc3\x06\xf5g\x9fm\xdd\xd2\xd9\xd9\xf5\x8d\x83\a\x0f~\xb2\xa3\xa3\x03cc\xe3\x0e\xa0[]y\x84I\x96\x1e\u06000\f\x04\x8a\xcbQ\xb5\xf0\xcf\xd0t\xd3\x16\u052d\u0744\x82\xf2J\xe8\x9a\x01-\x9d\x02\x11\xe6du\xe1\xd2,{\x00\xd0C18\x15S\xe2\xa1f\xe0\x9a\xa4\xe9\xe2\xcdg\u0443_ej\x9a\xa7\x89\xc6]\x90\xa49\x13\x1e\xf290\xba\x1bw\x04f\x1f\xe2\x90\u07e4\xcb\xedDh\xfa\u04d8\u067a\xc8z\x9d\x88<\x83\x96!r\xadj\x9d\x13\x8e]\xa0%\xae\xcf\x17\x989\xc9\xc8\xdd4\xc4\\\x9b\vc\x14\x8c\u0240$;:|\xc1a\xa4\xa61y\xf9\x02FzN\xe1\xe2\u98f8\xd4\xf1\x1e\xe2\xe7;\x91\x18:\xef\u027e\x9d\x8d\xd1j\xf2!\x14\xdc\xd0\x05c\x8cD\"\x11,X0\x1f\xcd\xcd\xcd\u03efX\xb1\xe2\u026d[\xbfx\xcc~\xfc\xe2\xc5K\xd8\xf3\xcf?+\xae\xbf~\xa5/1\xf4\xc1\u070f\xdfetu\x9d\xa1\x0f?\xfc\b\xfb\u044f^\xd6,P/|\xee\xb9g?\xbfw\xef\xde\a\xde}\xb7\xad\xb6\xbb\xbb\an\x1e\xdd\xee\u04a3\x8c\x81P\t\x9c\xeb\x10\x86\x8e`I\x05j\x96\xaf\xc3\xe2-\xf7 z\xddZHA\x15\\\x17\xa6\xfa\x85s\xcblIx@z&\x14z=@\xdc\xee\x8d\"\xcb\xc5\u007f\x10W\x15x\xb8x\xb7$\u041d1\x13\xfb\xf4\x007_\rOJ\xce-\x030\x8f\xdcp\x96\x85$f1~'y\xb6\x1a\x87~q\xb6,\xe2\xfa\xbe\xbbx)\xf2\x8c\x87\x13\xaeS\xc6\f\v\x84\xec{4\x87hg\a+\xcb2$E\x81\xa4\x98\x04|r:\x03#=\x8d\xd4\xf8(\xc6\xfa\xbbp\xf9l;\x86\xde?\x84\xf1\x81nL\x8e\\B21\xea=\xd1\xc8\n`\xdd\v\xc4*~\x9b\x1b\x91\x10\x86\xa1\x93\xf2\xf2\b\xaa\xaa\xa2X\xb2\xe4\xba\xf77o\xde\xf4\xe4\x1dw\xdc\xf5\x8c-'\xbc\uffbfS\xee\xbc\xf3\x0e\xe3\x86\x1b\xd6\xfa\x12C\x1f\xcc\xfd\xf80\xe3\xc0\x81w\u021a57\nW\xe6\x1e~\xec\xb1\xef<\xbeg\u03de\u03df8qB\xb9x\xf1\x12\x00h\x84\x10\xd9\x01\v3+\x03Hv\nLQM=\xea\xd7nBs\xcb\x16\x945.\x84ZRf\x0e+H&\xad\xe6\x11\xc7\xff\xd6\xe3o\r/\xf0e\x8b\x9d9z\xf1\xfc\xae0y\xb2qO\xf1q\xc6EL\xbe\x1a\x8e\xb6\xdb),\nO\xd3\x0e\x17\xf0(\xc3E\xbe4|Vt\x9f\xc9\x059\xde5N\x11\xd8,\u019a[\x8e\x9d\xf3r\x97\xcc3\xf7rY\xeb\x81\xec\fW/\xc9D ,\x9bXU\x91A\xe4\x00d\x85\x82gt\xa4'\xc60=2\x8c\xf1\xc1s\x18>\xfb>bg\x8ea\xe4\\\a\x92c1\xa4\xa7&\xc0\r}\xc6\xe7I)\u02f6\u061b_\xad\xb1mB\x18\xe9t\x9a\x97\x95\x95\xc9s\xe6\xd4`\xfe\xfc\xf9\xf1\x95+\xff\xfc\xdf\xee\xbf\xffk\xdf%\x84d\xc7\x02}\xe7;\xffJ\x1ex\xe0\x1f}y\xa1\x0f\xe6~|T\xf1\xea\xab?\xc3\xee\u077b\xc9SO\xed\xc8.\xbcw\xde\u0673\xf1\xe5\x97\u007f\xf2\x8d\xe3\xc7O\xac\xef\xec\xec\xc4\xe8\u8a1d\x9cR\x0f\x8d`\x1d\xbd\x05\u7812\x84py\r\xe6,[\x83\xba\x1bnA\xe5\u0095(\xaem\x02Q$\xe8i\u00f4\b\xe0\x1c\x82\xebV\xd6\xeed\xa6\xe6\x14!W\xcb\xd1U-\x02\xf3\xd3(\xf4K\xd5\x00\x00\v\xecIDAT\"\x1e\x83+K\x8d\xe1\xc9\xfc\x89\xfd\xe2g\aL\x9b\xe20r&M\xb8\x9b\x99f{\x1dW\x1b\xd6\xecH\xfeL\x12;+[\xb4~n\xb8\xb2\xf3|\x1c8r\b!\x02\xaf2\x881\x06EU!)\x12\x88\x00\u04898&c\x171q\xb1\x0f\xa3\xbd\x1d\x88u\x9d\xc4\u04296\\\x19\ua0de\x9a\xc2\f{\a\xf7\xe9\x8b8~\xf2\x0emC@\b\x11\x9a\x96\xe1\xa1P\x98\u035bw\r\xea\xea\xea\x06\x97-[\xba\xf3\xcb_\xfe\u04bfG\"\x95=\xeekuvv`\u07bc\x05\xfe\xe2\xf2\xc1\u070f\xdfG\xb4\xb6>\x8d\xad[\xb7\xb9\x8e\xf9B~\xe9\xa5\x1f\u07bdk\u05ee;\xfb\xfb\a\u059d<y\x12\xf1x<\xef\x8deN\x81qRc\xb5\xa0\bU\v\xaf\xc7\u0715-\xa8\xb8v\x05\xd4\xd2J\xc8\xc1\x10\x94P!\xd4\xc2\x12PU13`\x0e@p\b\xce\xc1\r\x03\xdc\xe0\x10\xe0\x10\xdcQ\xc3\x10\xf7\xf4#\x97b&+\x13$n\xda\xc1\u017c\x13g$\x9d\xb3\v\x89lA\x93\xc2\xfd\x1cv\xb6nj\xb6s\xfdP\xdc\xe1\xf6\x96\x99\x15q\xb3\x19\xb4w\xba\x12\x85\xd7\xfc*\x97\x02\x82epe\x16\x17-\x1fx\xe1\x14\x88\x89\xdd\xe9I\x19\x98$\x81\xc9\x12\b\x03\xb4\xc9$\xa6.\xf5c|\xf0\x1c\u2f671\xda{\x06\xa3\xe7N#~\xfe,\x92\x89\xf8\fi\xa7\xbd\x11\x13J\u034d\x81x\x89\xae\xdc\xc7\xc2\xf2$onnBCC\xe3\xe8\x92%\x8b\u007f\xfc\xd0C\x0f=\xa5\xaa^\x89\xe1\xe0`?jk\xeb\xfc\xc5\u40f9\x1f\xbf\xefx\xeb\xad\xffEK\xcb\xcd\xde\xccS\x88\xf0\v/<\xff\u0653'On\xeb\xec\xec\xbc\xfe\xddw\xdb0\x1a\x8fg}[\xbc\xa0nfw \x04\x82\xeb\xa0LFaU-\u0095\xb5P\nK\x10(*C\xa8<\x8aP$\x8a`Y\x15\xe4\x82b(\x05\xc5\b\x14\x96B-\x8e@-,\x01\x91\x95,gM\x00\x10av\xc8\b\xc1\u0351\xee\x9c[|\xbe\x93u\xe7v^\x12\xe25\xa1r+b\xf2\xa9i\xa8\x95\x1d\xdb*\x0f\x9bj!y\x8a\x94\xf9T3\xe6>F\xb2lR.yd\x83e\xd6\xf8\xca*\x80:\x1b\x94p\x9aw(1\x1b\xf8)\x01\xb3(-JMb\x852\n\x89\x02Z:\x83\x89K\x03&h\xf7\x9e\xc1X\xdfY\x8c\xf5wc|\xa0\v\x13\x17\a\xbd\xe3\xe0(3yn\xeb\x14E\x89W\xc6#\xf2\fN\x15B\x80R\x02YV \xcb\x12\x1a\x1b\x1b\xd1\xdc\xdc<\x19\x8dV\xb5\xdeu\u05dd?Y\xb5jM\x9b\xfb\xf1\xdf\xff\xfe\xe3\xb8\uffbf\xf7\x17\x90\x0f\xe6~|\x9c\xe2\x95W\xfe\v[\xb6\u070e#G\x0ea\u03de\xbd\xec\x81\a\x1e\xb4\x95/U/\xbf\xfc\u04a7\xdb\xdbO\xdc\xdd\xd6vt\xfd\xa9S\xa7\x90H$\x90N\xa7=L\xc5L\n\xc603hj\x82<\xa1\x14L\x0e@\t\x17A\n\x85!\a\v\xa0\x84\x8b\xa0\x16\x95!\x14\xa9B\xb8j.\xc2\x15s\x10(.\x85\x1a.B\xb0\xb8\x14\xc1\x92\n\xa8\x85%\x90\x14\x15\xce \x04\x0eC7\xcci\xedB\x98\x13L\x05\xcf\x0e\f\xa6\xd4\xd2e\u00dc\x96\x03 ;\xcb\xd46\xb0\u02b5\x9b\xe5B@\xe3f\xe1\xd5\x00\xcd\u038c\x13v\xeaL\b\xe0\x18E\xb9\xc6\u06f9\xac\xa9,>\u01b1\x036\v\xae\xb6\xbe\x9c\x11\xc7!\xd11,s\rL&f\x87'\x17\x02\f\xe6&\x96\x99\x9e\u0115\xe1~\x8c\xf5w\"~\xfe,F{;\x90\x18\xee\xc3\xc4\xf0\x00&b\x17\xc0u\x87\xf3\xa6L\u029aY\x99\xb2A\xe21\xf52\x9d\x10E\xdeF,\xc30\x04!\x84\x84B!\x14\x15\x15\xa2\xb1\xb1\x11\x8b\x17/>\xdd\xd4\xd4\xf8\x8b\xa5K\x97\xbd|\xd3M-\xed\x00\xb0~\xfd\x8dt\u06f6m\xb8\xf3\xce\xcf\xf9\xea\x14\x1f\xcc\xfd\xf8C\x897\xde\xf8\x05}\xe3\x8d_\xb2'\x9ex\xd2V\xbe\x84\u007f\xf0\x83\x176\x1f=\xfa\u07b7\x8e\x1e=\xba``\xa0\x1f\xb1X\f\x9a\xa6\xe7-\t\xba\xb3C\x13\xe0I6\xd3\x16\x82Cp\xd7\fw\xca \xa9A\u0221\x02H\x81\x10\xa4@\x10J\xa8\x00\x81\xc2R\x84\"QD\x1a\xe6#\u04bc\b\xa5\x9f\xb8\x16\xe1\xb2JH\x8a\nI\x92 I\xd41\xc4\x12\x00\xb8\x01\xce-B\xc5eW\xeb1\x8d\xb2\xa8\ffm2 \x04\x19\x01d,x\xd2\f\xb3\u02d2[\x1bD\x96\n\xe2\xe6\tA\b\x93'2\xbf\xaf\xc3\xd0\xd20\xd2Ip]\x03\xd7M\x1baSJHA\xc0A\xb9n\x82\xb3\xaeA\x18:\fC\x03\x84\x80\x1a.\x82\x1c\f\x9b\x12A-\x8d\xf4D\x02S\xf1\u02d8\x8c\rc\xf2R?\x12\xc3\x03\x98\x8a_F*1\x8aTb\x14\xc9\u0118\xc7\a\x85P\x06\xcaXv\x93\xb3\v\x964\x0f\x80C\x88\x19\x12Ka\x06!\x04(**FYY\x19\xae\xb9\xa6\x19MM\x8d?^\xb0`\xc1\xd3\u06f6}\xb9\x8b\x102l?]k\xeb\xd3l\xeb\xd6m\xba\xbf2|0\xf7\xe3\x0f4\xac\x81\xd1n\xe5\vmm\xdd\xf1\xb9\u00c7\x0f\xdf\u007f\xf2\xe4\xff-\x1d\x19\x19\xc1\xf0\xf002\x99\x8cA\b\x04\xa5L2\v\x9d9\x1c\xac\xc5E\x10\xf7\x84g\xe7\xa2\x10\xdc\u021b5RJ!\a\xc3P\vK\x10,-G\u025cFT^\xb3\b%\xd1Z\x14\x94U \\Z\t\xb5\xa8\x14r(\fI\t\x80I2d\x89\x81P\xe6iN2\v\x8f\xe6f\xc2u\x03\x84\xeb\xa0\xe0\xa000\x9d\xd611\x95\xc4\u0115+\x98\x1a\x1f\xc5\xd4D\x02\xa9\xa9\t\xe8\xa9$2\xa9\x142\x13cHO\x8cCKN\xc2H\xa7`d\xd204\xf3\xab\x9eJ\x82k\xa6m\xb00\f\ap\xadZ\x02\xb1\xc0\x94s\xdd,\x06\x1b\xe6\xfb\xb4\x1b\xb3\f-\x03\xaee\xa0k\x19\xf3\u07fav\xf5\x05\x9d\x95\x8bRPP\xc7\xc7\x1c\x8e\u01ce;\x03'\xc43(\x9b\xeb\xban\b\xc1%UUI$\x12AYY\x19\xae\xbdv~b\xf9\xf2\xa5\xaf/_\xbe\xec_n\xbe\xf93\x1e>\xfc\xb5\xd7^!\xd5\xd5Q\xb1j\xd5\x1a\u007f1\xf8`\xee\xc7\x1fC\xec\xdc\xf9sr\xeb\xad\u007f)r8\u04ed\x1d\x1d\x1d[\xbb\xba\xba\xe7\x0f\r\r\x05c\xb1\x18\xe2\xf11!\x047\b\xa1T\bn+(H.\x1d\xe3\xf6Gw\xeeT\xe2\x92\xf59\xed\xa3B\x18.\x87?\x02I\r\x80\xc92\x98\xacB\t\x86\xa0\x14\x14\xa1\xb8\xbc\x1a\x85\x955\b\x16\x95@\r\x86!)*\x18\xa5Y!$#\x020t\x18\x99\f2\xa9i\xd3\xe27\x93\x82\xd0\u04d8\xba2\x8exl\x04\x93\x93W\x90\x9e\x9e\x86\xaee\xc0\r\xdd\x04^+\x03\xe7\u0724v\x84\xf8\xe8Tw6\x10\x13W\xb3\x8eS\xb4\x9c\xc9w\xbb5\xdd\xc4\xf1+\x067\x83J\x12#\x95\x95\x95(++CAA\xc1x]]]\xfb\xfa\xf5\xeb\xf7\xdes\xcf\u059f\x12B:\xdc\xd7{\xf0\xc1\xaf\x93{\xef\xbdW\xd4\xd4\xd4\xfa7\xbf\x0f\xe6~\xfc\xb1\xc5\xd3O?\x85S\xa7N\x91'\x9ex\u049d\xa9\xb3C\x87\x0e\u073as\xe7\xceM\xdd\xdd=\xab\x93\xc9\xe4\x82X,\x86\x81\x81ALOOA\xd7uLO'!\xc4\a2\xac\xbd\u028dLr\xe7\xd2\xffN\x16E\xde\xd1l\x1f\xe5\x02\xf5H\x02]_\xad\xb6y\u4302\xcb97y\u078d\xfd\x18\xce\x058\xe7\x90$\t\xaa\xaa\xa2\xa8\xa8\b\xd1h\x14\x91H\xd9XII\xc9\u19a6\xa6C\x1b7\xb6\x1c\xfd\u0527n\xdaE\b\xf1\xf0\xdfo\xbe\xf9+|\xfa\xd3\u007f\xe1\xdf\xec>\x98\xfb\xf1\xa7\x10\xb3\xa9\x18\x84\xe0\u05fe\xf8\xe2\x8b\x1b\xdb\xdb\xdb\xe7\uaeb6\xe2\u0295\x89\xf2s\xe7\xceM\x85\xc3\xe1\u56a6\x05{{\xcf#\x91H \x93I\xc308\f\xc3@&\x93\xf9\xe3\\x\xb9Y2!\x1e\xd31\xea\xea\xc1w\x17D\xed,;\xdf\x06f\u007f\x9fR{\xec\x9a\xd5\xf9i\xa9V\x18cP\xd5\x00**\xcaQ[[\v\u01a4\xe9`0\xd0Y]]=\xac\xaa\u02ae\xdbn\xdbrv\u035a\xb5\xbf\xca\u05d5\xb9k\xd7/q\xcb-\x9f\xf1on\x1f\xcc\xfd\xf8S\x8c\x8b\x17\x87\x10\x8d\xd6`p\xb0\x8f\xd4\xd6~B\xe4\x1c\xfb\xab\x00\x94\xec\u077b7\xd9\xdb\xdb3\xaf\xaf\xaf\xaf\xee\u0295\xc9\x06Bp\xfd\xc8H\x8c\xc6b#)JiEQQ\u046a\u02d7/c``\x10\x13\x13\x13\u0434\f4M\x83\xae\x1b0\f\x03\x9a\xa6\x9b<\xb7\xdbf\xe0Cj\x11\xbf\x9a\x89\x96\x99\a\x93\x19\xf3D\xb3\t\xf2l\xb9\xbdG\xcbm6\xfa0f\xd3%\xc4j\xa42m\x87\u0740\x9d}FB!\u02e6R\xc56\xb7b\x8cZ\xe0mf\u07a5\xa5%\xa8\xad\xadE0\x18\ucaac\xac\xec\f\x04\x82GC\xa1`\xe7\xbcy\u05cc\xb6\xb4l8\x1f\x8d\xd6^$\x84$\xec\xabvv\x9e!\xf3\xe6\xcd\x17\x00\xb0c\xc7S\u063cy3\xe6\u0319\xeb\xdf\xd0>\x98\xfb\xf1\xa7\x1c==\x9dhj\x9a\a!\x04v\xee\xfc9\x89\xc5b\xec\xc9'\x9f\x16\u01cf\x1f7\xf2\x03\xa6\xa8\xb0\xeeK\xbd\xa3\xe3T\xb8\xaf\xaf\u007fN[[[@Q\xe4\u0159Lf\xe5\u0673g\xb5\xe9\xe9diII\xf1\xba\x8b\x17/\x97\x0e\f\f`||\x1c\xba\xae!\x93\u0450L\x9a\xfe/6\xd8Sk B~\x80\xcf\xef\xed2\xc3\xfb\u071e=*\x04r\x8b\xb7\xb2,\x83Rj6\xf5\xe4\\U\x96e(\xb2\x9c\x05W\x0f\xc7M\x88w,\x9b\x95=\xeb\xba\x0e\u039d\xe9M\xf6\xefp\xceA)\x81$IP\x14\x15\xb2,\x811\x06J\x19dYBqq\xb1\x88F\xa3\xa4\xa4\xa48\x1d\x8f\xc7\x0f\x8c\x8d\x8d\x9fkhh\b44\u05031\xe98\xe7\xbcm\xf5\xea\x1b\x86W\xaf^;F\b\x19\xcd}\xcf\xf7\xddw\xaf\xb4b\xc5r\xb1l\xd92\xbed\xc9R\xf1\xfa\xeb\xff\x83M\x9b6\xfb7\xb0\x0f\xe6~\xf8q\xb5\fW\xe0\u0529\xf7iOO/y\xfb\xed}(,\f\xd3D\xe2\n}\xfc\xf1\xffH\xff\x86\xdf\v\x02\x10\xa9\u0514|\xe4\xc8\xe1\u04b6\xb6cJEE\xf9\xc6\u04e7;\x16\x8d\x8c\u0116\x8d\x8c\x8c.\x1e\x1a\x1a\n'\x12\xe3\xd40\xcc\x06\x97t:\x8d\xa9\xa9)LM%a\xaa\r\t4M\x87\xa6i\xf9\x93k\x80\u0232\fIbfc\x90p\x18sU\r\xa0\xa8\xa8\x10\x81@\x00\x92$\x81s\x81d2\t\x00P\x14\x05\xc1`\x00\x92$\x9bEXY\x02!$I\b\x9d,..DAA\x01\x14EE \xa0BUU(\x8a\n\xc6X\xf6\x89UU\xa5\xc1`\x80^\xb80\xb4\xef\xfc\xf9\xf3\xa7\xc2\xe10K\xa7\u04d9\xea\xea\xea\xe6`0T\xdc\xdf\xdfwZQ\x14c\xee\u0739\xa4\xbe\xbe\x1e\xc5\xc5E\b\x85B2\x80\xc1c\xc7\xda\xf7LNN&\u05acY%777\x19\r\r\r\xf1H\xa4j\n\xf6\xdc\rB\x92\xb9\xeb~\xfb\xf6\x87\x95T*i\xd4\xd7\u05cbU\xabV\x8a\xa5KW\xf8\xdap\x1f\xcc\xfd\xf0\xe3\u00cb\xae\xae3\u063d\xfb\xd7\u0636\xedK\xbf\xf1\xb1\xd3\xd3\x13U\x87\x0e\x1d^\xb7\u007f\xff\x81\x8at:\xb5H\b\\\xdf\xdd\u074dX,\x16\xe6\\\xd4%\x12\t#\x95JrEQKB\xa1\x90\xb5A8\x14\rc\fB\b$\x12\t=\x9dNO\xaa\xaaJM\xe0UH(\x14\x82,+\xa3\x15\x15\x91X}}=\"\x91\x88$\x84\xb8\xd4\xd9\xd9\xf9v:\x9d\x89777K\xcd\xcdM(..\x86$1Z\\\\\"\x15\x16\x16\xbe;22z|\xf5\xea\x1b\fYV\xd9\aY\x8b\x84\x90\xe9<\x9b\x98G\xfe\xf9\xdb\xc4\x0f\u007f\xf8\"\u05ad[\x87\xba\xba\x06\xff\xa6\xf2\xc1\xdc\x0f?>>\xd1\xde\xfe\x1e\x1e}\xf4\x9f\xf1\uaaef}\x90\x93@\xf9\x89\x13\xc7n>r\xa4-}\xe6\xcc\x19\xbd\xb2\xb2\xe2\x9a\xf2\xf2\xc8\n\xceE\x88s\xce3\x99\x8c\xb0\xb2c\x99\x10\x92\xec\xed=\xff\xf6\x85\v\x17\x06\xeb\xea\xeaX]]\x1d\xa9\xae\x8e\xb2\x86\x86\x06\xb2h\u0452vB\xc8\u064f\xe3\xe71<<\x88\xeaj_&\xe8\x87\x1f~\xfc\x81\xc7\xd0\xd0\xc0\xc7\xf2u\xdd\u007f\xff\xdfbp\xb0\xdf\xff\x03\xf9\xf1\xa1\xc7\xff\x03 \x99\rH\xa6\x0e6\x92\x00\x00\x00\x00IEND\xaeB`\x82")
+
+// dashboardDockerfile is the Dockerfile required to build an dashboard container
+// to aggregate various private network services under one easily accessible page.
+var dashboardDockerfile = `
+FROM mhart/alpine-node:latest
+
+RUN \
+ npm install connect serve-static && \
+ \
+ echo 'var connect = require("connect");' > server.js && \
+ echo 'var serveStatic = require("serve-static");' >> server.js && \
+ echo 'connect().use(serveStatic("/dashboard")).listen(80, function(){' >> server.js && \
+ echo ' console.log("Server running on 80...");' >> server.js && \
+ echo '});' >> server.js
+
+ADD {{.Network}}.json /dashboard/{{.Network}}.json
+ADD index.html /dashboard/index.html
+ADD puppeth.png /dashboard/puppeth.png
+
+EXPOSE 80
+
+CMD ["node", "/server.js"]
+`
+
+// dashboardComposefile is the docker-compose.yml file required to deploy and
+// maintain an service aggregating dashboard.
+var dashboardComposefile = `
+version: '2'
+services:
+ dashboard:
+ build: .
+ image: {{.Network}}/dashboard{{if not .VHost}}
+ ports:
+ - "{{.Port}}:80"{{else}}
+ environment:
+ - VIRTUAL_HOST={{.VHost}}{{end}}
+ restart: always
+`
+
+// deployDashboard deploys a new dashboard container to a remote machine via SSH,
+// docker and docker-compose. If an instance with the specified network name
+// already exists there, it will be overwritten!
+func deployDashboard(client *sshClient, network string, port int, vhost string, services map[string]string, conf *config, ethstats bool) ([]byte, error) {
+ // Generate the content to upload to the server
+ workdir := fmt.Sprintf("%d", rand.Int63())
+ files := make(map[string][]byte)
+
+ dockerfile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(dashboardDockerfile)).Execute(dockerfile, map[string]interface{}{
+ "Network": network,
+ })
+ files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
+
+ composefile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(dashboardComposefile)).Execute(composefile, map[string]interface{}{
+ "Network": network,
+ "Port": port,
+ "VHost": vhost,
+ })
+ files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
+
+ statsLogin := fmt.Sprintf("yournode:%s", conf.ethstats)
+ if !ethstats {
+ statsLogin = ""
+ }
+ indexfile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
+ "Network": network,
+ "NetworkID": conf.genesis.Config.ChainId,
+ "NetworkTitle": strings.Title(network),
+ "EthstatsPage": services["ethstats"],
+ "ExplorerPage": services["explorer"],
+ "WalletPage": services["wallet"],
+ "FaucetPage": services["faucet"],
+ "GethGenesis": network + ".json",
+ "BootnodesFull": conf.bootFull,
+ "BootnodesLight": conf.bootLight,
+ "BootnodesFullFlat": strings.Join(conf.bootFull, ","),
+ "BootnodesLightFlat": strings.Join(conf.bootLight, ","),
+ "Ethstats": statsLogin,
+ })
+ files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
+
+ genesis, _ := conf.genesis.MarshalJSON()
+ files[filepath.Join(workdir, network+".json")] = genesis
+
+ files[filepath.Join(workdir, "puppeth.png")] = dashboardMascot
+
+ // Upload the deployment files to the remote server (and clean up afterwards)
+ if out, err := client.Upload(files); err != nil {
+ return out, err
+ }
+ defer client.Run("rm -rf " + workdir)
+
+ // Build and deploy the dashboard service
+ return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network))
+}
+
+// dashboardInfos is returned from an dashboard status check to allow reporting
+// various configuration parameters.
+type dashboardInfos struct {
+ host string
+ port int
+}
+
+// String implements the stringer interface.
+func (info *dashboardInfos) String() string {
+ return fmt.Sprintf("host=%s, port=%d", info.host, info.port)
+}
+
+// checkDashboard does a health-check against a dashboard container to verify if
+// it's running, and if yes, gathering a collection of useful infos about it.
+func checkDashboard(client *sshClient, network string) (*dashboardInfos, error) {
+ // Inspect a possible ethstats container on the host
+ infos, err := inspectContainer(client, fmt.Sprintf("%s_dashboard_1", network))
+ if err != nil {
+ return nil, err
+ }
+ if !infos.running {
+ return nil, ErrServiceOffline
+ }
+ // Resolve the port from the host, or the reverse proxy
+ port := infos.portmap["80/tcp"]
+ if port == 0 {
+ if proxy, _ := checkNginx(client, network); proxy != nil {
+ port = proxy.port
+ }
+ }
+ if port == 0 {
+ return nil, ErrNotExposed
+ }
+ // Resolve the host from the reverse-proxy and configure the connection string
+ host := infos.envvars["VIRTUAL_HOST"]
+ if host == "" {
+ host = client.server
+ }
+ // Run a sanity check to see if the port is reachable
+ if err = checkPort(host, port); err != nil {
+ log.Warn("Dashboard service seems unreachable", "server", host, "port", port, "err", err)
+ }
+ // Container available, assemble and return the useful infos
+ return &dashboardInfos{
+ host: host,
+ port: port,
+ }, nil
+}
diff --git a/cmd/puppeth/module_ethstats.go b/cmd/puppeth/module_ethstats.go
new file mode 100644
index 000000000..571df1454
--- /dev/null
+++ b/cmd/puppeth/module_ethstats.go
@@ -0,0 +1,159 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "math/rand"
+ "path/filepath"
+ "strings"
+ "text/template"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// ethstatsDockerfile is the Dockerfile required to build an ethstats backend
+// and associated monitoring site.
+var ethstatsDockerfile = `
+FROM mhart/alpine-node:latest
+
+RUN \
+ apk add --update git && \
+ git clone --depth=1 https://github.com/karalabe/eth-netstats && \
+ apk del git && rm -rf /var/cache/apk/* && \
+ \
+ cd /eth-netstats && npm install && npm install -g grunt-cli && grunt
+
+WORKDIR /eth-netstats
+EXPOSE 3000
+
+RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: []};' > lib/utils/config.js
+
+CMD ["npm", "start"]
+`
+
+// ethstatsComposefile is the docker-compose.yml file required to deploy and
+// maintain an ethstats monitoring site.
+var ethstatsComposefile = `
+version: '2'
+services:
+ ethstats:
+ build: .
+ image: {{.Network}}/ethstats{{if not .VHost}}
+ ports:
+ - "{{.Port}}:3000"{{end}}
+ environment:
+ - WS_SECRET={{.Secret}}{{if .VHost}}
+ - VIRTUAL_HOST={{.VHost}}{{end}}
+ restart: always
+`
+
+// deployEthstats deploys a new ethstats container to a remote machine via SSH,
+// docker and docker-compose. If an instance with the specified network name
+// already exists there, it will be overwritten!
+func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string) ([]byte, error) {
+ // Generate the content to upload to the server
+ workdir := fmt.Sprintf("%d", rand.Int63())
+ files := make(map[string][]byte)
+
+ for i, address := range trusted {
+ trusted[i] = fmt.Sprintf("\"%s\"", address)
+ }
+
+ dockerfile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(ethstatsDockerfile)).Execute(dockerfile, map[string]interface{}{
+ "Trusted": strings.Join(trusted, ", "),
+ })
+ files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
+
+ composefile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(ethstatsComposefile)).Execute(composefile, map[string]interface{}{
+ "Network": network,
+ "Port": port,
+ "Secret": secret,
+ "VHost": vhost,
+ })
+ files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
+
+ // Upload the deployment files to the remote server (and clean up afterwards)
+ if out, err := client.Upload(files); err != nil {
+ return out, err
+ }
+ defer client.Run("rm -rf " + workdir)
+
+ // Build and deploy the ethstats service
+ return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network))
+}
+
+// ethstatsInfos is returned from an ethstats status check to allow reporting
+// various configuration parameters.
+type ethstatsInfos struct {
+ host string
+ port int
+ secret string
+ config string
+}
+
+// String implements the stringer interface.
+func (info *ethstatsInfos) String() string {
+ return fmt.Sprintf("host=%s, port=%d, secret=%s", info.host, info.port, info.secret)
+}
+
+// checkEthstats does a health-check against an ethstats server to verify whether
+// it's running, and if yes, gathering a collection of useful infos about it.
+func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) {
+ // Inspect a possible ethstats container on the host
+ infos, err := inspectContainer(client, fmt.Sprintf("%s_ethstats_1", network))
+ if err != nil {
+ return nil, err
+ }
+ if !infos.running {
+ return nil, ErrServiceOffline
+ }
+ // Resolve the port from the host, or the reverse proxy
+ port := infos.portmap["3000/tcp"]
+ if port == 0 {
+ if proxy, _ := checkNginx(client, network); proxy != nil {
+ port = proxy.port
+ }
+ }
+ if port == 0 {
+ return nil, ErrNotExposed
+ }
+ // Resolve the host from the reverse-proxy and configure the connection string
+ host := infos.envvars["VIRTUAL_HOST"]
+ if host == "" {
+ host = client.server
+ }
+ secret := infos.envvars["WS_SECRET"]
+ config := fmt.Sprintf("%s@%s", secret, host)
+ if port != 80 && port != 443 {
+ config += fmt.Sprintf(":%d", port)
+ }
+ // Run a sanity check to see if the port is reachable
+ if err = checkPort(host, port); err != nil {
+ log.Warn("Ethstats service seems unreachable", "server", host, "port", port, "err", err)
+ }
+ // Container available, assemble and return the useful infos
+ return &ethstatsInfos{
+ host: host,
+ port: port,
+ secret: secret,
+ config: config,
+ }, nil
+}
diff --git a/cmd/puppeth/module_faucet.go b/cmd/puppeth/module_faucet.go
new file mode 100644
index 000000000..44016c53e
--- /dev/null
+++ b/cmd/puppeth/module_faucet.go
@@ -0,0 +1,210 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "math/rand"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// faucetDockerfile is the Dockerfile required to build an faucet container to
+// grant crypto tokens based on GitHub authentications.
+var faucetDockerfile = `
+FROM alpine:latest
+
+RUN mkdir /go
+ENV GOPATH /go
+
+RUN \
+ apk add --update git go make gcc musl-dev ca-certificates linux-headers && \
+ mkdir -p $GOPATH/src/github.com/ethereum && \
+ (cd $GOPATH/src/github.com/ethereum && git clone --depth=1 https://github.com/ethereum/go-ethereum) && \
+ go build -v github.com/ethereum/go-ethereum/cmd/faucet && \
+ apk del git go make gcc musl-dev linux-headers && \
+ rm -rf $GOPATH && rm -rf /var/cache/apk/*
+
+ADD genesis.json /genesis.json
+ADD account.json /account.json
+ADD account.pass /account.pass
+
+EXPOSE 8080
+
+CMD [ \
+ "/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", \
+ "--ethport", "{{.EthPort}}", "--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", \
+ "--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass" \
+]`
+
+// faucetComposefile is the docker-compose.yml file required to deploy and maintain
+// a crypto faucet.
+var faucetComposefile = `
+version: '2'
+services:
+ faucet:
+ build: .
+ image: {{.Network}}/faucet
+ ports:
+ - "{{.EthPort}}:{{.EthPort}}"{{if not .VHost}}
+ - "{{.ApiPort}}:8080"{{end}}
+ volumes:
+ - {{.Datadir}}:/root/.faucet
+ environment:
+ - ETH_PORT={{.EthPort}}
+ - ETH_NAME={{.EthName}}
+ - FAUCET_AMOUNT={{.FaucetAmount}}
+ - FAUCET_MINUTES={{.FaucetMinutes}}
+ - GITHUB_USER={{.GitHubUser}}
+ - GITHUB_TOKEN={{.GitHubToken}}{{if .VHost}}
+ - VIRTUAL_HOST={{.VHost}}
+ - VIRTUAL_PORT=8080{{end}}
+ restart: always
+`
+
+// deployFaucet deploys a new faucet container to a remote machine via SSH,
+// docker and docker-compose. If an instance with the specified network name
+// already exists there, it will be overwritten!
+func deployFaucet(client *sshClient, network string, bootnodes []string, config *faucetInfos) ([]byte, error) {
+ // Generate the content to upload to the server
+ workdir := fmt.Sprintf("%d", rand.Int63())
+ files := make(map[string][]byte)
+
+ dockerfile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(faucetDockerfile)).Execute(dockerfile, map[string]interface{}{
+ "NetworkID": config.node.network,
+ "Bootnodes": strings.Join(bootnodes, ","),
+ "Ethstats": config.node.ethstats,
+ "EthPort": config.node.portFull,
+ "GitHubUser": config.githubUser,
+ "GitHubToken": config.githubToken,
+ "FaucetName": strings.Title(network),
+ "FaucetAmount": config.amount,
+ "FaucetMinutes": config.minutes,
+ })
+ files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
+
+ composefile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(faucetComposefile)).Execute(composefile, map[string]interface{}{
+ "Network": network,
+ "Datadir": config.node.datadir,
+ "VHost": config.host,
+ "ApiPort": config.port,
+ "EthPort": config.node.portFull,
+ "EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")],
+ "GitHubUser": config.githubUser,
+ "GitHubToken": config.githubToken,
+ "FaucetAmount": config.amount,
+ "FaucetMinutes": config.minutes,
+ })
+ files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
+
+ files[filepath.Join(workdir, "genesis.json")] = []byte(config.node.genesis)
+ files[filepath.Join(workdir, "account.json")] = []byte(config.node.keyJSON)
+ files[filepath.Join(workdir, "account.pass")] = []byte(config.node.keyPass)
+
+ // Upload the deployment files to the remote server (and clean up afterwards)
+ if out, err := client.Upload(files); err != nil {
+ return out, err
+ }
+ defer client.Run("rm -rf " + workdir)
+
+ // Build and deploy the faucet service
+ return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network))
+}
+
+// faucetInfos is returned from an faucet status check to allow reporting various
+// configuration parameters.
+type faucetInfos struct {
+ node *nodeInfos
+ host string
+ port int
+ amount int
+ minutes int
+ githubUser string
+ githubToken string
+}
+
+// String implements the stringer interface.
+func (info *faucetInfos) String() string {
+ return fmt.Sprintf("host=%s, api=%d, eth=%d, amount=%d, minutes=%d, github=%s, ethstats=%s", info.host, info.port, info.node.portFull, info.amount, info.minutes, info.githubUser, info.node.ethstats)
+}
+
+// checkFaucet does a health-check against an faucet server to verify whether
+// it's running, and if yes, gathering a collection of useful infos about it.
+func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
+ // Inspect a possible faucet container on the host
+ infos, err := inspectContainer(client, fmt.Sprintf("%s_faucet_1", network))
+ if err != nil {
+ return nil, err
+ }
+ if !infos.running {
+ return nil, ErrServiceOffline
+ }
+ // Resolve the port from the host, or the reverse proxy
+ port := infos.portmap["8080/tcp"]
+ if port == 0 {
+ if proxy, _ := checkNginx(client, network); proxy != nil {
+ port = proxy.port
+ }
+ }
+ if port == 0 {
+ return nil, ErrNotExposed
+ }
+ // Resolve the host from the reverse-proxy and the config values
+ host := infos.envvars["VIRTUAL_HOST"]
+ if host == "" {
+ host = client.server
+ }
+ amount, _ := strconv.Atoi(infos.envvars["FAUCET_AMOUNT"])
+ minutes, _ := strconv.Atoi(infos.envvars["FAUCET_MINUTES"])
+
+ // Retrieve the funding account informations
+ var out []byte
+ keyJSON, keyPass := "", ""
+ if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.json", network)); err == nil {
+ keyJSON = string(bytes.TrimSpace(out))
+ }
+ if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.pass", network)); err == nil {
+ keyPass = string(bytes.TrimSpace(out))
+ }
+ // Run a sanity check to see if the port is reachable
+ if err = checkPort(host, port); err != nil {
+ log.Warn("Faucet service seems unreachable", "server", host, "port", port, "err", err)
+ }
+ // Container available, assemble and return the useful infos
+ return &faucetInfos{
+ node: &nodeInfos{
+ datadir: infos.volumes["/root/.faucet"],
+ portFull: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"],
+ ethstats: infos.envvars["ETH_NAME"],
+ keyJSON: keyJSON,
+ keyPass: keyPass,
+ },
+ host: host,
+ port: port,
+ amount: amount,
+ minutes: minutes,
+ githubUser: infos.envvars["GITHUB_USER"],
+ githubToken: infos.envvars["GITHUB_TOKEN"],
+ }, nil
+}
diff --git a/cmd/puppeth/module_nginx.go b/cmd/puppeth/module_nginx.go
new file mode 100644
index 000000000..0eac5ace5
--- /dev/null
+++ b/cmd/puppeth/module_nginx.go
@@ -0,0 +1,106 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "math/rand"
+ "path/filepath"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// nginxDockerfile is theis the Dockerfile required to build an nginx reverse-
+// proxy.
+var nginxDockerfile = `FROM jwilder/nginx-proxy`
+
+// nginxComposefile is the docker-compose.yml file required to deploy and maintain
+// an nginx reverse-proxy. The proxy is responsible for exposing one or more HTTP
+// services running on a single host.
+var nginxComposefile = `
+version: '2'
+services:
+ nginx:
+ build: .
+ image: {{.Network}}/nginx
+ ports:
+ - "{{.Port}}:80"
+ volumes:
+ - /var/run/docker.sock:/tmp/docker.sock:ro
+ restart: always
+`
+
+// deployNginx deploys a new nginx reverse-proxy container to expose one or more
+// HTTP services running on a single host. If an instance with the specified
+// network name already exists there, it will be overwritten!
+func deployNginx(client *sshClient, network string, port int) ([]byte, error) {
+ log.Info("Deploying nginx reverse-proxy", "server", client.server, "port", port)
+
+ // Generate the content to upload to the server
+ workdir := fmt.Sprintf("%d", rand.Int63())
+ files := make(map[string][]byte)
+
+ dockerfile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(nginxDockerfile)).Execute(dockerfile, nil)
+ files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
+
+ composefile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(nginxComposefile)).Execute(composefile, map[string]interface{}{
+ "Network": network,
+ "Port": port,
+ })
+ files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
+
+ // Upload the deployment files to the remote server (and clean up afterwards)
+ if out, err := client.Upload(files); err != nil {
+ return out, err
+ }
+ defer client.Run("rm -rf " + workdir)
+
+ // Build and deploy the ethstats service
+ return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network))
+}
+
+// nginxInfos is returned from an nginx reverse-proxy status check to allow
+// reporting various configuration parameters.
+type nginxInfos struct {
+ port int
+}
+
+// String implements the stringer interface.
+func (info *nginxInfos) String() string {
+ return fmt.Sprintf("port=%d", info.port)
+}
+
+// checkNginx does a health-check against an nginx reverse-proxy to verify whether
+// it's running, and if yes, gathering a collection of useful infos about it.
+func checkNginx(client *sshClient, network string) (*nginxInfos, error) {
+ // Inspect a possible nginx container on the host
+ infos, err := inspectContainer(client, fmt.Sprintf("%s_nginx_1", network))
+ if err != nil {
+ return nil, err
+ }
+ if !infos.running {
+ return nil, ErrServiceOffline
+ }
+ // Container available, assemble and return the useful infos
+ return &nginxInfos{
+ port: infos.portmap["80/tcp"],
+ }, nil
+}
diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go
new file mode 100644
index 000000000..78681934d
--- /dev/null
+++ b/cmd/puppeth/module_node.go
@@ -0,0 +1,222 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "math/rand"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "text/template"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// nodeDockerfile is the Dockerfile required to run an Ethereum node.
+var nodeDockerfile = `
+FROM ethereum/client-go:alpine-develop
+
+ADD genesis.json /genesis.json
+{{if .Unlock}}
+ ADD signer.json /signer.json
+ ADD signer.pass /signer.pass
+{{end}}
+RUN \
+ echo '/geth init /genesis.json' > geth.sh && \{{if .Unlock}}
+ echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}}
+ echo $'/geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}}' >> geth.sh
+
+ENTRYPOINT ["/bin/sh", "geth.sh"]
+`
+
+// nodeComposefile is the docker-compose.yml file required to deploy and maintain
+// an Ethereum node (bootnode or miner for now).
+var nodeComposefile = `
+version: '2'
+services:
+ {{.Type}}:
+ build: .
+ image: {{.Network}}/{{.Type}}
+ ports:
+ - "{{.FullPort}}:{{.FullPort}}"
+ - "{{.FullPort}}:{{.FullPort}}/udp"{{if .Light}}
+ - "{{.LightPort}}:{{.LightPort}}/udp"{{end}}
+ volumes:
+ - {{.Datadir}}:/root/.ethereum
+ environment:
+ - FULL_PORT={{.FullPort}}/tcp
+ - LIGHT_PORT={{.LightPort}}/udp
+ - TOTAL_PEERS={{.TotalPeers}}
+ - LIGHT_PEERS={{.LightPeers}}
+ - STATS_NAME={{.Ethstats}}
+ - MINER_NAME={{.Etherbase}}
+ restart: always
+`
+
+// deployNode deploys a new Ethereum node container to a remote machine via SSH,
+// docker and docker-compose. If an instance with the specified network name
+// already exists there, it will be overwritten!
+func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos) ([]byte, error) {
+ kind := "sealnode"
+ if config.keyJSON == "" && config.etherbase == "" {
+ kind = "bootnode"
+ bootnodes = make([]string, 0)
+ }
+ // Generate the content to upload to the server
+ workdir := fmt.Sprintf("%d", rand.Int63())
+ files := make(map[string][]byte)
+
+ lightFlag := ""
+ if config.peersLight > 0 {
+ lightFlag = fmt.Sprintf("--lightpeers=%d --lightserv=50", config.peersLight)
+ }
+ dockerfile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{
+ "NetworkID": config.network,
+ "Port": config.portFull,
+ "Peers": config.peersTotal,
+ "LightFlag": lightFlag,
+ "Bootnodes": strings.Join(bootnodes, ","),
+ "Ethstats": config.ethstats,
+ "Etherbase": config.etherbase,
+ "Unlock": config.keyJSON != "",
+ })
+ files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
+
+ composefile := new(bytes.Buffer)
+ template.Must(template.New("").Parse(nodeComposefile)).Execute(composefile, map[string]interface{}{
+ "Type": kind,
+ "Datadir": config.datadir,
+ "Network": network,
+ "FullPort": config.portFull,
+ "TotalPeers": config.peersTotal,
+ "Light": config.peersLight > 0,
+ "LightPort": config.portFull + 1,
+ "LightPeers": config.peersLight,
+ "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")],
+ "Etherbase": config.etherbase,
+ })
+ files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
+
+ //genesisfile, _ := json.MarshalIndent(config.genesis, "", " ")
+ files[filepath.Join(workdir, "genesis.json")] = []byte(config.genesis)
+
+ if config.keyJSON != "" {
+ files[filepath.Join(workdir, "signer.json")] = []byte(config.keyJSON)
+ files[filepath.Join(workdir, "signer.pass")] = []byte(config.keyPass)
+ }
+ // Upload the deployment files to the remote server (and clean up afterwards)
+ if out, err := client.Upload(files); err != nil {
+ return out, err
+ }
+ defer client.Run("rm -rf " + workdir)
+
+ // Build and deploy the bootnode service
+ return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network))
+}
+
+// nodeInfos is returned from a boot or seal node status check to allow reporting
+// various configuration parameters.
+type nodeInfos struct {
+ genesis []byte
+ network int64
+ datadir string
+ ethstats string
+ portFull int
+ portLight int
+ enodeFull string
+ enodeLight string
+ peersTotal int
+ peersLight int
+ etherbase string
+ keyJSON string
+ keyPass string
+}
+
+// String implements the stringer interface.
+func (info *nodeInfos) String() string {
+ discv5 := ""
+ if info.peersLight > 0 {
+ discv5 = fmt.Sprintf(", portv5=%d", info.portLight)
+ }
+ return fmt.Sprintf("port=%d%s, datadir=%s, peers=%d, lights=%d, ethstats=%s", info.portFull, discv5, info.datadir, info.peersTotal, info.peersLight, info.ethstats)
+}
+
+// checkNode does a health-check against an boot or seal node server to verify
+// whether it's running, and if yes, whether it's responsive.
+func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error) {
+ kind := "bootnode"
+ if !boot {
+ kind = "sealnode"
+ }
+ // Inspect a possible bootnode container on the host
+ infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, kind))
+ if err != nil {
+ return nil, err
+ }
+ if !infos.running {
+ return nil, ErrServiceOffline
+ }
+ // Resolve a few types from the environmental variables
+ totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"])
+ lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"])
+
+ // Container available, retrieve its node ID and its genesis json
+ var out []byte
+ if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 /geth --exec admin.nodeInfo.id attach", network, kind)); err != nil {
+ return nil, ErrServiceUnreachable
+ }
+ id := bytes.Trim(bytes.TrimSpace(out), "\"")
+
+ if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /genesis.json", network, kind)); err != nil {
+ return nil, ErrServiceUnreachable
+ }
+ genesis := bytes.TrimSpace(out)
+
+ keyJSON, keyPass := "", ""
+ if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.json", network, kind)); err == nil {
+ keyJSON = string(bytes.TrimSpace(out))
+ }
+ if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.pass", network, kind)); err == nil {
+ keyPass = string(bytes.TrimSpace(out))
+ }
+ // Run a sanity check to see if the devp2p is reachable
+ port := infos.portmap[infos.envvars["FULL_PORT"]]
+ if err = checkPort(client.server, port); err != nil {
+ log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err)
+ }
+ // Assemble and return the useful infos
+ stats := &nodeInfos{
+ genesis: genesis,
+ datadir: infos.volumes["/root/.ethereum"],
+ portFull: infos.portmap[infos.envvars["FULL_PORT"]],
+ portLight: infos.portmap[infos.envvars["LIGHT_PORT"]],
+ peersTotal: totalPeers,
+ peersLight: lightPeers,
+ ethstats: infos.envvars["STATS_NAME"],
+ etherbase: infos.envvars["MINER_NAME"],
+ keyJSON: keyJSON,
+ keyPass: keyPass,
+ }
+ stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull)
+ if stats.portLight != 0 {
+ stats.enodeLight = fmt.Sprintf("enode://%s@%s:%d?discport=%d", id, client.address, stats.portFull, stats.portLight)
+ }
+ return stats, nil
+}
diff --git a/cmd/puppeth/puppeth.go b/cmd/puppeth/puppeth.go
new file mode 100644
index 000000000..f783a7981
--- /dev/null
+++ b/cmd/puppeth/puppeth.go
@@ -0,0 +1,55 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+// puppeth is a command to assemble and maintain private networks.
+package main
+
+import (
+ "math/rand"
+ "os"
+ "time"
+
+ "github.com/ethereum/go-ethereum/log"
+ "gopkg.in/urfave/cli.v1"
+)
+
+// main is just a boring entry point to set up the CLI app.
+func main() {
+ app := cli.NewApp()
+ app.Name = "puppeth"
+ app.Usage = "assemble and maintain private Ethereum networks"
+ app.Flags = []cli.Flag{
+ cli.StringFlag{
+ Name: "network",
+ Usage: "name of the network to administer",
+ },
+ cli.IntFlag{
+ Name: "loglevel",
+ Value: 4,
+ Usage: "log level to emit to the screen",
+ },
+ }
+ app.Action = func(c *cli.Context) error {
+ // Set up the logger to print everything and the random generator
+ log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
+ rand.Seed(time.Now().UnixNano())
+
+ // Start the wizard and relinquish control
+ makeWizard(c.String("network")).run()
+ return nil
+ }
+ app.Run(os.Args)
+}
diff --git a/cmd/puppeth/ssh.go b/cmd/puppeth/ssh.go
new file mode 100644
index 000000000..34fbc566d
--- /dev/null
+++ b/cmd/puppeth/ssh.go
@@ -0,0 +1,195 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strings"
+ "syscall"
+
+ "github.com/ethereum/go-ethereum/log"
+ "golang.org/x/crypto/ssh"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+// sshClient is a small wrapper around Go's SSH client with a few utility methods
+// implemented on top.
+type sshClient struct {
+ server string // Server name or IP without port number
+ address string // IP address of the remote server
+ client *ssh.Client
+ logger log.Logger
+}
+
+// dial establishes an SSH connection to a remote node using the current user and
+// the user's configured private RSA key.
+func dial(server string) (*sshClient, error) {
+ // Figure out a label for the server and a logger
+ label := server
+ if strings.Contains(label, ":") {
+ label = label[:strings.Index(label, ":")]
+ }
+ logger := log.New("server", label)
+ logger.Debug("Attempting to establish SSH connection")
+
+ user, err := user.Current()
+ if err != nil {
+ return nil, err
+ }
+ // Configure the supported authentication methods (private key and password)
+ var auths []ssh.AuthMethod
+
+ path := filepath.Join(user.HomeDir, ".ssh", "id_rsa")
+ if buf, err := ioutil.ReadFile(path); err != nil {
+ log.Warn("No SSH key, falling back to passwords", "path", path, "err", err)
+ } else {
+ key, err := ssh.ParsePrivateKey(buf)
+ if err != nil {
+ log.Warn("Bad SSH key, falling back to passwords", "path", path, "err", err)
+ } else {
+ auths = append(auths, ssh.PublicKeys(key))
+ }
+ }
+ auths = append(auths, ssh.PasswordCallback(func() (string, error) {
+ fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", user.Username, server)
+ blob, err := terminal.ReadPassword(int(syscall.Stdin))
+
+ fmt.Println()
+ return string(blob), err
+ }))
+ // Resolve the IP address of the remote server
+ addr, err := net.LookupHost(label)
+ if err != nil {
+ return nil, err
+ }
+ if len(addr) == 0 {
+ return nil, errors.New("no IPs associated with domain")
+ }
+ // Try to dial in to the remote server
+ logger.Trace("Dialing remote SSH server", "user", user.Username, "key", path)
+ if !strings.Contains(server, ":") {
+ server += ":22"
+ }
+ client, err := ssh.Dial("tcp", server, &ssh.ClientConfig{User: user.Username, Auth: auths})
+ if err != nil {
+ return nil, err
+ }
+ // Connection established, return our utility wrapper
+ c := &sshClient{
+ server: label,
+ address: addr[0],
+ client: client,
+ logger: logger,
+ }
+ if err := c.init(); err != nil {
+ client.Close()
+ return nil, err
+ }
+ return c, nil
+}
+
+// init runs some initialization commands on the remote server to ensure it's
+// capable of acting as puppeth target.
+func (client *sshClient) init() error {
+ client.logger.Debug("Verifying if docker is available")
+ if out, err := client.Run("docker version"); err != nil {
+ if len(out) == 0 {
+ return err
+ }
+ return fmt.Errorf("docker configured incorrectly: %s", out)
+ }
+ client.logger.Debug("Verifying if docker-compose is available")
+ if out, err := client.Run("docker-compose version"); err != nil {
+ if len(out) == 0 {
+ return err
+ }
+ return fmt.Errorf("docker-compose configured incorrectly: %s", out)
+ }
+ return nil
+}
+
+// Close terminates the connection to an SSH server.
+func (client *sshClient) Close() error {
+ return client.client.Close()
+}
+
+// Run executes a command on the remote server and returns the combined output
+// along with any error status.
+func (client *sshClient) Run(cmd string) ([]byte, error) {
+ // Establish a single command session
+ session, err := client.client.NewSession()
+ if err != nil {
+ return nil, err
+ }
+ defer session.Close()
+
+ // Execute the command and return any output
+ client.logger.Trace("Running command on remote server", "cmd", cmd)
+ return session.CombinedOutput(cmd)
+}
+
+// Stream executes a command on the remote server and streams all outputs into
+// the local stdout and stderr streams.
+func (client *sshClient) Stream(cmd string) error {
+ // Establish a single command session
+ session, err := client.client.NewSession()
+ if err != nil {
+ return err
+ }
+ defer session.Close()
+
+ session.Stdout = os.Stdout
+ session.Stderr = os.Stderr
+
+ // Execute the command and return any output
+ client.logger.Trace("Streaming command on remote server", "cmd", cmd)
+ return session.Run(cmd)
+}
+
+// Upload copied the set of files to a remote server via SCP, creating any non-
+// existing folder in te mean time.
+func (client *sshClient) Upload(files map[string][]byte) ([]byte, error) {
+ // Establish a single command session
+ session, err := client.client.NewSession()
+ if err != nil {
+ return nil, err
+ }
+ defer session.Close()
+
+ // Create a goroutine that streams the SCP content
+ go func() {
+ out, _ := session.StdinPipe()
+ defer out.Close()
+
+ for file, content := range files {
+ client.logger.Trace("Uploading file to server", "file", file, "bytes", len(content))
+
+ fmt.Fprintln(out, "D0755", 0, filepath.Dir(file)) // Ensure the folder exists
+ fmt.Fprintln(out, "C0644", len(content), filepath.Base(file)) // Create the actual file
+ out.Write(content) // Stream the data content
+ fmt.Fprint(out, "\x00") // Transfer end with \x00
+ fmt.Fprintln(out, "E") // Leave directory (simpler)
+ }
+ }()
+ return session.CombinedOutput("/usr/bin/scp -v -tr ./")
+}
diff --git a/cmd/puppeth/wizard.go b/cmd/puppeth/wizard.go
new file mode 100644
index 000000000..92d7962a0
--- /dev/null
+++ b/cmd/puppeth/wizard.go
@@ -0,0 +1,229 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "math/big"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/log"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+// config contains all the configurations needed by puppeth that should be saved
+// between sessions.
+type config struct {
+ path string // File containing the configuration values
+ genesis *core.Genesis // Genesis block to cache for node deploys
+ bootFull []string // Bootnodes to always connect to by full nodes
+ bootLight []string // Bootnodes to always connect to by light nodes
+ ethstats string // Ethstats settings to cache for node deploys
+
+ Servers []string `json:"servers,omitempty"`
+}
+
+// flush dumps the contents of config to disk.
+func (c config) flush() {
+ os.MkdirAll(filepath.Dir(c.path), 0755)
+
+ sort.Strings(c.Servers)
+ out, _ := json.MarshalIndent(c, "", " ")
+ if err := ioutil.WriteFile(c.path, out, 0644); err != nil {
+ log.Warn("Failed to save puppeth configs", "file", c.path, "err", err)
+ }
+}
+
+type wizard struct {
+ network string // Network name to manage
+ conf config // Configurations from previous runs
+
+ servers map[string]*sshClient // SSH connections to servers to administer
+ services map[string][]string // Ethereum services known to be running on servers
+
+ in *bufio.Reader // Wrapper around stdin to allow reading user input
+}
+
+// read reads a single line from stdin, trimming if from spaces.
+func (w *wizard) read() string {
+ fmt.Printf("> ")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ return strings.TrimSpace(text)
+}
+
+// readString reads a single line from stdin, trimming if from spaces, enforcing
+// non-emptyness.
+func (w *wizard) readString() string {
+ for {
+ fmt.Printf("> ")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ if text = strings.TrimSpace(text); text != "" {
+ return text
+ }
+ }
+}
+
+// readDefaultString reads a single line from stdin, trimming if from spaces. If
+// an empty line is entered, the default value is returned.
+func (w *wizard) readDefaultString(def string) string {
+ for {
+ fmt.Printf("> ")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ if text = strings.TrimSpace(text); text != "" {
+ return text
+ }
+ return def
+ }
+}
+
+// readInt reads a single line from stdin, trimming if from spaces, enforcing it
+// to parse into an integer.
+func (w *wizard) readInt() int {
+ for {
+ fmt.Printf("> ")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ if text = strings.TrimSpace(text); text == "" {
+ continue
+ }
+ val, err := strconv.Atoi(strings.TrimSpace(text))
+ if err != nil {
+ log.Error("Invalid input, expected integer", "err", err)
+ continue
+ }
+ return val
+ }
+}
+
+// readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing
+// it to parse into an integer. If an empty line is entered, the default value is
+// returned.
+func (w *wizard) readDefaultInt(def int) int {
+ for {
+ fmt.Printf("> ")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ if text = strings.TrimSpace(text); text == "" {
+ return def
+ }
+ val, err := strconv.Atoi(strings.TrimSpace(text))
+ if err != nil {
+ log.Error("Invalid input, expected integer", "err", err)
+ continue
+ }
+ return val
+ }
+}
+
+// readPassword reads a single line from stdin, trimming it from the trailing new
+// line and returns it. The input will not be echoed.
+func (w *wizard) readPassword() string {
+ for {
+ fmt.Printf("> ")
+ text, err := terminal.ReadPassword(int(syscall.Stdin))
+ if err != nil {
+ log.Crit("Failed to read password", "err", err)
+ }
+ fmt.Println()
+ return string(text)
+ }
+}
+
+// readAddress reads a single line from stdin, trimming if from spaces and converts
+// it to an Ethereum address.
+func (w *wizard) readAddress() *common.Address {
+ for {
+ // Read the address from the user
+ fmt.Printf("> 0x")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ if text = strings.TrimSpace(text); text == "" {
+ return nil
+ }
+ // Make sure it looks ok and return it if so
+ if len(text) != 40 {
+ log.Error("Invalid address length, please retry")
+ continue
+ }
+ bigaddr, _ := new(big.Int).SetString(text, 16)
+ address := common.BigToAddress(bigaddr)
+ return &address
+ }
+}
+
+// readDefaultAddress reads a single line from stdin, trimming if from spaces and
+// converts it to an Ethereum address. If an empty line is entered, the default
+// value is returned.
+func (w *wizard) readDefaultAddress(def common.Address) common.Address {
+ for {
+ // Read the address from the user
+ fmt.Printf("> 0x")
+ text, err := w.in.ReadString('\n')
+ if err != nil {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ if text = strings.TrimSpace(text); text == "" {
+ return def
+ }
+ // Make sure it looks ok and return it if so
+ if len(text) != 40 {
+ log.Error("Invalid address length, please retry")
+ continue
+ }
+ bigaddr, _ := new(big.Int).SetString(text, 16)
+ return common.BigToAddress(bigaddr)
+ }
+}
+
+// readJSON reads a raw JSON message and returns it.
+func (w *wizard) readJSON() string {
+ var blob json.RawMessage
+
+ for {
+ fmt.Printf("> ")
+ if err := json.NewDecoder(w.in).Decode(&blob); err != nil {
+ log.Error("Invalid JSON, please try again", "err", err)
+ continue
+ }
+ return string(blob)
+ }
+}
diff --git a/cmd/puppeth/wizard_dashboard.go b/cmd/puppeth/wizard_dashboard.go
new file mode 100644
index 000000000..53a28a535
--- /dev/null
+++ b/cmd/puppeth/wizard_dashboard.go
@@ -0,0 +1,132 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// deployDashboard queries the user for various input on deploying a web-service
+// dashboard, after which is pushes the container.
+func (w *wizard) deployDashboard() {
+ // Select the server to interact with
+ server := w.selectServer()
+ if server == "" {
+ return
+ }
+ client := w.servers[server]
+
+ // Retrieve any active dashboard configurations from the server
+ infos, err := checkDashboard(client, w.network)
+ if err != nil {
+ infos = &dashboardInfos{
+ port: 80,
+ host: client.server,
+ }
+ }
+ // Figure out which port to listen on
+ fmt.Println()
+ fmt.Printf("Which port should the dashboard listen on? (default = %d)\n", infos.port)
+ infos.port = w.readDefaultInt(infos.port)
+
+ // Figure which virtual-host to deploy the dashboard on
+ infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host)
+ if err != nil {
+ log.Error("Failed to decide on dashboard host", "err", err)
+ return
+ }
+ // Port and proxy settings retrieved, figure out which services are available
+ available := make(map[string][]string)
+ for server, services := range w.services {
+ for _, service := range services {
+ available[service] = append(available[service], server)
+ }
+ }
+ listing := make(map[string]string)
+ for _, service := range []string{"ethstats", "explorer", "wallet", "faucet"} {
+ // Gather all the locally hosted pages of this type
+ var pages []string
+ for _, server := range available[service] {
+ client := w.servers[server]
+ if client == nil {
+ continue
+ }
+ // If there's a service running on the machine, retrieve it's port number
+ var port int
+ switch service {
+ case "ethstats":
+ if infos, err := checkEthstats(client, w.network); err == nil {
+ port = infos.port
+ }
+ case "faucet":
+ if infos, err := checkFaucet(client, w.network); err == nil {
+ port = infos.port
+ }
+ }
+ if page, err := resolve(client, w.network, service, port); err == nil && page != "" {
+ pages = append(pages, page)
+ }
+ }
+ // Promt the user to chose one, enter manually or simply not list this service
+ defLabel, defChoice := "don't list", len(pages)+2
+ if len(pages) > 0 {
+ defLabel, defChoice = pages[0], 1
+ }
+ fmt.Println()
+ fmt.Printf("Which %s service to list? (default = %s)\n", service, defLabel)
+ for i, page := range pages {
+ fmt.Printf(" %d. %s\n", i+1, page)
+ }
+ fmt.Printf(" %d. List external %s service\n", len(pages)+1, service)
+ fmt.Printf(" %d. Don't list any %s service\n", len(pages)+2, service)
+
+ choice := w.readDefaultInt(defChoice)
+ if choice < 0 || choice > len(pages)+2 {
+ log.Error("Invalid listing choice, aborting")
+ return
+ }
+ switch {
+ case choice <= len(pages):
+ listing[service] = pages[choice-1]
+ case choice == len(pages)+1:
+ fmt.Println()
+ fmt.Printf("Which address is the external %s service at?\n", service)
+ listing[service] = w.readString()
+ default:
+ // No service hosting for this
+ }
+ }
+ // If we have ethstats running, ask whether to make the secret public or not
+ var ethstats bool
+ if w.conf.ethstats != "" {
+ fmt.Println()
+ fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
+ ethstats = w.readDefaultString("y") == "y"
+ }
+ // Try to deploy the dashboard container on the host
+ if out, err := deployDashboard(client, w.network, infos.port, infos.host, listing, &w.conf, ethstats); err != nil {
+ log.Error("Failed to deploy dashboard container", "err", err)
+ if len(out) > 0 {
+ fmt.Printf("%s\n", out)
+ }
+ return
+ }
+ // All ok, run a network scan to pick any changes up
+ w.networkStats(false)
+}
diff --git a/cmd/puppeth/wizard_ethstats.go b/cmd/puppeth/wizard_ethstats.go
new file mode 100644
index 000000000..c117a6027
--- /dev/null
+++ b/cmd/puppeth/wizard_ethstats.go
@@ -0,0 +1,79 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// deployEthstats queries the user for various input on deploying an ethstats
+// monitoring server, after which it executes it.
+func (w *wizard) deployEthstats() {
+ // Select the server to interact with
+ server := w.selectServer()
+ if server == "" {
+ return
+ }
+ client := w.servers[server]
+
+ // Retrieve any active ethstats configurations from the server
+ infos, err := checkEthstats(client, w.network)
+ if err != nil {
+ infos = &ethstatsInfos{
+ port: 80,
+ host: client.server,
+ secret: "",
+ }
+ }
+ // Figure out which port to listen on
+ fmt.Println()
+ fmt.Printf("Which port should ethstats listen on? (default = %d)\n", infos.port)
+ infos.port = w.readDefaultInt(infos.port)
+
+ // Figure which virtual-host to deploy ethstats on
+ if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil {
+ log.Error("Failed to decide on ethstats host", "err", err)
+ return
+ }
+ // Port and proxy settings retrieved, figure out the secret and boot ethstats
+ fmt.Println()
+ if infos.secret == "" {
+ fmt.Printf("What should be the secret password for the API? (must not be empty)\n")
+ infos.secret = w.readString()
+ } else {
+ fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret)
+ infos.secret = w.readDefaultString(infos.secret)
+ }
+ // Try to deploy the ethstats server on the host
+ trusted := make([]string, 0, len(w.servers))
+ for _, client := range w.servers {
+ if client != nil {
+ trusted = append(trusted, client.address)
+ }
+ }
+ if out, err := deployEthstats(client, w.network, infos.port, infos.secret, infos.host, trusted); err != nil {
+ log.Error("Failed to deploy ethstats container", "err", err)
+ if len(out) > 0 {
+ fmt.Printf("%s\n", out)
+ }
+ return
+ }
+ // All ok, run a network scan to pick any changes up
+ w.networkStats(false)
+}
diff --git a/cmd/puppeth/wizard_faucet.go b/cmd/puppeth/wizard_faucet.go
new file mode 100644
index 000000000..71d1c910b
--- /dev/null
+++ b/cmd/puppeth/wizard_faucet.go
@@ -0,0 +1,172 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// deployFaucet queries the user for various input on deploying a faucet, after
+// which it executes it.
+func (w *wizard) deployFaucet() {
+ // Select the server to interact with
+ server := w.selectServer()
+ if server == "" {
+ return
+ }
+ client := w.servers[server]
+
+ // Retrieve any active faucet configurations from the server
+ infos, err := checkFaucet(client, w.network)
+ if err != nil {
+ infos = &faucetInfos{
+ node: &nodeInfos{portFull: 30303, peersTotal: 25},
+ port: 80,
+ host: client.server,
+ amount: 1,
+ minutes: 1440,
+ }
+ }
+ infos.node.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ")
+ infos.node.network = w.conf.genesis.Config.ChainId.Int64()
+
+ // Figure out which port to listen on
+ fmt.Println()
+ fmt.Printf("Which port should the faucet listen on? (default = %d)\n", infos.port)
+ infos.port = w.readDefaultInt(infos.port)
+
+ // Figure which virtual-host to deploy ethstats on
+ if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil {
+ log.Error("Failed to decide on faucet host", "err", err)
+ return
+ }
+ // Port and proxy settings retrieved, figure out the funcing amount per perdion configurations
+ fmt.Println()
+ fmt.Printf("How many Ethers to release per request? (default = %d)\n", infos.amount)
+ infos.amount = w.readDefaultInt(infos.amount)
+
+ fmt.Println()
+ fmt.Printf("How many minutes to enforce between requests? (default = %d)\n", infos.minutes)
+ infos.minutes = w.readDefaultInt(infos.minutes)
+
+ // Accessing GitHub gists requires API authorization, retrieve it
+ if infos.githubUser != "" {
+ fmt.Println()
+ fmt.Printf("Reused previous (%s) GitHub API authorization (y/n)? (default = yes)\n", infos.githubUser)
+ if w.readDefaultString("y") != "y" {
+ infos.githubUser, infos.githubToken = "", ""
+ }
+ }
+ if infos.githubUser == "" {
+ // No previous authorization (or new one requested)
+ fmt.Println()
+ fmt.Println("Which GitHub user to verify Gists through?")
+ infos.githubUser = w.readString()
+
+ fmt.Println()
+ fmt.Println("What is the GitHub personal access token of the user? (won't be echoed)")
+ infos.githubToken = w.readPassword()
+
+ // Do a sanity check query against github to ensure it's valid
+ req, _ := http.NewRequest("GET", "https://api.github.com/user", nil)
+ req.SetBasicAuth(infos.githubUser, infos.githubToken)
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ log.Error("Failed to verify GitHub authentication", "err", err)
+ return
+ }
+ defer res.Body.Close()
+
+ var msg struct {
+ Login string `json:"login"`
+ Message string `json:"message"`
+ }
+ if err = json.NewDecoder(res.Body).Decode(&msg); err != nil {
+ log.Error("Failed to decode authorization response", "err", err)
+ return
+ }
+ if msg.Login != infos.githubUser {
+ log.Error("GitHub authorization failed", "user", infos.githubUser, "message", msg.Message)
+ return
+ }
+ }
+ // Figure out where the user wants to store the persistent data
+ fmt.Println()
+ if infos.node.datadir == "" {
+ fmt.Printf("Where should data be stored on the remote machine?\n")
+ infos.node.datadir = w.readString()
+ } else {
+ fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.node.datadir)
+ infos.node.datadir = w.readDefaultString(infos.node.datadir)
+ }
+ // Figure out which port to listen on
+ fmt.Println()
+ fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.portFull)
+ infos.node.portFull = w.readDefaultInt(infos.node.portFull)
+
+ // Set a proper name to report on the stats page
+ fmt.Println()
+ if infos.node.ethstats == "" {
+ fmt.Printf("What should the node be called on the stats page?\n")
+ infos.node.ethstats = w.readString() + ":" + w.conf.ethstats
+ } else {
+ fmt.Printf("What should the node be called on the stats page? (default = %s)\n", infos.node.ethstats)
+ infos.node.ethstats = w.readDefaultString(infos.node.ethstats) + ":" + w.conf.ethstats
+ }
+ // Load up the credential needed to release funds
+ if infos.node.keyJSON != "" {
+ var key keystore.Key
+ if err := json.Unmarshal([]byte(infos.node.keyJSON), &key); err != nil {
+ infos.node.keyJSON, infos.node.keyPass = "", ""
+ } else {
+ fmt.Println()
+ fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex())
+ if w.readDefaultString("y") != "y" {
+ infos.node.keyJSON, infos.node.keyPass = "", ""
+ }
+ }
+ }
+ if infos.node.keyJSON == "" {
+ fmt.Println()
+ fmt.Println("Please paste the faucet's funding account key JSON:")
+ infos.node.keyJSON = w.readJSON()
+
+ fmt.Println()
+ fmt.Println("What's the unlock password for the account? (won't be echoed)")
+ infos.node.keyPass = w.readPassword()
+
+ if _, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil {
+ log.Error("Failed to decrypt key with given passphrase")
+ return
+ }
+ }
+ // Try to deploy the faucet server on the host
+ if out, err := deployFaucet(client, w.network, w.conf.bootLight, infos); err != nil {
+ log.Error("Failed to deploy faucet container", "err", err)
+ if len(out) > 0 {
+ fmt.Printf("%s\n", out)
+ }
+ return
+ }
+ // All ok, run a network scan to pick any changes up
+ w.networkStats(false)
+}
diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go
new file mode 100644
index 000000000..a67812e92
--- /dev/null
+++ b/cmd/puppeth/wizard_genesis.go
@@ -0,0 +1,136 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "math/rand"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// makeGenesis creates a new genesis struct based on some user input.
+func (w *wizard) makeGenesis() {
+ // Construct a default genesis block
+ genesis := &core.Genesis{
+ Timestamp: uint64(time.Now().Unix()),
+ GasLimit: 4700000,
+ Difficulty: big.NewInt(1048576),
+ Alloc: make(core.GenesisAlloc),
+ Config: &params.ChainConfig{
+ HomesteadBlock: big.NewInt(1),
+ EIP150Block: big.NewInt(2),
+ EIP155Block: big.NewInt(3),
+ EIP158Block: big.NewInt(3),
+ },
+ }
+ // Figure out which consensus engine to choose
+ fmt.Println()
+ fmt.Println("Which consensus engine to use? (default = clique)")
+ fmt.Println(" 1. Ethash - proof-of-work")
+ fmt.Println(" 2. Clique - proof-of-authority")
+
+ choice := w.read()
+ switch {
+ case choice == "1":
+ // In case of ethash, we're pretty much done
+ genesis.Config.Ethash = new(params.EthashConfig)
+ genesis.ExtraData = make([]byte, 32)
+
+ case choice == "" || choice == "2":
+ // In the case of clique, configure the consensus parameters
+ genesis.Difficulty = big.NewInt(1)
+ genesis.Config.Clique = &params.CliqueConfig{
+ Period: 15,
+ Epoch: 30000,
+ }
+ fmt.Println()
+ fmt.Println("How many seconds should blocks take? (default = 15)")
+ genesis.Config.Clique.Period = uint64(w.readDefaultInt(15))
+
+ // We also need the initial list of signers
+ fmt.Println()
+ fmt.Println("Which accounts are allowed to seal? (mandatory at least one)")
+
+ var signers []common.Address
+ for {
+ if address := w.readAddress(); address != nil {
+ signers = append(signers, *address)
+ continue
+ }
+ if len(signers) > 0 {
+ break
+ }
+ }
+ // Sort the signers and embed into the extra-data section
+ for i := 0; i < len(signers); i++ {
+ for j := i + 1; j < len(signers); j++ {
+ if bytes.Compare(signers[i][:], signers[j][:]) > 0 {
+ signers[i], signers[j] = signers[j], signers[i]
+ }
+ }
+ }
+ genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
+ for i, signer := range signers {
+ copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
+ }
+
+ default:
+ log.Crit("Invalid consensus engine choice", "choice", choice)
+ }
+ // Consensus all set, just ask for initial funds and go
+ fmt.Println()
+ fmt.Println("Which accounts should be pre-funded? (advisable at least one)")
+ for {
+ // Read the address of the account to fund
+ if address := w.readAddress(); address != nil {
+ genesis.Alloc[*address] = core.GenesisAccount{
+ Balance: new(big.Int).Lsh(big.NewInt(1), 256-7), // 2^256 / 128 (allow many pre-funds without balance overflows)
+ }
+ continue
+ }
+ break
+ }
+ // Add a batch of precompile balances to avoid them getting deleted
+ for i := int64(0); i < 256; i++ {
+ genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
+ }
+ fmt.Println()
+
+ // Query the user for some custom extras
+ fmt.Println()
+ fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
+ genesis.Config.ChainId = big.NewInt(int64(w.readDefaultInt(rand.Intn(65536))))
+
+ fmt.Println()
+ fmt.Println("Anything fun to embed into the genesis block? (max 32 bytes)")
+
+ extra := w.read()
+ if len(extra) > 32 {
+ extra = extra[:32]
+ }
+ genesis.ExtraData = append([]byte(extra), genesis.ExtraData[len(extra):]...)
+
+ // All done, store the genesis and flush to disk
+ w.conf.genesis = genesis
+}
diff --git a/cmd/puppeth/wizard_intro.go b/cmd/puppeth/wizard_intro.go
new file mode 100644
index 000000000..46383bb54
--- /dev/null
+++ b/cmd/puppeth/wizard_intro.go
@@ -0,0 +1,153 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// makeWizard creates and returns a new puppeth wizard.
+func makeWizard(network string) *wizard {
+ return &wizard{
+ network: network,
+ servers: make(map[string]*sshClient),
+ services: make(map[string][]string),
+ in: bufio.NewReader(os.Stdin),
+ }
+}
+
+// run displays some useful infos to the user, starting on the journey of
+// setting up a new or managing an existing Ethereum private network.
+func (w *wizard) run() {
+ fmt.Println("+-----------------------------------------------------------+")
+ fmt.Println("| Welcome to puppeth, your Ethereum private network manager |")
+ fmt.Println("| |")
+ fmt.Println("| This tool lets you create a new Ethereum network down to |")
+ fmt.Println("| the genesis block, bootnodes, miners and ethstats servers |")
+ fmt.Println("| without the hassle that it would normally entail. |")
+ fmt.Println("| |")
+ fmt.Println("| Puppeth uses SSH to dial in to remote servers, and builds |")
+ fmt.Println("| its network components out of Docker containers using the |")
+ fmt.Println("| docker-compose toolset. |")
+ fmt.Println("+-----------------------------------------------------------+")
+ fmt.Println()
+
+ // Make sure we have a good network name to work with fmt.Println()
+ if w.network == "" {
+ fmt.Println("Please specify a network name to administer (no spaces, please)")
+ for {
+ w.network = w.readString()
+ if !strings.Contains(w.network, " ") {
+ fmt.Printf("Sweet, you can set this via --network=%s next time!\n\n", w.network)
+ break
+ }
+ log.Error("I also like to live dangerously, still no spaces")
+ }
+ }
+ log.Info("Administering Ethereum network", "name", w.network)
+
+ // Load initial configurations and connect to all live servers
+ w.conf.path = filepath.Join(os.Getenv("HOME"), ".puppeth", w.network)
+
+ blob, err := ioutil.ReadFile(w.conf.path)
+ if err != nil {
+ log.Warn("No previous configurations found", "path", w.conf.path)
+ } else if err := json.Unmarshal(blob, &w.conf); err != nil {
+ log.Crit("Previous configuration corrupted", "path", w.conf.path, "err", err)
+ } else {
+ for _, server := range w.conf.Servers {
+ log.Info("Dialing previously configured server", "server", server)
+ client, err := dial(server)
+ if err != nil {
+ log.Error("Previous server unreachable", "server", server, "err", err)
+ }
+ w.servers[server] = client
+ }
+ w.networkStats(false)
+ }
+ // Basics done, loop ad infinitum about what to do
+ for {
+ fmt.Println()
+ fmt.Println("What would you like to do? (default = stats)")
+ fmt.Println(" 1. Show network stats")
+ if w.conf.genesis == nil {
+ fmt.Println(" 2. Configure new genesis")
+ } else {
+ fmt.Println(" 2. Save existing genesis")
+ }
+ if len(w.servers) == 0 {
+ fmt.Println(" 3. Track new remote server")
+ } else {
+ fmt.Println(" 3. Manage tracked machines")
+ }
+ if len(w.services) == 0 {
+ fmt.Println(" 4. Deploy network components")
+ } else {
+ fmt.Println(" 4. Manage network components")
+ }
+ //fmt.Println(" 5. ProTips for common usecases")
+
+ choice := w.read()
+ switch {
+ case choice == "" || choice == "1":
+ w.networkStats(false)
+
+ case choice == "2":
+ // If we don't have a genesis, make one
+ if w.conf.genesis == nil {
+ w.makeGenesis()
+ } else {
+ // Otherwise just save whatever we currently have
+ fmt.Println()
+ fmt.Printf("Which file to save the genesis into? (default = %s.json)\n", w.network)
+ out, _ := json.MarshalIndent(w.conf.genesis, "", " ")
+ if err := ioutil.WriteFile(w.readDefaultString(fmt.Sprintf("%s.json", w.network)), out, 0644); err != nil {
+ log.Error("Failed to save genesis file", "err", err)
+ }
+ log.Info("Exported existing genesis block")
+ }
+ case choice == "3":
+ if len(w.servers) == 0 {
+ if w.makeServer() != "" {
+ w.networkStats(false)
+ }
+ } else {
+ w.manageServers()
+ }
+ case choice == "4":
+ if len(w.services) == 0 {
+ w.deployComponent()
+ } else {
+ w.manageComponents()
+ }
+
+ case choice == "5":
+ w.networkStats(true)
+
+ default:
+ log.Error("That's not something I can do")
+ }
+ }
+}
diff --git a/cmd/puppeth/wizard_netstats.go b/cmd/puppeth/wizard_netstats.go
new file mode 100644
index 000000000..c2a933a55
--- /dev/null
+++ b/cmd/puppeth/wizard_netstats.go
@@ -0,0 +1,235 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/olekukonko/tablewriter"
+)
+
+// networkStats verifies the status of network components and generates a protip
+// configuration set to give users hints on how to do various tasks.
+func (w *wizard) networkStats(tips bool) {
+ if len(w.servers) == 0 {
+ log.Error("No remote machines to gather stats from")
+ return
+ }
+ protips := new(protips)
+
+ // Iterate over all the specified hosts and check their status
+ stats := tablewriter.NewWriter(os.Stdout)
+ stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"})
+ stats.SetColWidth(128)
+
+ for _, server := range w.conf.Servers {
+ client := w.servers[server]
+ logger := log.New("server", server)
+ logger.Info("Starting remote server health-check")
+
+ // If the server is not connected, try to connect again
+ if client == nil {
+ conn, err := dial(server)
+ if err != nil {
+ logger.Error("Failed to establish remote connection", "err", err)
+ stats.Append([]string{server, "", err.Error(), "", ""})
+ continue
+ }
+ client = conn
+ }
+ // Client connected one way or another, run health-checks
+ services := make(map[string]string)
+ logger.Debug("Checking for nginx availability")
+ if infos, err := checkNginx(client, w.network); err != nil {
+ if err != ErrServiceUnknown {
+ services["nginx"] = err.Error()
+ }
+ } else {
+ services["nginx"] = infos.String()
+ }
+ logger.Debug("Checking for ethstats availability")
+ if infos, err := checkEthstats(client, w.network); err != nil {
+ if err != ErrServiceUnknown {
+ services["ethstats"] = err.Error()
+ }
+ } else {
+ services["ethstats"] = infos.String()
+ protips.ethstats = infos.config
+ }
+ logger.Debug("Checking for bootnode availability")
+ if infos, err := checkNode(client, w.network, true); err != nil {
+ if err != ErrServiceUnknown {
+ services["bootnode"] = err.Error()
+ }
+ } else {
+ services["bootnode"] = infos.String()
+
+ protips.genesis = string(infos.genesis)
+ protips.bootFull = append(protips.bootFull, infos.enodeFull)
+ if infos.enodeLight != "" {
+ protips.bootLight = append(protips.bootLight, infos.enodeLight)
+ }
+ }
+ logger.Debug("Checking for sealnode availability")
+ if infos, err := checkNode(client, w.network, false); err != nil {
+ if err != ErrServiceUnknown {
+ services["sealnode"] = err.Error()
+ }
+ } else {
+ services["sealnode"] = infos.String()
+ protips.genesis = string(infos.genesis)
+ }
+ logger.Debug("Checking for faucet availability")
+ if infos, err := checkFaucet(client, w.network); err != nil {
+ if err != ErrServiceUnknown {
+ services["faucet"] = err.Error()
+ }
+ } else {
+ services["faucet"] = infos.String()
+ }
+ logger.Debug("Checking for dashboard availability")
+ if infos, err := checkDashboard(client, w.network); err != nil {
+ if err != ErrServiceUnknown {
+ services["dashboard"] = err.Error()
+ }
+ } else {
+ services["dashboard"] = infos.String()
+ }
+ // All status checks complete, report and check next server
+ delete(w.services, server)
+ for service := range services {
+ w.services[server] = append(w.services[server], service)
+ }
+ server, address := client.server, client.address
+ for service, status := range services {
+ stats.Append([]string{server, address, "online", service, status})
+ server, address = "", ""
+ }
+ if len(services) == 0 {
+ stats.Append([]string{server, address, "online", "", ""})
+ }
+ }
+ // If a genesis block was found, load it into our configs
+ if protips.genesis != "" {
+ genesis := new(core.Genesis)
+ if err := json.Unmarshal([]byte(protips.genesis), genesis); err != nil {
+ log.Error("Failed to parse remote genesis", "err", err)
+ } else {
+ w.conf.genesis = genesis
+ protips.network = genesis.Config.ChainId.Int64()
+ }
+ }
+ if protips.ethstats != "" {
+ w.conf.ethstats = protips.ethstats
+ }
+ w.conf.bootFull = protips.bootFull
+ w.conf.bootLight = protips.bootLight
+
+ // Print any collected stats and return
+ if !tips {
+ stats.Render()
+ } else {
+ protips.print(w.network)
+ }
+}
+
+// protips contains a collection of network infos to report pro-tips
+// based on.
+type protips struct {
+ genesis string
+ network int64
+ bootFull []string
+ bootLight []string
+ ethstats string
+}
+
+// print analyzes the network information available and prints a collection of
+// pro tips for the user's consideration.
+func (p *protips) print(network string) {
+ // If a known genesis block is available, display it and prepend an init command
+ fullinit, lightinit := "", ""
+ if p.genesis != "" {
+ fullinit = fmt.Sprintf("geth --datadir=$HOME/.%s init %s.json && ", network, network)
+ lightinit = fmt.Sprintf("geth --datadir=$HOME/.%s --light init %s.json && ", network, network)
+ }
+ // If an ethstats server is available, add the ethstats flag
+ statsflag := ""
+ if p.ethstats != "" {
+ if strings.Contains(p.ethstats, " ") {
+ statsflag = fmt.Sprintf(` --ethstats="yournode:%s"`, p.ethstats)
+ } else {
+ statsflag = fmt.Sprintf(` --ethstats=yournode:%s`, p.ethstats)
+ }
+ }
+ // If bootnodes have been specified, add the bootnode flag
+ bootflagFull := ""
+ if len(p.bootFull) > 0 {
+ bootflagFull = fmt.Sprintf(` --bootnodes %s`, strings.Join(p.bootFull, ","))
+ }
+ bootflagLight := ""
+ if len(p.bootLight) > 0 {
+ bootflagLight = fmt.Sprintf(` --bootnodes %s`, strings.Join(p.bootLight, ","))
+ }
+ // Assemble all the known pro-tips
+ var tasks, tips []string
+
+ tasks = append(tasks, "Run an archive node with historical data")
+ tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=1024%s%s", fullinit, p.network, network, statsflag, bootflagFull))
+
+ tasks = append(tasks, "Run a full node with recent data only")
+ tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=512 --fast%s%s", fullinit, p.network, network, statsflag, bootflagFull))
+
+ tasks = append(tasks, "Run a light node with on demand retrievals")
+ tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --light%s%s", lightinit, p.network, network, statsflag, bootflagLight))
+
+ tasks = append(tasks, "Run an embedded node with constrained memory")
+ tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=32 --light%s%s", lightinit, p.network, network, statsflag, bootflagLight))
+
+ // If the tips are short, display in a table
+ short := true
+ for _, tip := range tips {
+ if len(tip) > 100 {
+ short = false
+ break
+ }
+ }
+ fmt.Println()
+ if short {
+ howto := tablewriter.NewWriter(os.Stdout)
+ howto.SetHeader([]string{"Fun tasks for you", "Tips on how to"})
+ howto.SetColWidth(100)
+
+ for i := 0; i < len(tasks); i++ {
+ howto.Append([]string{tasks[i], tips[i]})
+ }
+ howto.Render()
+ return
+ }
+ // Meh, tips got ugly, split into many lines
+ for i := 0; i < len(tasks); i++ {
+ fmt.Println(tasks[i])
+ fmt.Println(strings.Repeat("-", len(tasks[i])))
+ fmt.Println(tips[i])
+ fmt.Println()
+ fmt.Println()
+ }
+}
diff --git a/cmd/puppeth/wizard_network.go b/cmd/puppeth/wizard_network.go
new file mode 100644
index 000000000..001d4e5b4
--- /dev/null
+++ b/cmd/puppeth/wizard_network.go
@@ -0,0 +1,194 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// manageServers displays a list of servers the user can disconnect from, and an
+// option to connect to new servers.
+func (w *wizard) manageServers() {
+ // List all the servers we can disconnect, along with an entry to connect a new one
+ fmt.Println()
+ for i, server := range w.conf.Servers {
+ fmt.Printf(" %d. Disconnect %s\n", i+1, server)
+ }
+ fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1)
+
+ choice := w.readInt()
+ if choice < 0 || choice > len(w.conf.Servers)+1 {
+ log.Error("Invalid server choice, aborting")
+ return
+ }
+ // If the user selected an existing server, drop it
+ if choice <= len(w.conf.Servers) {
+ server := w.conf.Servers[choice-1]
+ client := w.servers[server]
+
+ delete(w.servers, server)
+ if client != nil {
+ client.Close()
+ }
+ w.conf.Servers = append(w.conf.Servers[:choice-1], w.conf.Servers[choice:]...)
+ w.conf.flush()
+
+ log.Info("Disconnected existing server", "server", server)
+ w.networkStats(false)
+ return
+ }
+ // If the user requested connecting a new server, do it
+ if w.makeServer() != "" {
+ w.networkStats(false)
+ }
+}
+
+// makeServer reads a single line from stdin and interprets it as a hostname to
+// connect to. It tries to establish a new SSH session and also executing some
+// baseline validations.
+//
+// If connection succeeds, the server is added to the wizards configs!
+func (w *wizard) makeServer() string {
+ fmt.Println()
+ fmt.Println("Please enter remote server's address:")
+
+ for {
+ // Read and fial the server to ensure docker is present
+ input := w.readString()
+
+ client, err := dial(input)
+ if err != nil {
+ log.Error("Server not ready for puppeth", "err", err)
+ return ""
+ }
+ // All checks passed, start tracking the server
+ w.servers[input] = client
+ w.conf.Servers = append(w.conf.Servers, input)
+ w.conf.flush()
+
+ return input
+ }
+}
+
+// selectServer lists the user all the currnetly known servers to choose from,
+// also granting the option to add a new one.
+func (w *wizard) selectServer() string {
+ // List the available server to the user and wait for a choice
+ fmt.Println()
+ fmt.Println("Which server do you want to interact with?")
+ for i, server := range w.conf.Servers {
+ fmt.Printf(" %d. %s\n", i+1, server)
+ }
+ fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1)
+
+ choice := w.readInt()
+ if choice < 0 || choice > len(w.conf.Servers)+1 {
+ log.Error("Invalid server choice, aborting")
+ return ""
+ }
+ // If the user requested connecting to a new server, go for it
+ if choice <= len(w.conf.Servers) {
+ return w.conf.Servers[choice-1]
+ }
+ return w.makeServer()
+}
+
+// manageComponents displays a list of network components the user can tear down
+// and an option
+func (w *wizard) manageComponents() {
+ // List all the componens we can tear down, along with an entry to deploy a new one
+ fmt.Println()
+
+ var serviceHosts, serviceNames []string
+ for server, services := range w.services {
+ for _, service := range services {
+ serviceHosts = append(serviceHosts, server)
+ serviceNames = append(serviceNames, service)
+
+ fmt.Printf(" %d. Tear down %s on %s\n", len(serviceHosts), strings.Title(service), server)
+ }
+ }
+ fmt.Printf(" %d. Deploy new network component\n", len(serviceHosts)+1)
+
+ choice := w.readInt()
+ if choice < 0 || choice > len(serviceHosts)+1 {
+ log.Error("Invalid component choice, aborting")
+ return
+ }
+ // If the user selected an existing service, destroy it
+ if choice <= len(serviceHosts) {
+ // Figure out the service to destroy and execute it
+ service := serviceNames[choice-1]
+ server := serviceHosts[choice-1]
+ client := w.servers[server]
+
+ if out, err := tearDown(client, w.network, service, true); err != nil {
+ log.Error("Failed to tear down component", "err", err)
+ if len(out) > 0 {
+ fmt.Printf("%s\n", out)
+ }
+ return
+ }
+ // Clean up any references to it from out state
+ services := w.services[server]
+ for i, name := range services {
+ if name == service {
+ w.services[server] = append(services[:i], services[i+1:]...)
+ if len(w.services[server]) == 0 {
+ delete(w.services, server)
+ }
+ }
+ }
+ log.Info("Torn down existing component", "server", server, "service", service)
+ return
+ }
+ // If the user requested deploying a new component, do it
+ w.deployComponent()
+}
+
+// deployComponent displays a list of network components the user can deploy and
+// guides through the process.
+func (w *wizard) deployComponent() {
+ // Print all the things we can deploy and wait or user choice
+ fmt.Println()
+ fmt.Println("What would you like to deploy? (recommended order)")
+ fmt.Println(" 1. Ethstats - Network monitoring tool")
+ fmt.Println(" 2. Bootnode - Entry point of the network")
+ fmt.Println(" 3. Sealer - Full node minting new blocks")
+ fmt.Println(" 4. Wallet - Browser wallet for quick sends (todo)")
+ fmt.Println(" 5. Faucet - Crypto faucet to give away funds")
+ fmt.Println(" 6. Dashboard - Website listing above web-services")
+
+ switch w.read() {
+ case "1":
+ w.deployEthstats()
+ case "2":
+ w.deployNode(true)
+ case "3":
+ w.deployNode(false)
+ case "4":
+ case "5":
+ w.deployFaucet()
+ case "6":
+ w.deployDashboard()
+ default:
+ log.Error("That's not something I can do")
+ }
+}
diff --git a/cmd/puppeth/wizard_nginx.go b/cmd/puppeth/wizard_nginx.go
new file mode 100644
index 000000000..86fba29f5
--- /dev/null
+++ b/cmd/puppeth/wizard_nginx.go
@@ -0,0 +1,58 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// ensureVirtualHost checks whether a reverse-proxy is running on the specified
+// host machine, and if yes requests a virtual host from the user to host a
+// specific web service on. If no proxy exists, the method will offer to deploy
+// one.
+//
+// If the user elects not to use a reverse proxy, an empty hostname is returned!
+func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (string, error) {
+ if proxy, _ := checkNginx(client, w.network); proxy != nil {
+ // Reverse proxy is running, if ports match, we need a virtual host
+ if proxy.port == port {
+ fmt.Println()
+ fmt.Printf("Shared port, which domain to assign? (default = %s)\n", def)
+ return w.readDefaultString(def), nil
+ }
+ }
+ // Reverse proxy is not running, offer to deploy a new one
+ fmt.Println()
+ fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)")
+ if w.readDefaultString("y") == "y" {
+ if out, err := deployNginx(client, w.network, port); err != nil {
+ log.Error("Failed to deploy reverse-proxy", "err", err)
+ if len(out) > 0 {
+ fmt.Printf("%s\n", out)
+ }
+ return "", err
+ }
+ // Reverse proxy deployed, ask again for the virtual-host
+ fmt.Println()
+ fmt.Printf("Proxy deployed, which domain to assign? (default = %s)\n", def)
+ return w.readDefaultString(def), nil
+ }
+ // Reverse proxy not requested, deploy as a standalone service
+ return "", nil
+}
diff --git a/cmd/puppeth/wizard_node.go b/cmd/puppeth/wizard_node.go
new file mode 100644
index 000000000..d70d8f3c9
--- /dev/null
+++ b/cmd/puppeth/wizard_node.go
@@ -0,0 +1,153 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// deployNode creates a new node configuration based on some user input.
+func (w *wizard) deployNode(boot bool) {
+ // Do some sanity check before the user wastes time on input
+ if w.conf.genesis == nil {
+ log.Error("No genesis block configured")
+ return
+ }
+ if w.conf.ethstats == "" {
+ log.Error("No ethstats server configured")
+ return
+ }
+ // Select the server to interact with
+ server := w.selectServer()
+ if server == "" {
+ return
+ }
+ client := w.servers[server]
+
+ // Retrieve any active ethstats configurations from the server
+ infos, err := checkNode(client, w.network, boot)
+ if err != nil {
+ if boot {
+ infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256}
+ } else {
+ infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0}
+ }
+ }
+ infos.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ")
+ infos.network = w.conf.genesis.Config.ChainId.Int64()
+
+ // Figure out where the user wants to store the persistent data
+ fmt.Println()
+ if infos.datadir == "" {
+ fmt.Printf("Where should data be stored on the remote machine?\n")
+ infos.datadir = w.readString()
+ } else {
+ fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.datadir)
+ infos.datadir = w.readDefaultString(infos.datadir)
+ }
+ // Figure out which port to listen on
+ fmt.Println()
+ fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.portFull)
+ infos.portFull = w.readDefaultInt(infos.portFull)
+
+ // Figure out how many peers to allow (different based on node type)
+ fmt.Println()
+ fmt.Printf("How many peers to allow connecting? (default = %d)\n", infos.peersTotal)
+ infos.peersTotal = w.readDefaultInt(infos.peersTotal)
+
+ // Figure out how many light peers to allow (different based on node type)
+ fmt.Println()
+ fmt.Printf("How many light peers to allow connecting? (default = %d)\n", infos.peersLight)
+ infos.peersLight = w.readDefaultInt(infos.peersLight)
+
+ // Set a proper name to report on the stats page
+ fmt.Println()
+ if infos.ethstats == "" {
+ fmt.Printf("What should the node be called on the stats page?\n")
+ infos.ethstats = w.readString() + ":" + w.conf.ethstats
+ } else {
+ fmt.Printf("What should the node be called on the stats page? (default = %s)\n", infos.ethstats)
+ infos.ethstats = w.readDefaultString(infos.ethstats) + ":" + w.conf.ethstats
+ }
+ // If the node is a miner/signer, load up needed credentials
+ if !boot {
+ if w.conf.genesis.Config.Ethash != nil {
+ // Ethash based miners only need an etherbase to mine against
+ fmt.Println()
+ if infos.etherbase == "" {
+ fmt.Printf("What address should the miner user?\n")
+ for {
+ if address := w.readAddress(); address != nil {
+ infos.etherbase = address.Hex()
+ break
+ }
+ }
+ } else {
+ fmt.Printf("What address should the miner user? (default = %s)\n", infos.etherbase)
+ infos.etherbase = w.readDefaultAddress(common.HexToAddress(infos.etherbase)).Hex()
+ }
+ } else if w.conf.genesis.Config.Clique != nil {
+ // If a previous signer was already set, offer to reuse it
+ if infos.keyJSON != "" {
+ var key keystore.Key
+ if err := json.Unmarshal([]byte(infos.keyJSON), &key); err != nil {
+ infos.keyJSON, infos.keyPass = "", ""
+ } else {
+ fmt.Println()
+ fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex())
+ if w.readDefaultString("y") != "y" {
+ infos.keyJSON, infos.keyPass = "", ""
+ }
+ }
+ }
+ // Clique based signers need a keyfile and unlock password, ask if unavailable
+ if infos.keyJSON == "" {
+ fmt.Println()
+ fmt.Println("Please paste the signer's key JSON:")
+ infos.keyJSON = w.readJSON()
+
+ fmt.Println()
+ fmt.Println("What's the unlock password for the account? (won't be echoed)")
+ infos.keyPass = w.readPassword()
+
+ if _, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil {
+ log.Error("Failed to decrypt key with given passphrase")
+ return
+ }
+ }
+ }
+ }
+ // Try to deploy the full node on the host
+ if out, err := deployNode(client, w.network, w.conf.bootFull, infos); err != nil {
+ log.Error("Failed to deploy Ethereum node container", "err", err)
+ if len(out) > 0 {
+ fmt.Printf("%s\n", out)
+ }
+ return
+ }
+ // All ok, run a network scan to pick any changes up
+ log.Info("Waiting for node to finish booting")
+ time.Sleep(3 * time.Second)
+
+ w.networkStats(false)
+}
diff --git a/cmd/rlpdump/main.go b/cmd/rlpdump/main.go
index 10eea1fde..7d328e59b 100644
--- a/cmd/rlpdump/main.go
+++ b/cmd/rlpdump/main.go
@@ -32,6 +32,7 @@ import (
var (
hexMode = flag.String("hex", "", "dump given hex data")
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
+ single = flag.Bool("single", false, "print only the first element, discard the rest")
)
func init() {
@@ -82,6 +83,9 @@ func main() {
break
}
fmt.Println()
+ if *single {
+ break
+ }
}
}
diff --git a/cmd/swarm/cleandb.go b/cmd/swarm/cleandb.go
index 29d01ba0f..268076062 100644
--- a/cmd/swarm/cleandb.go
+++ b/cmd/swarm/cleandb.go
@@ -1,18 +1,18 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
-// The go-ethereum library is distributed in the hope that it will be useful,
+// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
+// GNU General Public License for more details.
//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
diff --git a/cmd/swarm/list.go b/cmd/swarm/list.go
index 06d3883cf..57b5517c6 100644
--- a/cmd/swarm/list.go
+++ b/cmd/swarm/list.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go
index 47ed662d4..26aa3e50f 100644
--- a/cmd/swarm/main.go
+++ b/cmd/swarm/main.go
@@ -39,19 +39,16 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/swarm"
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
"gopkg.in/urfave/cli.v1"
)
-const (
- clientIdentifier = "swarm"
- versionString = "0.2"
-)
+const clientIdentifier = "swarm"
var (
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
- app = utils.NewApp(gitCommit, "Ethereum Swarm")
testbetBootNodes = []string{
"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
@@ -126,13 +123,22 @@ var (
}
)
+var defaultNodeConfig = node.DefaultConfig
+
+// This init function sets defaults so cmd/swarm can run alongside geth.
func init() {
- // Override flag defaults so bzzd can run alongside geth.
+ defaultNodeConfig.Name = clientIdentifier
+ defaultNodeConfig.Version = params.VersionWithCommit(gitCommit)
+ defaultNodeConfig.P2P.ListenAddr = ":30399"
+ defaultNodeConfig.IPCPath = "bzzd.ipc"
+ // Set flag defaults for --help display.
utils.ListenPortFlag.Value = 30399
- utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
- utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, swarmfs, web3"
+}
+
+var app = utils.NewApp(gitCommit, "Ethereum Swarm")
- // Set up the cli app.
+// This init function creates the cli.App.
+func init() {
app.Action = bzzd
app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
@@ -235,8 +241,8 @@ Cleans database of corrupted entries.
utils.MaxPeersFlag,
utils.NATFlag,
utils.IPCDisabledFlag,
- utils.IPCApiFlag,
utils.IPCPathFlag,
+ utils.PasswordFileFlag,
// bzzd-specific flags
CorsStringFlag,
EthAPIFlag,
@@ -275,7 +281,7 @@ func main() {
func version(ctx *cli.Context) error {
fmt.Println(strings.Title(clientIdentifier))
- fmt.Println("Version:", versionString)
+ fmt.Println("Version:", params.Version)
if gitCommit != "" {
fmt.Println("Git Commit:", gitCommit)
}
@@ -288,9 +294,16 @@ func version(ctx *cli.Context) error {
}
func bzzd(ctx *cli.Context) error {
- stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
+ cfg := defaultNodeConfig
+ utils.SetNodeConfig(ctx, &cfg)
+ stack, err := node.New(&cfg)
+ if err != nil {
+ utils.Fatalf("can't create node: %v", err)
+ }
+
registerBzzService(ctx, stack)
utils.StartNode(stack)
+
go func() {
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGTERM)
@@ -299,6 +312,7 @@ func bzzd(ctx *cli.Context) error {
log.Info("Got sigterm, shutting swarm down...")
stack.Stop()
}()
+
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
// Add bootnodes as initial peers.
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
@@ -315,7 +329,6 @@ func bzzd(ctx *cli.Context) error {
}
func registerBzzService(ctx *cli.Context, stack *node.Node) {
-
prvkey := getAccount(ctx, stack)
chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
@@ -345,6 +358,8 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
if err != nil {
utils.Fatalf("Can't connect: %v", err)
}
+ } else {
+ swapEnabled = false
}
return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors)
}
@@ -368,10 +383,10 @@ func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
am := stack.AccountManager()
ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
- return decryptStoreAccount(ks, keyid)
+ return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx))
}
-func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKey {
+func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
var a accounts.Account
var err error
if common.IsHexAddress(account) {
@@ -392,9 +407,9 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKe
if err != nil {
utils.Fatalf("Can't load swarm account key: %v", err)
}
- for i := 1; i <= 3; i++ {
- passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i))
- key, err := keystore.DecryptKey(keyjson, passphrase)
+ for i := 0; i < 3; i++ {
+ password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
+ key, err := keystore.DecryptKey(keyjson, password)
if err == nil {
return key.PrivateKey
}
@@ -403,7 +418,18 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKe
return nil
}
-func promptPassphrase(prompt string) string {
+// getPassPhrase retrieves the password associated with bzz account, either by fetching
+// from a list of pre-loaded passwords, or by requesting it interactively from user.
+func getPassPhrase(prompt string, i int, passwords []string) string {
+ // non-interactive
+ if len(passwords) > 0 {
+ if i < len(passwords) {
+ return passwords[i]
+ }
+ return passwords[len(passwords)-1]
+ }
+
+ // fallback to interactive mode
if prompt != "" {
fmt.Println(prompt)
}
diff --git a/cmd/swarm/manifest.go b/cmd/swarm/manifest.go
index 9729022c0..7c4d6052c 100644
--- a/cmd/swarm/manifest.go
+++ b/cmd/swarm/manifest.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
diff --git a/cmd/utils/customflags.go b/cmd/utils/customflags.go
index 00f28f2ec..e5bf8724c 100644
--- a/cmd/utils/customflags.go
+++ b/cmd/utils/customflags.go
@@ -17,6 +17,7 @@
package utils
import (
+ "encoding"
"errors"
"flag"
"fmt"
@@ -78,6 +79,58 @@ func (self DirectoryFlag) Apply(set *flag.FlagSet) {
})
}
+type TextMarshaler interface {
+ encoding.TextMarshaler
+ encoding.TextUnmarshaler
+}
+
+// textMarshalerVal turns a TextMarshaler into a flag.Value
+type textMarshalerVal struct {
+ v TextMarshaler
+}
+
+func (v textMarshalerVal) String() string {
+ if v.v == nil {
+ return ""
+ }
+ text, _ := v.v.MarshalText()
+ return string(text)
+}
+
+func (v textMarshalerVal) Set(s string) error {
+ return v.v.UnmarshalText([]byte(s))
+}
+
+// TextMarshalerFlag wraps a TextMarshaler value.
+type TextMarshalerFlag struct {
+ Name string
+ Value TextMarshaler
+ Usage string
+}
+
+func (f TextMarshalerFlag) GetName() string {
+ return f.Name
+}
+
+func (f TextMarshalerFlag) String() string {
+ return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)
+}
+
+func (f TextMarshalerFlag) Apply(set *flag.FlagSet) {
+ eachName(f.Name, func(name string) {
+ set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
+ })
+}
+
+// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
+func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
+ val := ctx.GlobalGeneric(name)
+ if val == nil {
+ return nil
+ }
+ return val.(textMarshalerVal).v
+}
+
// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
// hexadecimal syntax.
type BigFlag struct {
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 0ca407a75..b35574c86 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -23,7 +23,6 @@ import (
"io/ioutil"
"math/big"
"os"
- "os/user"
"path/filepath"
"runtime"
"strconv"
@@ -38,6 +37,8 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethstats"
"github.com/ethereum/go-ethereum/event"
@@ -45,12 +46,12 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
"gopkg.in/urfave/cli.v1"
)
@@ -121,31 +122,32 @@ var (
EthashCachesInMemoryFlag = cli.IntFlag{
Name: "ethash.cachesinmem",
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
- Value: 2,
+ Value: eth.DefaultConfig.EthashCachesInMem,
}
EthashCachesOnDiskFlag = cli.IntFlag{
Name: "ethash.cachesondisk",
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
- Value: 3,
+ Value: eth.DefaultConfig.EthashCachesOnDisk,
}
EthashDatasetDirFlag = DirectoryFlag{
Name: "ethash.dagdir",
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
+ Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir},
}
EthashDatasetsInMemoryFlag = cli.IntFlag{
Name: "ethash.dagsinmem",
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
- Value: 1,
+ Value: eth.DefaultConfig.EthashDatasetsInMem,
}
EthashDatasetsOnDiskFlag = cli.IntFlag{
Name: "ethash.dagsondisk",
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
- Value: 2,
+ Value: eth.DefaultConfig.EthashDatasetsOnDisk,
}
NetworkIdFlag = cli.IntFlag{
Name: "networkid",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
- Value: eth.NetworkId,
+ Value: eth.DefaultConfig.NetworkId,
}
TestNetFlag = cli.BoolFlag{
Name: "testnet",
@@ -172,6 +174,13 @@ var (
Name: "light",
Usage: "Enable light client mode",
}
+ defaultSyncMode = eth.DefaultConfig.SyncMode
+ SyncModeFlag = TextMarshalerFlag{
+ Name: "syncmode",
+ Usage: `Blockchain sync mode ("fast", "full", or "light")`,
+ Value: &defaultSyncMode,
+ }
+
LightServFlag = cli.IntFlag{
Name: "lightserv",
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
@@ -238,19 +247,6 @@ var (
Value: "",
}
- VMForceJitFlag = cli.BoolFlag{
- Name: "forcejit",
- Usage: "Force the JIT VM to take precedence",
- }
- VMJitCacheFlag = cli.IntFlag{
- Name: "jitcache",
- Usage: "Amount of cached JIT VM programs",
- Value: 64,
- }
- VMEnableJitFlag = cli.BoolFlag{
- Name: "jitvm",
- Usage: "Enable the JIT VM",
- }
VMEnableDebugFlag = cli.BoolFlag{
Name: "vmdebug",
Usage: "Record information useful for VM and contract debugging",
@@ -295,21 +291,15 @@ var (
RPCApiFlag = cli.StringFlag{
Name: "rpcapi",
Usage: "API's offered over the HTTP-RPC interface",
- Value: rpc.DefaultHTTPApis,
+ Value: "",
}
IPCDisabledFlag = cli.BoolFlag{
Name: "ipcdisable",
Usage: "Disable the IPC-RPC server",
}
- IPCApiFlag = cli.StringFlag{
- Name: "ipcapi",
- Usage: "APIs offered over the IPC-RPC interface",
- Value: rpc.DefaultIPCApis,
- }
IPCPathFlag = DirectoryFlag{
Name: "ipcpath",
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
- Value: DirectoryString{"geth.ipc"},
}
WSEnabledFlag = cli.BoolFlag{
Name: "ws",
@@ -328,7 +318,7 @@ var (
WSApiFlag = cli.StringFlag{
Name: "wsapi",
Usage: "API's offered over the WS-RPC interface",
- Value: rpc.DefaultHTTPApis,
+ Value: "",
}
WSAllowedOriginsFlag = cli.StringFlag{
Name: "wsorigins",
@@ -402,22 +392,17 @@ var (
Usage: "JavaScript root path for `loadScript`",
Value: ".",
}
- SolcPathFlag = cli.StringFlag{
- Name: "solc",
- Usage: "Solidity compiler command to be used",
- Value: "solc",
- }
// Gas price oracle settings
GpoBlocksFlag = cli.IntFlag{
Name: "gpoblocks",
Usage: "Number of recent blocks to check for gas prices",
- Value: 10,
+ Value: eth.DefaultConfig.GPO.Blocks,
}
GpoPercentileFlag = cli.IntFlag{
Name: "gpopercentile",
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
- Value: 50,
+ Value: eth.DefaultConfig.GPO.Percentile,
}
)
@@ -436,88 +421,42 @@ func MakeDataDir(ctx *cli.Context) string {
return ""
}
-// MakeEthashCacheDir returns the directory to use for storing the ethash cache
-// dumps.
-func MakeEthashCacheDir(ctx *cli.Context) string {
- if ctx.GlobalIsSet(EthashCacheDirFlag.Name) && ctx.GlobalString(EthashCacheDirFlag.Name) == "" {
- return ""
- }
- if !ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
- return "ethash"
- }
- return ctx.GlobalString(EthashCacheDirFlag.Name)
-}
-
-// MakeEthashDatasetDir returns the directory to use for storing the full ethash
-// dataset dumps.
-func MakeEthashDatasetDir(ctx *cli.Context) string {
- if !ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
- home := os.Getenv("HOME")
- if home == "" {
- if user, err := user.Current(); err == nil {
- home = user.HomeDir
- }
- }
- if runtime.GOOS == "windows" {
- return filepath.Join(home, "AppData", "Ethash")
- }
- return filepath.Join(home, ".ethash")
- }
- return ctx.GlobalString(EthashDatasetDirFlag.Name)
-}
-
-// MakeIPCPath creates an IPC path configuration from the set command line flags,
-// returning an empty string if IPC was explicitly disabled, or the set path.
-func MakeIPCPath(ctx *cli.Context) string {
- if ctx.GlobalBool(IPCDisabledFlag.Name) {
- return ""
- }
- return ctx.GlobalString(IPCPathFlag.Name)
-}
-
-// MakeNodeKey creates a node key from set command line flags, either loading it
+// setNodeKey creates a node key from set command line flags, either loading it
// from a file or as a specified hex value. If neither flags were provided, this
// method returns nil and an emphemeral key is to be generated.
-func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey {
+func setNodeKey(ctx *cli.Context, cfg *p2p.Config) {
var (
hex = ctx.GlobalString(NodeKeyHexFlag.Name)
file = ctx.GlobalString(NodeKeyFileFlag.Name)
-
- key *ecdsa.PrivateKey
- err error
+ key *ecdsa.PrivateKey
+ err error
)
switch {
case file != "" && hex != "":
Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
-
case file != "":
if key, err = crypto.LoadECDSA(file); err != nil {
Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
}
-
+ cfg.PrivateKey = key
case hex != "":
if key, err = crypto.HexToECDSA(hex); err != nil {
Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
}
+ cfg.PrivateKey = key
}
- return key
}
-// makeNodeUserIdent creates the user identifier from CLI flags.
-func makeNodeUserIdent(ctx *cli.Context) string {
- var comps []string
+// setNodeUserIdent creates the user identifier from CLI flags.
+func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
- comps = append(comps, identity)
- }
- if ctx.GlobalBool(VMEnableJitFlag.Name) {
- comps = append(comps, "JIT")
+ cfg.UserIdent = identity
}
- return strings.Join(comps, "/")
}
-// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
+// setBootstrapNodes creates a list of bootstrap nodes from the command line
// flags, reverting to pre-configured ones if none have been specified.
-func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
+func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls := params.MainnetBootnodes
if ctx.GlobalIsSet(BootnodesFlag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
@@ -525,62 +464,68 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
urls = params.TestnetBootnodes
}
- bootnodes := make([]*discover.Node, 0, len(urls))
+ cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
for _, url := range urls {
node, err := discover.ParseNode(url)
if err != nil {
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
continue
}
- bootnodes = append(bootnodes, node)
+ cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
}
- return bootnodes
}
-// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line
+// setBootstrapNodesV5 creates a list of bootstrap nodes from the command line
// flags, reverting to pre-configured ones if none have been specified.
-func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node {
+func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
urls := params.DiscoveryV5Bootnodes
if ctx.GlobalIsSet(BootnodesFlag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
+ } else if cfg.BootstrapNodesV5 == nil {
+ return // already set, don't apply defaults.
}
- bootnodes := make([]*discv5.Node, 0, len(urls))
+ cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls))
for _, url := range urls {
node, err := discv5.ParseNode(url)
if err != nil {
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
continue
}
- bootnodes = append(bootnodes, node)
+ cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node)
}
- return bootnodes
}
-// MakeListenAddress creates a TCP listening address string from set command
+// setListenAddress creates a TCP listening address string from set command
// line flags.
-func MakeListenAddress(ctx *cli.Context) string {
- return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
+func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
+ if ctx.GlobalIsSet(ListenPortFlag.Name) {
+ cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
+ }
}
-// MakeDiscoveryV5Address creates a UDP listening address string from set command
+// setDiscoveryV5Address creates a UDP listening address string from set command
// line flags for the V5 discovery protocol.
-func MakeDiscoveryV5Address(ctx *cli.Context) string {
- return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
+func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) {
+ if ctx.GlobalIsSet(ListenPortFlag.Name) {
+ cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
+ }
}
-// MakeNAT creates a port mapper from set command line flags.
-func MakeNAT(ctx *cli.Context) nat.Interface {
- natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
- if err != nil {
- Fatalf("Option %s: %v", NATFlag.Name, err)
+// setNAT creates a port mapper from command line flags.
+func setNAT(ctx *cli.Context, cfg *p2p.Config) {
+ if ctx.GlobalIsSet(NATFlag.Name) {
+ natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
+ if err != nil {
+ Fatalf("Option %s: %v", NATFlag.Name, err)
+ }
+ cfg.NAT = natif
}
- return natif
}
-// MakeRPCModules splits input separated by a comma and trims excessive white
-// space from the substrings.
-func MakeRPCModules(input string) []string {
+// splitAndTrim splits input separated by a comma
+// and trims excessive white space from the substrings.
+func splitAndTrim(input string) []string {
result := strings.Split(input, ",")
for i, r := range result {
result[i] = strings.TrimSpace(r)
@@ -588,27 +533,63 @@ func MakeRPCModules(input string) []string {
return result
}
-// MakeHTTPRpcHost creates the HTTP RPC listener interface string from the set
+// setHTTP creates the HTTP RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled.
-func MakeHTTPRpcHost(ctx *cli.Context) string {
- if !ctx.GlobalBool(RPCEnabledFlag.Name) {
- return ""
+func setHTTP(ctx *cli.Context, cfg *node.Config) {
+ if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" {
+ cfg.HTTPHost = "127.0.0.1"
+ if ctx.GlobalIsSet(RPCListenAddrFlag.Name) {
+ cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
+ }
+ }
+
+ if ctx.GlobalIsSet(RPCPortFlag.Name) {
+ cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
+ }
+ if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) {
+ cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name))
+ }
+ if ctx.GlobalIsSet(RPCApiFlag.Name) {
+ cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
}
- return ctx.GlobalString(RPCListenAddrFlag.Name)
}
-// MakeWSRpcHost creates the WebSocket RPC listener interface string from the set
+// setWS creates the WebSocket RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled.
-func MakeWSRpcHost(ctx *cli.Context) string {
- if !ctx.GlobalBool(WSEnabledFlag.Name) {
- return ""
+func setWS(ctx *cli.Context, cfg *node.Config) {
+ if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" {
+ cfg.WSHost = "127.0.0.1"
+ if ctx.GlobalIsSet(WSListenAddrFlag.Name) {
+ cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
+ }
+ }
+
+ if ctx.GlobalIsSet(WSPortFlag.Name) {
+ cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
+ }
+ if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) {
+ cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name))
+ }
+ if ctx.GlobalIsSet(WSApiFlag.Name) {
+ cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name))
+ }
+}
+
+// setIPC creates an IPC path configuration from the set command line flags,
+// returning an empty string if IPC was explicitly disabled, or the set path.
+func setIPC(ctx *cli.Context, cfg *node.Config) {
+ checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
+ switch {
+ case ctx.GlobalBool(IPCDisabledFlag.Name):
+ cfg.IPCPath = ""
+ case ctx.GlobalIsSet(IPCPathFlag.Name):
+ cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name)
}
- return ctx.GlobalString(WSListenAddrFlag.Name)
}
-// MakeDatabaseHandles raises out the number of allowed file handles per process
+// makeDatabaseHandles raises out the number of allowed file handles per process
// for Geth and returns half of the allowance to assign to the database.
-func MakeDatabaseHandles() int {
+func makeDatabaseHandles() int {
if err := raiseFdLimit(2048); err != nil {
Fatalf("Failed to raise file descriptor allowance: %v", err)
}
@@ -641,33 +622,25 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
return accs[index], nil
}
-// MakeEtherbase retrieves the etherbase either from the directly specified
+// setEtherbase retrieves the etherbase either from the directly specified
// command line flags or from the keystore if CLI indexed.
-func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address {
- accounts := ks.Accounts()
- if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 {
- log.Warn("No etherbase set and no accounts found as default")
- return common.Address{}
- }
- etherbase := ctx.GlobalString(EtherbaseFlag.Name)
- if etherbase == "" {
- return common.Address{}
- }
- // If the specified etherbase is a valid address, return it
- account, err := MakeAddress(ks, etherbase)
- if err != nil {
- Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
+func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
+ if ctx.GlobalIsSet(EtherbaseFlag.Name) {
+ account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name))
+ if err != nil {
+ Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
+ }
+ cfg.Etherbase = account.Address
+ return
}
- return account.Address
-}
-
-// MakeMinerExtra resolves extradata for the miner from the set command line flags
-// or returns a default one composed on the client, runtime and OS metadata.
-func MakeMinerExtra(extra []byte, ctx *cli.Context) []byte {
- if ctx.GlobalIsSet(ExtraDataFlag.Name) {
- return []byte(ctx.GlobalString(ExtraDataFlag.Name))
+ accounts := ks.Accounts()
+ if (cfg.Etherbase == common.Address{}) {
+ if len(accounts) > 0 {
+ cfg.Etherbase = accounts[0].Address
+ } else {
+ log.Warn("No etherbase set and no accounts found as default")
+ }
}
- return extra
}
// MakePasswordList reads password lines from the file specified by --password.
@@ -688,144 +661,213 @@ func MakePasswordList(ctx *cli.Context) []string {
return lines
}
-// MakeNode configures a node with no services from command line flags.
-func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
- vsn := params.Version
- if gitCommit != "" {
- vsn += "-" + gitCommit[:8]
+func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
+ setNodeKey(ctx, cfg)
+ setNAT(ctx, cfg)
+ setListenAddress(ctx, cfg)
+ setDiscoveryV5Address(ctx, cfg)
+ setBootstrapNodes(ctx, cfg)
+ setBootstrapNodesV5(ctx, cfg)
+
+ if ctx.GlobalIsSet(MaxPeersFlag.Name) {
+ cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
+ }
+ if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) {
+ cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name)
+ }
+ if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) {
+ cfg.NoDiscovery = true
}
- // if we're running a light client or server, force enable the v5 peer discovery unless it is explicitly disabled with --nodiscover
- // note that explicitly specifying --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
+ // if we're running a light client or server, force enable the v5 peer discovery
+ // unless it is explicitly disabled with --nodiscover note that explicitly specifying
+ // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name)
-
- config := &node.Config{
- DataDir: MakeDataDir(ctx),
- KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name),
- UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name),
- PrivateKey: MakeNodeKey(ctx),
- Name: name,
- Version: vsn,
- UserIdent: makeNodeUserIdent(ctx),
- NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // always disable v4 discovery in light client mode
- DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || forceV5Discovery,
- DiscoveryV5Addr: MakeDiscoveryV5Address(ctx),
- BootstrapNodes: MakeBootstrapNodes(ctx),
- BootstrapNodesV5: MakeBootstrapNodesV5(ctx),
- ListenAddr: MakeListenAddress(ctx),
- NAT: MakeNAT(ctx),
- MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
- MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
- IPCPath: MakeIPCPath(ctx),
- HTTPHost: MakeHTTPRpcHost(ctx),
- HTTPPort: ctx.GlobalInt(RPCPortFlag.Name),
- HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name),
- HTTPModules: MakeRPCModules(ctx.GlobalString(RPCApiFlag.Name)),
- WSHost: MakeWSRpcHost(ctx),
- WSPort: ctx.GlobalInt(WSPortFlag.Name),
- WSOrigins: ctx.GlobalString(WSAllowedOriginsFlag.Name),
- WSModules: MakeRPCModules(ctx.GlobalString(WSApiFlag.Name)),
- }
- if ctx.GlobalBool(DevModeFlag.Name) {
- if !ctx.GlobalIsSet(DataDirFlag.Name) {
- config.DataDir = filepath.Join(os.TempDir(), "/ethereum_dev_mode")
- }
- // --dev mode does not need p2p networking.
- config.MaxPeers = 0
- config.ListenAddr = ":0"
+ if ctx.GlobalIsSet(DiscoveryV5Flag.Name) {
+ cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name)
+ } else if forceV5Discovery {
+ cfg.DiscoveryV5 = true
}
+
if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" {
list, err := netutil.ParseNetlist(netrestrict)
if err != nil {
Fatalf("Option %q: %v", NetrestrictFlag.Name, err)
}
- config.NetRestrict = list
+ cfg.NetRestrict = list
}
- stack, err := node.New(config)
- if err != nil {
- Fatalf("Failed to create the protocol stack: %v", err)
+ if ctx.GlobalBool(DevModeFlag.Name) {
+ // --dev mode can't use p2p networking.
+ cfg.MaxPeers = 0
+ cfg.ListenAddr = ":0"
+ cfg.NoDiscovery = true
+ cfg.DiscoveryV5 = false
}
- return stack
}
-// RegisterEthService configures eth.Ethereum from command line flags and adds it to the
-// given node.
-func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
- // Avoid conflicting network flags
- networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag}
- for _, flag := range netFlags {
- if ctx.GlobalBool(flag.Name) {
- networks++
+// SetNodeConfig applies node-related command line flags to the config.
+func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
+ SetP2PConfig(ctx, &cfg.P2P)
+ setIPC(ctx, cfg)
+ setHTTP(ctx, cfg)
+ setWS(ctx, cfg)
+ setNodeUserIdent(ctx, cfg)
+
+ switch {
+ case ctx.GlobalIsSet(DataDirFlag.Name):
+ cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
+ case ctx.GlobalBool(DevModeFlag.Name):
+ cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode")
+ case ctx.GlobalBool(TestNetFlag.Name):
+ cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
+ }
+
+ if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
+ cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name)
+ }
+ if ctx.GlobalIsSet(LightKDFFlag.Name) {
+ cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name)
+ }
+}
+
+func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
+ if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
+ cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
+ }
+ if ctx.GlobalIsSet(GpoPercentileFlag.Name) {
+ cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name)
+ }
+}
+
+func setEthash(ctx *cli.Context, cfg *eth.Config) {
+ if ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
+ cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name)
+ }
+ if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
+ cfg.EthashDatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name)
+ }
+ if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) {
+ cfg.EthashCachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name)
+ }
+ if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) {
+ cfg.EthashCachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name)
+ }
+ if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) {
+ cfg.EthashDatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name)
+ }
+ if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) {
+ cfg.EthashDatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name)
+ }
+}
+
+func checkExclusive(ctx *cli.Context, flags ...cli.Flag) {
+ set := make([]string, 0, 1)
+ for _, flag := range flags {
+ if ctx.GlobalIsSet(flag.GetName()) {
+ set = append(set, "--"+flag.GetName())
}
}
- if networks > 1 {
- Fatalf("The %v flags are mutually exclusive", netFlags)
+ if len(set) > 1 {
+ Fatalf("flags %v can't be used at the same time", strings.Join(set, ", "))
}
+}
+
+// SetEthConfig applies eth-related command line flags to the config.
+func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
+ // Avoid conflicting network flags
+ checkExclusive(ctx, DevModeFlag, TestNetFlag)
+ checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag)
+
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ setEtherbase(ctx, ks, cfg)
+ setGPO(ctx, &cfg.GPO)
+ setEthash(ctx, cfg)
- ethConf := &eth.Config{
- Etherbase: MakeEtherbase(ks, ctx),
- FastSync: ctx.GlobalBool(FastSyncFlag.Name),
- LightMode: ctx.GlobalBool(LightModeFlag.Name),
- LightServ: ctx.GlobalInt(LightServFlag.Name),
- LightPeers: ctx.GlobalInt(LightPeersFlag.Name),
- MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
- DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
- DatabaseHandles: MakeDatabaseHandles(),
- NetworkId: ctx.GlobalInt(NetworkIdFlag.Name),
- MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
- ExtraData: MakeMinerExtra(extra, ctx),
- DocRoot: ctx.GlobalString(DocRootFlag.Name),
- GasPrice: GlobalBig(ctx, GasPriceFlag.Name),
- GpoBlocks: ctx.GlobalInt(GpoBlocksFlag.Name),
- GpoPercentile: ctx.GlobalInt(GpoPercentileFlag.Name),
- SolcPath: ctx.GlobalString(SolcPathFlag.Name),
- EthashCacheDir: MakeEthashCacheDir(ctx),
- EthashCachesInMem: ctx.GlobalInt(EthashCachesInMemoryFlag.Name),
- EthashCachesOnDisk: ctx.GlobalInt(EthashCachesOnDiskFlag.Name),
- EthashDatasetDir: MakeEthashDatasetDir(ctx),
- EthashDatasetsInMem: ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name),
- EthashDatasetsOnDisk: ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name),
- EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name),
- }
-
- // Override any default configs in dev mode or the test net
+ switch {
+ case ctx.GlobalIsSet(SyncModeFlag.Name):
+ cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
+ case ctx.GlobalBool(FastSyncFlag.Name):
+ cfg.SyncMode = downloader.FastSync
+ case ctx.GlobalBool(LightModeFlag.Name):
+ cfg.SyncMode = downloader.LightSync
+ }
+ if ctx.GlobalIsSet(LightServFlag.Name) {
+ cfg.LightServ = ctx.GlobalInt(LightServFlag.Name)
+ }
+ if ctx.GlobalIsSet(LightPeersFlag.Name) {
+ cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
+ }
+ if ctx.GlobalIsSet(NetworkIdFlag.Name) {
+ cfg.NetworkId = ctx.GlobalInt(NetworkIdFlag.Name)
+ }
+
+ // Ethereum needs to know maxPeers to calculate the light server peer ratio.
+ // TODO(fjl): ensure Ethereum can get MaxPeers from node.
+ cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
+
+ if ctx.GlobalIsSet(CacheFlag.Name) {
+ cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name)
+ }
+ cfg.DatabaseHandles = makeDatabaseHandles()
+
+ if ctx.GlobalIsSet(MinerThreadsFlag.Name) {
+ cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name)
+ }
+ if ctx.GlobalIsSet(DocRootFlag.Name) {
+ cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name)
+ }
+ if ctx.GlobalIsSet(ExtraDataFlag.Name) {
+ cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name))
+ }
+ if ctx.GlobalIsSet(GasPriceFlag.Name) {
+ cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name)
+ }
+ if ctx.GlobalIsSet(VMEnableDebugFlag.Name) {
+ // TODO(fjl): force-enable this in --dev mode
+ cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
+ }
+
+ // Override any default configs for --dev and --testnet.
switch {
case ctx.GlobalBool(TestNetFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
- ethConf.NetworkId = 3
+ cfg.NetworkId = 3
}
- ethConf.Genesis = core.DefaultTestnetGenesisBlock()
+ cfg.Genesis = core.DefaultTestnetGenesisBlock()
case ctx.GlobalBool(DevModeFlag.Name):
- ethConf.Genesis = core.DevGenesisBlock()
+ cfg.Genesis = core.DevGenesisBlock()
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
- ethConf.GasPrice = new(big.Int)
+ cfg.GasPrice = new(big.Int)
}
- ethConf.PowTest = true
+ cfg.PowTest = true
}
- // Override any global options pertaining to the Ethereum protocol
+
+ // TODO(fjl): move trie cache generations into config
if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 {
state.MaxTrieCacheGen = uint16(gen)
}
+}
- if ethConf.LightMode {
- if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- return les.New(ctx, ethConf)
- }); err != nil {
- Fatalf("Failed to register the Ethereum light node service: %v", err)
- }
+// RegisterEthService adds an Ethereum client to the stack.
+func RegisterEthService(stack *node.Node, cfg *eth.Config) {
+ var err error
+ if cfg.SyncMode == downloader.LightSync {
+ err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
+ return les.New(ctx, cfg)
+ })
} else {
- if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- fullNode, err := eth.New(ctx, ethConf)
- if fullNode != nil && ethConf.LightServ > 0 {
- ls, _ := les.NewLesServer(fullNode, ethConf)
+ err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
+ fullNode, err := eth.New(ctx, cfg)
+ if fullNode != nil && cfg.LightServ > 0 {
+ ls, _ := les.NewLesServer(fullNode, cfg)
fullNode.AddLesServer(ls)
}
return fullNode, err
- }); err != nil {
- Fatalf("Failed to register the Ethereum full node service: %v", err)
- }
+ })
+ }
+ if err != nil {
+ Fatalf("Failed to register the Ethereum service: %v", err)
}
}
@@ -855,6 +897,7 @@ func RegisterEthStatsService(stack *node.Node, url string) {
// SetupNetwork configures the system for either the main net or some test network.
func SetupNetwork(ctx *cli.Context) {
+ // TODO(fjl): move target gas limit into config
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
}
@@ -870,7 +913,7 @@ func ChainDbName(ctx *cli.Context) string {
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var (
cache = ctx.GlobalInt(CacheFlag.Name)
- handles = MakeDatabaseHandles()
+ handles = makeDatabaseHandles()
name = ChainDbName(ctx)
)
diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go
index 7431980b5..23b180487 100644
--- a/cmd/wnode/main.go
+++ b/cmd/wnode/main.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
@@ -257,7 +257,6 @@ func initialize() {
Config: p2p.Config{
PrivateKey: nodeid,
MaxPeers: maxPeers,
- Discovery: true,
Name: common.MakeName("wnode", "5.0"),
Protocols: shh.Protocols(),
ListenAddr: *argIP,
diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go
index d27bddd9f..abb803989 100644
--- a/common/compiler/solidity.go
+++ b/common/compiler/solidity.go
@@ -25,20 +25,11 @@ import (
"io/ioutil"
"os/exec"
"regexp"
+ "strconv"
"strings"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
)
-var (
- versionRegexp = regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`)
- solcParams = []string{
- "--combined-json", "bin,abi,userdoc,devdoc",
- "--add-std", // include standard lib contracts
- "--optimize", // code optimizer switched on
- }
-)
+var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`)
type Contract struct {
Code string `json:"code"`
@@ -54,17 +45,33 @@ type ContractInfo struct {
AbiDefinition interface{} `json:"abiDefinition"`
UserDoc interface{} `json:"userDoc"`
DeveloperDoc interface{} `json:"developerDoc"`
+ Metadata string `json:"metadata"`
}
// Solidity contains information about the solidity compiler.
type Solidity struct {
Path, Version, FullVersion string
+ Major, Minor, Patch int
}
// --combined-output format
type solcOutput struct {
- Contracts map[string]struct{ Bin, Abi, Devdoc, Userdoc string }
- Version string
+ Contracts map[string]struct {
+ Bin, Abi, Devdoc, Userdoc, Metadata string
+ }
+ Version string
+}
+
+func (s *Solidity) makeArgs() []string {
+ p := []string{
+ "--combined-json", "bin,abi,userdoc,devdoc",
+ "--add-std", // include standard lib contracts
+ "--optimize", // code optimizer switched on
+ }
+ if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {
+ p[1] += ",metadata"
+ }
+ return p
}
// SolidityVersion runs solc and parses its version output.
@@ -75,13 +82,23 @@ func SolidityVersion(solc string) (*Solidity, error) {
var out bytes.Buffer
cmd := exec.Command(solc, "--version")
cmd.Stdout = &out
- if err := cmd.Run(); err != nil {
+ err := cmd.Run()
+ if err != nil {
+ return nil, err
+ }
+ matches := versionRegexp.FindStringSubmatch(out.String())
+ if len(matches) != 4 {
+ return nil, fmt.Errorf("can't parse solc version %q", out.String())
+ }
+ s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]}
+ if s.Major, err = strconv.Atoi(matches[1]); err != nil {
return nil, err
}
- s := &Solidity{
- Path: cmd.Path,
- FullVersion: out.String(),
- Version: versionRegexp.FindString(out.String()),
+ if s.Minor, err = strconv.Atoi(matches[2]); err != nil {
+ return nil, err
+ }
+ if s.Patch, err = strconv.Atoi(matches[3]); err != nil {
+ return nil, err
}
return s, nil
}
@@ -91,13 +108,14 @@ func CompileSolidityString(solc, source string) (map[string]*Contract, error) {
if len(source) == 0 {
return nil, errors.New("solc: empty source string")
}
- if solc == "" {
- solc = "solc"
+ s, err := SolidityVersion(solc)
+ if err != nil {
+ return nil, err
}
- args := append(solcParams, "--")
- cmd := exec.Command(solc, append(args, "-")...)
+ args := append(s.makeArgs(), "--")
+ cmd := exec.Command(s.Path, append(args, "-")...)
cmd.Stdin = strings.NewReader(source)
- return runsolc(cmd, source)
+ return s.run(cmd, source)
}
// CompileSolidity compiles all given Solidity source files.
@@ -109,15 +127,16 @@ func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract,
if err != nil {
return nil, err
}
- if solc == "" {
- solc = "solc"
+ s, err := SolidityVersion(solc)
+ if err != nil {
+ return nil, err
}
- args := append(solcParams, "--")
- cmd := exec.Command(solc, append(args, sourcefiles...)...)
- return runsolc(cmd, source)
+ args := append(s.makeArgs(), "--")
+ cmd := exec.Command(s.Path, append(args, sourcefiles...)...)
+ return s.run(cmd, source)
}
-func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
+func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
var stderr, stdout bytes.Buffer
cmd.Stderr = &stderr
cmd.Stdout = &stdout
@@ -128,7 +147,6 @@ func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
if err := json.Unmarshal(stdout.Bytes(), &output); err != nil {
return nil, err
}
- shortVersion := versionRegexp.FindString(output.Version)
// Compilation succeeded, assemble and return the contracts.
contracts := make(map[string]*Contract)
@@ -151,12 +169,13 @@ func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
Info: ContractInfo{
Source: source,
Language: "Solidity",
- LanguageVersion: shortVersion,
- CompilerVersion: shortVersion,
- CompilerOptions: strings.Join(solcParams, " "),
+ LanguageVersion: s.Version,
+ CompilerVersion: s.Version,
+ CompilerOptions: strings.Join(s.makeArgs(), " "),
AbiDefinition: abi,
UserDoc: userdoc,
DeveloperDoc: devdoc,
+ Metadata: info.Metadata,
},
}
}
@@ -174,13 +193,3 @@ func slurpFiles(files []string) (string, error) {
}
return concat.String(), nil
}
-
-// SaveInfo serializes info to the given file and returns its Keccak256 hash.
-func SaveInfo(info *ContractInfo, filename string) (common.Hash, error) {
- infojson, err := json.Marshal(info)
- if err != nil {
- return common.Hash{}, err
- }
- contenthash := common.BytesToHash(crypto.Keccak256(infojson))
- return contenthash, ioutil.WriteFile(filename, infojson, 0600)
-}
diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go
index f16637547..0da3bb337 100644
--- a/common/compiler/solidity_test.go
+++ b/common/compiler/solidity_test.go
@@ -17,14 +17,8 @@
package compiler
import (
- "encoding/json"
- "io/ioutil"
- "os"
"os/exec"
- "path"
"testing"
-
- "github.com/ethereum/go-ethereum/common"
)
const (
@@ -36,7 +30,6 @@ contract test {
}
}
`
- testInfo = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0.1.1","compilerVersion":"0.1.1","compilerOptions":"--binary file --json-abi file --add-std 1","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}`
)
func skipWithoutSolc(t *testing.T) {
@@ -57,7 +50,10 @@ func TestCompiler(t *testing.T) {
}
c, ok := contracts["test"]
if !ok {
- t.Fatal("info for contract 'test' not present in result")
+ c, ok = contracts["<stdin>:test"]
+ if !ok {
+ t.Fatal("info for contract 'test' not present in result")
+ }
}
if c.Code == "" {
t.Error("empty code")
@@ -79,28 +75,3 @@ func TestCompileError(t *testing.T) {
}
t.Logf("error: %v", err)
}
-
-func TestSaveInfo(t *testing.T) {
- var cinfo ContractInfo
- err := json.Unmarshal([]byte(testInfo), &cinfo)
- if err != nil {
- t.Errorf("%v", err)
- }
- filename := path.Join(os.TempDir(), "solctest.info.json")
- os.Remove(filename)
- cinfohash, err := SaveInfo(&cinfo, filename)
- if err != nil {
- t.Errorf("error extracting info: %v", err)
- }
- got, err := ioutil.ReadFile(filename)
- if err != nil {
- t.Errorf("error reading '%v': %v", filename, err)
- }
- if string(got) != testInfo {
- t.Errorf("incorrect info.json extracted, expected:\n%s\ngot\n%s", testInfo, string(got))
- }
- wantHash := common.HexToHash("0x22450a77f0c3ff7a395948d07bc1456881226a1b6325f4189cb5f1254a824080")
- if cinfohash != wantHash {
- t.Errorf("content hash for info is incorrect. expected %v, got %v", wantHash.Hex(), cinfohash.Hex())
- }
-}
diff --git a/common/math/big.go b/common/math/big.go
index 0b67a1b50..5255a88e9 100644
--- a/common/math/big.go
+++ b/common/math/big.go
@@ -51,6 +51,9 @@ func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
// MarshalText implements encoding.TextMarshaler.
func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
+ if i == nil {
+ return []byte("0x0"), nil
+ }
return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
}
diff --git a/common/math/big_test.go b/common/math/big_test.go
index deff25465..e789bd18e 100644
--- a/common/math/big_test.go
+++ b/common/math/big_test.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/consensus/clique/api.go b/consensus/clique/api.go
index 0cf25abff..b875eef01 100644
--- a/consensus/clique/api.go
+++ b/consensus/clique/api.go
@@ -31,7 +31,7 @@ type API struct {
}
// GetSnapshot retrieves the state snapshot at a given block.
-func (api *API) GetSnapshot(number *rpc.BlockNumber) (interface{}, error) {
+func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
// Retrieve the requested block number (or current if none requested)
var header *types.Header
if number == nil || *number == rpc.LatestBlockNumber {
@@ -46,6 +46,15 @@ func (api *API) GetSnapshot(number *rpc.BlockNumber) (interface{}, error) {
return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
}
+// GetSnapshotAtHash retrieves the state snapshot at a given block.
+func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
+ header := api.chain.GetHeaderByHash(hash)
+ if header == nil {
+ return nil, errUnknownBlock
+ }
+ return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
+}
+
// GetSigners retrieves the list of authorized signers at the specified block.
func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) {
// Retrieve the requested block number (or current if none requested)
@@ -66,6 +75,19 @@ func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) {
return snap.signers(), nil
}
+// GetSignersAtHash retrieves the state snapshot at a given block.
+func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) {
+ header := api.chain.GetHeaderByHash(hash)
+ if header == nil {
+ return nil, errUnknownBlock
+ }
+ snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
+ if err != nil {
+ return nil, err
+ }
+ return snap.signers(), nil
+}
+
// Proposals returns the current proposals the node tries to uphold and vote on.
func (api *API) Proposals() map[common.Address]bool {
api.clique.lock.RLock()
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index e516d5057..8619bd1d8 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -220,6 +220,12 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique {
}
}
+// Author implements consensus.Engine, returning the Ethereum address recovered
+// from the signature in the header's extra-data section.
+func (c *Clique) Author(header *types.Header) (common.Address, error) {
+ return ecrecover(header)
+}
+
// VerifyHeader checks whether a header conforms to the consensus rules.
func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
return c.verifyHeader(chain, header, nil)
@@ -349,16 +355,16 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
}
// snapshot retrieves the authorization snapshot at a given point in time.
-func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*snapshot, error) {
+func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
// Search for a snapshot in memory or on disk for checkpoints
var (
headers []*types.Header
- snap *snapshot
+ snap *Snapshot
)
for snap == nil {
// If an in-memory snapshot was found, use that
if s, ok := c.recents.Get(hash); ok {
- snap = s.(*snapshot)
+ snap = s.(*Snapshot)
break
}
// If an on-disk checkpoint snapshot can be found, use that
diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go
index f95e1e920..46b32ca5f 100644
--- a/consensus/clique/snapshot.go
+++ b/consensus/clique/snapshot.go
@@ -26,45 +26,45 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-// vote represents a single vote that an authorized signer made to modify the
+// Vote represents a single vote that an authorized signer made to modify the
// list of authorizations.
-type vote struct {
+type Vote struct {
Signer common.Address `json:"signer"` // Authorized signer that cast this vote
Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes)
Address common.Address `json:"address"` // Account being voted on to change its authorization
Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account
}
-// tally is a simple vote tally to keep the current score of votes. Votes that
+// Tally is a simple vote tally to keep the current score of votes. Votes that
// go against the proposal aren't counted since it's equivalent to not voting.
-type tally struct {
+type Tally struct {
Authorize bool `json:"authorize"` // Whether the vote it about authorizing or kicking someone
Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal
}
-// snapshot is the state of the authorization voting at a given point in time.
-type snapshot struct {
+// Snapshot is the state of the authorization voting at a given point in time.
+type Snapshot struct {
config *params.CliqueConfig // Consensus engine parameters to fine tune behavior
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
- Votes []*vote `json:"votes"` // List of votes cast in chronological order
- Tally map[common.Address]tally `json:"tally"` // Current vote tally to avoid recalculating
+ Votes []*Vote `json:"votes"` // List of votes cast in chronological order
+ Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
}
// newSnapshot create a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for
// the genesis block.
-func newSnapshot(config *params.CliqueConfig, number uint64, hash common.Hash, signers []common.Address) *snapshot {
- snap := &snapshot{
+func newSnapshot(config *params.CliqueConfig, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
+ snap := &Snapshot{
config: config,
Number: number,
Hash: hash,
Signers: make(map[common.Address]struct{}),
Recents: make(map[uint64]common.Address),
- Tally: make(map[common.Address]tally),
+ Tally: make(map[common.Address]Tally),
}
for _, signer := range signers {
snap.Signers[signer] = struct{}{}
@@ -73,12 +73,12 @@ func newSnapshot(config *params.CliqueConfig, number uint64, hash common.Hash, s
}
// loadSnapshot loads an existing snapshot from the database.
-func loadSnapshot(config *params.CliqueConfig, db ethdb.Database, hash common.Hash) (*snapshot, error) {
+func loadSnapshot(config *params.CliqueConfig, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
blob, err := db.Get(append([]byte("clique-"), hash[:]...))
if err != nil {
return nil, err
}
- snap := new(snapshot)
+ snap := new(Snapshot)
if err := json.Unmarshal(blob, snap); err != nil {
return nil, err
}
@@ -88,7 +88,7 @@ func loadSnapshot(config *params.CliqueConfig, db ethdb.Database, hash common.Ha
}
// store inserts the snapshot into the database.
-func (s *snapshot) store(db ethdb.Database) error {
+func (s *Snapshot) store(db ethdb.Database) error {
blob, err := json.Marshal(s)
if err != nil {
return err
@@ -97,15 +97,15 @@ func (s *snapshot) store(db ethdb.Database) error {
}
// copy creates a deep copy of the snapshot, though not the individual votes.
-func (s *snapshot) copy() *snapshot {
- cpy := &snapshot{
+func (s *Snapshot) copy() *Snapshot {
+ cpy := &Snapshot{
config: s.config,
Number: s.Number,
Hash: s.Hash,
Signers: make(map[common.Address]struct{}),
Recents: make(map[uint64]common.Address),
- Votes: make([]*vote, len(s.Votes)),
- Tally: make(map[common.Address]tally),
+ Votes: make([]*Vote, len(s.Votes)),
+ Tally: make(map[common.Address]Tally),
}
for signer := range s.Signers {
cpy.Signers[signer] = struct{}{}
@@ -122,7 +122,7 @@ func (s *snapshot) copy() *snapshot {
}
// cast adds a new vote into the tally.
-func (s *snapshot) cast(address common.Address, authorize bool) bool {
+func (s *Snapshot) cast(address common.Address, authorize bool) bool {
// Ensure the vote is meaningful
_, signer := s.Signers[address]
if (signer && authorize) || (!signer && !authorize) {
@@ -133,13 +133,13 @@ func (s *snapshot) cast(address common.Address, authorize bool) bool {
old.Votes++
s.Tally[address] = old
} else {
- s.Tally[address] = tally{Authorize: authorize, Votes: 1}
+ s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
}
return true
}
// uncast removes a previously cast vote from the tally.
-func (s *snapshot) uncast(address common.Address, authorize bool) bool {
+func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
// If there's no tally, it's a dangling vote, just drop
tally, ok := s.Tally[address]
if !ok {
@@ -161,7 +161,7 @@ func (s *snapshot) uncast(address common.Address, authorize bool) bool {
// apply creates a new authorization snapshot by applying the given headers to
// the original one.
-func (s *snapshot) apply(headers []*types.Header) (*snapshot, error) {
+func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
// Allow passing in no headers for cleaner code
if len(headers) == 0 {
return s, nil
@@ -183,7 +183,7 @@ func (s *snapshot) apply(headers []*types.Header) (*snapshot, error) {
number := header.Number.Uint64()
if number%s.config.Epoch == 0 {
snap.Votes = nil
- snap.Tally = make(map[common.Address]tally)
+ snap.Tally = make(map[common.Address]Tally)
}
// Delete the oldest signer from the recent list to allow it signing again
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
@@ -226,7 +226,7 @@ func (s *snapshot) apply(headers []*types.Header) (*snapshot, error) {
return nil, errInvalidVote
}
if snap.cast(header.Coinbase, authorize) {
- snap.Votes = append(snap.Votes, &vote{
+ snap.Votes = append(snap.Votes, &Vote{
Signer: signer,
Block: number,
Address: header.Coinbase,
@@ -274,7 +274,7 @@ func (s *snapshot) apply(headers []*types.Header) (*snapshot, error) {
}
// signers retrieves the list of authorized signers in ascending order.
-func (s *snapshot) signers() []common.Address {
+func (s *Snapshot) signers() []common.Address {
signers := make([]common.Address, 0, len(s.Signers))
for signer := range s.Signers {
signers = append(signers, signer)
@@ -290,7 +290,7 @@ func (s *snapshot) signers() []common.Address {
}
// inturn returns if a signer at a given block height is in-turn or not.
-func (s *snapshot) inturn(number uint64, signer common.Address) bool {
+func (s *Snapshot) inturn(number uint64, signer common.Address) bool {
signers, offset := s.signers(), 0
for offset < len(signers) && signers[offset] != signer {
offset++
diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go
index 3ebe33bc5..49a1d7d5b 100644
--- a/consensus/clique/snapshot_test.go
+++ b/consensus/clique/snapshot_test.go
@@ -78,6 +78,7 @@ func (r *testerChainReader) Config() *params.ChainConfig { panic
func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") }
func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") }
func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") }
+func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") }
func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header {
if number == 0 {
return core.GetHeader(r.db, core.GetCanonicalHash(r.db, 0), 0)
diff --git a/consensus/consensus.go b/consensus/consensus.go
index e318e57c0..8cbd32c88 100644
--- a/consensus/consensus.go
+++ b/consensus/consensus.go
@@ -40,12 +40,20 @@ type ChainReader interface {
// GetHeaderByNumber retrieves a block header from the database by number.
GetHeaderByNumber(number uint64) *types.Header
+ // GetHeaderByHash retrieves a block header from the database by its hash.
+ GetHeaderByHash(hash common.Hash) *types.Header
+
// GetBlock retrieves a block from the database by hash and number.
GetBlock(hash common.Hash, number uint64) *types.Block
}
// Engine is an algorithm agnostic consensus engine.
type Engine interface {
+ // Author retrieves the Ethereum address of the account that minted the given
+ // block, which may be different from the header's coinbase if a consensus
+ // engine is based on signatures.
+ Author(header *types.Header) (common.Address, error)
+
// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index f4f8f4b17..4f1ab8702 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -22,7 +22,6 @@ import (
"fmt"
"math/big"
"runtime"
- "sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -46,7 +45,6 @@ var (
// codebase, inherently breaking if the engine is swapped out. Please put common
// error types into the consensus package.
var (
- errInvalidChain = errors.New("invalid header chain")
errLargeBlockTime = errors.New("timestamp too big")
errZeroBlockTime = errors.New("timestamp equals parent's")
errTooManyUncles = errors.New("too many uncles")
@@ -59,6 +57,12 @@ var (
errInvalidPoW = errors.New("invalid proof-of-work")
)
+// Author implements consensus.Engine, returning the header's coinbase as the
+// proof-of-work verified author of the block.
+func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
+ return header.Coinbase, nil
+}
+
// VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
@@ -84,111 +88,80 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He
// a results channel to retrieve the async verifications.
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
// If we're running a full engine faking, accept any input as valid
- if ethash.fakeFull {
+ if ethash.fakeFull || len(headers) == 0 {
abort, results := make(chan struct{}), make(chan error, len(headers))
for i := 0; i < len(headers); i++ {
results <- nil
}
return abort, results
}
+
// Spawn as many workers as allowed threads
workers := runtime.GOMAXPROCS(0)
if len(headers) < workers {
workers = len(headers)
}
- // Create a task channel and spawn the verifiers
- type result struct {
- index int
- err error
- }
- inputs := make(chan int, workers)
- outputs := make(chan result, len(headers))
- var badblock uint64
+ // Create a task channel and spawn the verifiers
+ var (
+ inputs = make(chan int)
+ done = make(chan int, workers)
+ errors = make([]error, len(headers))
+ abort = make(chan struct{})
+ )
for i := 0; i < workers; i++ {
go func() {
for index := range inputs {
- // If we've found a bad block already before this, stop validating
- if bad := atomic.LoadUint64(&badblock); bad != 0 && bad <= headers[index].Number.Uint64() {
- outputs <- result{index: index, err: errInvalidChain}
- continue
- }
- // We need to look up the first parent
- var parent *types.Header
- if index == 0 {
- parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
- } else if headers[index-1].Hash() == headers[index].ParentHash {
- parent = headers[index-1]
- }
- // Ensure the validation is useful and execute it
- var failure error
- switch {
- case chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()-1) != nil:
- outputs <- result{index: index, err: nil}
- case parent == nil:
- failure = consensus.ErrUnknownAncestor
- outputs <- result{index: index, err: failure}
- default:
- failure = ethash.verifyHeader(chain, headers[index], parent, false, seals[index])
- outputs <- result{index: index, err: failure}
- }
- // If a validation failure occurred, mark subsequent blocks invalid
- if failure != nil {
- number := headers[index].Number.Uint64()
- if prev := atomic.LoadUint64(&badblock); prev == 0 || prev > number {
- // This two step atomic op isn't thread-safe in that `badblock` might end
- // up slightly higher than the block number of the first failure (if many
- // workers try to write at the same time), but it's fine as we're mostly
- // interested to avoid large useless work, we don't care about 1-2 extra
- // runs. Doing "full thread safety" would involve mutexes, which would be
- // a noticeable sync overhead on the fast spinning worker routines.
- atomic.StoreUint64(&badblock, number)
- }
- }
+ errors[index] = ethash.verifyHeaderWorker(chain, headers, seals, index)
+ done <- index
}
}()
}
- // Feed item indices to the workers until done, sorting and feeding the results to the caller
- dones := make([]bool, len(headers))
- errors := make([]error, len(headers))
-
- abort := make(chan struct{})
- returns := make(chan error, len(headers))
+ errorsOut := make(chan error, len(headers))
go func() {
defer close(inputs)
-
- input, output := 0, 0
- for i := 0; i < len(headers)*2; i++ {
- var res result
-
- // If there are tasks left, push to workers
- if input < len(headers) {
- select {
- case inputs <- input:
- input++
- continue
- case <-abort:
- return
- case res = <-outputs:
+ var (
+ in, out = 0, 0
+ checked = make([]bool, len(headers))
+ inputs = inputs
+ )
+ for {
+ select {
+ case inputs <- in:
+ if in++; in == len(headers) {
+ // Reached end of headers. Stop sending to workers.
+ inputs = nil
}
- } else {
- // Otherwise keep waiting for results
- select {
- case <-abort:
- return
- case res = <-outputs:
+ case index := <-done:
+ for checked[index] = true; checked[out]; out++ {
+ errorsOut <- errors[out]
+ if out == len(headers)-1 {
+ return
+ }
}
- }
- // A result arrived, save and propagate if next
- dones[res.index], errors[res.index] = true, res.err
- for output < len(headers) && dones[output] {
- returns <- errors[output]
- output++
+ case <-abort:
+ return
}
}
}()
- return abort, returns
+ return abort, errorsOut
+}
+
+func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, seals []bool, index int) error {
+ var parent *types.Header
+ if index == 0 {
+ parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
+ } else if headers[index-1].Hash() == headers[index].ParentHash {
+ parent = headers[index-1]
+ }
+ if parent == nil {
+ return consensus.ErrUnknownAncestor
+ }
+ if chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()) != nil {
+ return nil // known block
+ }
+ return ethash.verifyHeader(chain, headers[index], parent, false, seals[index])
}
// VerifyUncles verifies that the given block's uncles conform to the consensus
diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go
index d284e7b00..b028f50e6 100644
--- a/consensus/ethash/ethash.go
+++ b/consensus/ethash/ethash.go
@@ -130,13 +130,16 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint
data := buffer[len(dumpMagic):]
generator(data)
- if err := mem.Flush(); err != nil {
- mem.Unmap()
- dump.Close()
+ if err := mem.Unmap(); err != nil {
+ return nil, nil, nil, err
+ }
+ if err := dump.Close(); err != nil {
+ return nil, nil, nil, err
+ }
+ if err := os.Rename(temp, path); err != nil {
return nil, nil, nil, err
}
- os.Rename(temp, path)
- return dump, mem, data, nil
+ return memoryMap(path)
}
// cache wraps an ethash cache with some metadata to allow easier concurrent use.
diff --git a/console/console_test.go b/console/console_test.go
index b5cff2c4d..0fc0e7051 100644
--- a/console/console_test.go
+++ b/console/console_test.go
@@ -91,7 +91,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
}
// Create a networkless protocol stack and start an Ethereum service within
- stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance, NoDiscovery: true})
+ stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance})
if err != nil {
t.Fatalf("failed to create node: %v", err)
}
diff --git a/core/blockchain.go b/core/blockchain.go
index b601c462c..d6f2653ae 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -113,7 +113,7 @@ type BlockChain struct {
}
// NewBlockChain returns a fully initialised block chain using information
-// available in the database. It initialiser the default Ethereum Validator and
+// available in the database. It initialises the default Ethereum Validator and
// Processor.
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, mux *event.TypeMux, vmConfig vm.Config) (*BlockChain, error) {
bodyCache, _ := lru.New(bodyCacheLimit)
@@ -1398,3 +1398,6 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
// Config retrieves the blockchain's chain configuration.
func (self *BlockChain) Config() *params.ChainConfig { return self.config }
+
+// Engine retrieves the blockchain's consensus engine.
+func (self *BlockChain) Engine() consensus.Engine { return self.engine }
diff --git a/core/chain_makers.go b/core/chain_makers.go
index c47c719f6..f34279ba0 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -85,7 +85,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
- receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
+ receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
diff --git a/core/dao_test.go b/core/dao_test.go
index cb6e54f8f..bc9f3f394 100644
--- a/core/dao_test.go
+++ b/core/dao_test.go
@@ -62,7 +62,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
gspec.MustCommit(db)
bc, _ := NewBlockChain(db, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
- blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
+ blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
for j := 0; j < len(blocks)/2; j++ {
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
}
@@ -83,7 +83,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
gspec.MustCommit(db)
bc, _ = NewBlockChain(db, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
- blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
+ blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
for j := 0; j < len(blocks)/2; j++ {
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
}
@@ -105,7 +105,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
gspec.MustCommit(db)
bc, _ := NewBlockChain(db, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
- blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
+ blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
for j := 0; j < len(blocks)/2; j++ {
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
}
@@ -121,7 +121,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
gspec.MustCommit(db)
bc, _ = NewBlockChain(db, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
- blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
+ blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
for j := 0; j < len(blocks)/2; j++ {
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
}
diff --git a/core/evm.go b/core/evm.go
index 6a5713075..4912aa650 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -20,25 +20,36 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
)
-// BlockFetcher retrieves headers by their hash
-type HeaderFetcher interface {
- // GetHeader returns the hash corresponding to their hash
+// ChainContext supports retrieving headers and consensus parameters from the
+// current blockchain to be used during transaction processing.
+type ChainContext interface {
+ // Engine retrieves the chain's consensus engine.
+ Engine() consensus.Engine
+
+ // GetHeader returns the hash corresponding to their hash.
GetHeader(common.Hash, uint64) *types.Header
}
// NewEVMContext creates a new context for use in the EVM.
-func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context {
+func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context {
+ // If we don't have an explicit author (i.e. not mining), extract from the header
+ var beneficiary common.Address
+ if author == nil {
+ beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation
+ } else {
+ beneficiary = *author
+ }
return vm.Context{
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: GetHashFn(header, chain),
-
Origin: msg.From(),
- Coinbase: header.Coinbase,
+ Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number),
Time: new(big.Int).Set(header.Time),
Difficulty: new(big.Int).Set(header.Difficulty),
@@ -48,7 +59,7 @@ func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Co
}
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
-func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash {
+func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash {
return func(n uint64) common.Hash {
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
if header.Number.Uint64() == n {
diff --git a/core/headerchain.go b/core/headerchain.go
index f58afc6ca..9bb7f1793 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -442,6 +442,9 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
// Config retrieves the header chain's chain configuration.
func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config }
+// Engine retrieves the header chain's consensus engine.
+func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine }
+
// GetBlock implements consensus.ChainReader, and returns nil for every input as
// a header chain does not have blocks available for retrieval.
func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block {
diff --git a/core/state_processor.go b/core/state_processor.go
index aca2929eb..4fc2f1eae 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -19,6 +19,7 @@ package core
import (
"math/big"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state"
@@ -69,7 +70,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i)
- receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
+ receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
return nil, nil, nil, err
}
@@ -86,13 +87,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
-func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
+func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
return nil, nil, err
}
// Create a new context to be used in the EVM environment
- context := NewEVMContext(msg, header, bc)
+ context := NewEVMContext(msg, header, bc, author)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg)
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index f1c62df8e..24ad6caa5 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
package vm
import (
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index cceb89285..1ee909e92 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
package vm
import (
diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go
index 61c83ba7e..82fbfed69 100644
--- a/core/vm/int_pool_verifier.go
+++ b/core/vm/int_pool_verifier.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
// +build VERIFY_EVM_INTEGER_POOL
package vm
diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go
index 982f8c6dd..a5f1dc02b 100644
--- a/core/vm/int_pool_verifier_empty.go
+++ b/core/vm/int_pool_verifier_empty.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
// +build !VERIFY_EVM_INTEGER_POOL
package vm
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 4d8ece41c..c0c52732b 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
index 3141a2f61..654137c70 100644
--- a/core/vm/memory_table.go
+++ b/core/vm/memory_table.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
package vm
import (
diff --git a/core/vm/runtime/fuzz.go b/core/vm/runtime/fuzz.go
index de5b0f45d..cb9ff08b5 100644
--- a/core/vm/runtime/fuzz.go
+++ b/core/vm/runtime/fuzz.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
// +build gofuzz
package runtime
diff --git a/core/vm/stack_table.go b/core/vm/stack_table.go
index ddc41fed2..a4b1cfcd8 100644
--- a/core/vm/stack_table.go
+++ b/core/vm/stack_table.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
package vm
import (
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
index d1485de08..feec5e7be 100644
--- a/crypto/signature_cgo.go
+++ b/crypto/signature_cgo.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go
index 47880aaf4..a022eef9a 100644
--- a/crypto/signature_nocgo.go
+++ b/crypto/signature_nocgo.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/crypto/signature_test.go b/crypto/signature_test.go
index ca7eefe99..aefd9e38d 100644
--- a/crypto/signature_test.go
+++ b/crypto/signature_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/eth/api.go b/eth/api.go
index 0decd57ca..b386c08b4 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -548,7 +548,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
if err != nil {
return nil, fmt.Errorf("sender retrieval failed: %v", err)
}
- context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain())
+ context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain(), nil)
// Mutate the state if we haven't reached the tracing transaction yet
if uint64(idx) < txIndex {
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 61e184401..fe108d272 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -114,7 +114,7 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state etha
from.SetBalance(math.MaxBig256)
vmError := func() error { return nil }
- context := core.NewEVMContext(msg, header, b.eth.BlockChain())
+ context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil
}
diff --git a/eth/backend.go b/eth/backend.go
index 4dffa2990..03c2e38e5 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -18,14 +18,15 @@
package eth
import (
+ "errors"
"fmt"
- "math/big"
- "regexp"
+ "runtime"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
@@ -43,55 +44,10 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)
-var (
- datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
- portInUseErrRE = regexp.MustCompile("address already in use")
-)
-
-type Config struct {
- // The genesis block, which is inserted if the database is empty.
- // If nil, the Ethereum main net block is used.
- Genesis *core.Genesis
-
- NetworkId int // Network ID to use for selecting peers to connect to
-
- FastSync bool // Enables the state download based fast synchronisation algorithm
- LightMode bool // Running in light client mode
- LightServ int // Maximum percentage of time allowed for serving LES requests
- LightPeers int // Maximum number of LES client peers
- MaxPeers int // Maximum number of global peers
-
- SkipBcVersionCheck bool // e.g. blockchain export
- DatabaseCache int
- DatabaseHandles int
-
- DocRoot string
- PowFake bool
- PowTest bool
- PowShared bool
- ExtraData []byte
-
- EthashCacheDir string
- EthashCachesInMem int
- EthashCachesOnDisk int
- EthashDatasetDir string
- EthashDatasetsInMem int
- EthashDatasetsOnDisk int
-
- Etherbase common.Address
- GasPrice *big.Int
- MinerThreads int
- SolcPath string
-
- GpoBlocks int
- GpoPercentile int
-
- EnablePreimageRecording bool
-}
-
type LesServer interface {
Start(srvr *p2p.Server)
Stop()
@@ -123,7 +79,6 @@ type Ethereum struct {
Mining bool
MinerThreads int
etherbase common.Address
- solcPath string
netVersionId int
netRPCService *ethapi.PublicNetAPI
@@ -137,6 +92,13 @@ func (s *Ethereum) AddLesServer(ls LesServer) {
// New creates a new Ethereum object (including the
// initialisation of the common Ethereum object)
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
+ if config.SyncMode == downloader.LightSync {
+ return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum")
+ }
+ if !config.SyncMode.IsValid() {
+ return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
+ }
+
chainDb, err := CreateDB(ctx, config, "chaindata")
if err != nil {
return nil, err
@@ -159,7 +121,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
netVersionId: config.NetworkId,
etherbase: config.Etherbase,
MinerThreads: config.MinerThreads,
- solcPath: config.SolcPath,
}
if err := addMipmapBloomBins(chainDb); err != nil {
@@ -201,25 +162,41 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
}
- if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
+ if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
return nil, err
}
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
eth.miner.SetGasPrice(config.GasPrice)
- eth.miner.SetExtra(config.ExtraData)
+ eth.miner.SetExtra(makeExtraData(config.ExtraData))
eth.ApiBackend = &EthApiBackend{eth, nil}
- gpoParams := gasprice.Config{
- Blocks: config.GpoBlocks,
- Percentile: config.GpoPercentile,
- Default: config.GasPrice,
+ gpoParams := config.GPO
+ if gpoParams.Default == nil {
+ gpoParams.Default = config.GasPrice
}
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
return eth, nil
}
+func makeExtraData(extra []byte) []byte {
+ if len(extra) == 0 {
+ // create default extradata
+ extra, _ = rlp.EncodeToBytes([]interface{}{
+ uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch),
+ "geth",
+ runtime.Version(),
+ runtime.GOOS,
+ })
+ }
+ if uint64(len(extra)) > params.MaximumExtraDataSize {
+ log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
+ extra = nil
+ }
+ return extra
+}
+
// CreateDB creates the chain database.
func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) {
db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles)
@@ -257,7 +234,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API {
- apis := ethapi.GetAPIs(s.ApiBackend, s.solcPath)
+ apis := ethapi.GetAPIs(s.ApiBackend)
// Append any APIs exposed explicitly by the consensus engine
apis = append(apis, s.engine.APIs(s.BlockChain())...)
@@ -415,8 +392,3 @@ func (s *Ethereum) Stop() error {
return nil
}
-
-// This function will wait for a shutdown and resumes main thread execution
-func (s *Ethereum) WaitForShutdown() {
- <-s.shutdownChan
-}
diff --git a/eth/config.go b/eth/config.go
new file mode 100644
index 000000000..7049940d3
--- /dev/null
+++ b/eth/config.go
@@ -0,0 +1,117 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package eth
+
+import (
+ "math/big"
+ "os"
+ "os/user"
+ "path/filepath"
+ "runtime"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/eth/gasprice"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// DefaultConfig contains default settings for use on the Ethereum main net.
+var DefaultConfig = Config{
+ SyncMode: downloader.FastSync,
+ EthashCacheDir: "ethash",
+ EthashCachesInMem: 2,
+ EthashCachesOnDisk: 3,
+ EthashDatasetsInMem: 1,
+ EthashDatasetsOnDisk: 2,
+ NetworkId: 1,
+ LightPeers: 20,
+ DatabaseCache: 128,
+ GasPrice: big.NewInt(20 * params.Shannon),
+
+ GPO: gasprice.Config{
+ Blocks: 10,
+ Percentile: 50,
+ },
+}
+
+func init() {
+ home := os.Getenv("HOME")
+ if home == "" {
+ if user, err := user.Current(); err == nil {
+ home = user.HomeDir
+ }
+ }
+ if runtime.GOOS == "windows" {
+ DefaultConfig.EthashDatasetDir = filepath.Join(home, "AppData", "Ethash")
+ } else {
+ DefaultConfig.EthashDatasetDir = filepath.Join(home, ".ethash")
+ }
+}
+
+//go:generate gencodec -type Config -field-override configMarshaling -formats toml -out gen_config.go
+
+type Config struct {
+ // The genesis block, which is inserted if the database is empty.
+ // If nil, the Ethereum main net block is used.
+ Genesis *core.Genesis `toml:",omitempty"`
+
+ // Protocol options
+ NetworkId int // Network ID to use for selecting peers to connect to
+ SyncMode downloader.SyncMode
+
+ // Light client options
+ LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
+ LightPeers int `toml:",omitempty"` // Maximum number of LES client peers
+ MaxPeers int `toml:"-"` // Maximum number of global peers
+
+ // Database options
+ SkipBcVersionCheck bool `toml:"-"`
+ DatabaseHandles int `toml:"-"`
+ DatabaseCache int
+
+ // Mining-related options
+ Etherbase common.Address `toml:",omitempty"`
+ MinerThreads int `toml:",omitempty"`
+ ExtraData []byte `toml:",omitempty"`
+ GasPrice *big.Int
+
+ // Ethash options
+ EthashCacheDir string
+ EthashCachesInMem int
+ EthashCachesOnDisk int
+ EthashDatasetDir string
+ EthashDatasetsInMem int
+ EthashDatasetsOnDisk int
+
+ // Gas Price Oracle options
+ GPO gasprice.Config
+
+ // Enables tracking of SHA3 preimages in the VM
+ EnablePreimageRecording bool
+
+ // Miscellaneous options
+ DocRoot string `toml:"-"`
+ PowFake bool `toml:"-"`
+ PowTest bool `toml:"-"`
+ PowShared bool `toml:"-"`
+}
+
+type configMarshaling struct {
+ ExtraData hexutil.Bytes
+}
diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go
index ae3c43888..8ecdf91f1 100644
--- a/eth/downloader/modes.go
+++ b/eth/downloader/modes.go
@@ -16,6 +16,8 @@
package downloader
+import "fmt"
+
// SyncMode represents the synchronisation mode of the downloader.
type SyncMode int
@@ -25,6 +27,10 @@ const (
LightSync // Download only the headers and terminate afterwards
)
+func (mode SyncMode) IsValid() bool {
+ return mode >= FullSync && mode <= LightSync
+}
+
// String implements the stringer interface.
func (mode SyncMode) String() string {
switch mode {
@@ -38,3 +44,30 @@ func (mode SyncMode) String() string {
return "unknown"
}
}
+
+func (mode SyncMode) MarshalText() ([]byte, error) {
+ switch mode {
+ case FullSync:
+ return []byte("full"), nil
+ case FastSync:
+ return []byte("fast"), nil
+ case LightSync:
+ return []byte("light"), nil
+ default:
+ return nil, fmt.Errorf("unknown sync mode %d", mode)
+ }
+}
+
+func (mode *SyncMode) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *mode = FullSync
+ case "fast":
+ *mode = FastSync
+ case "light":
+ *mode = LightSync
+ default:
+ return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text)
+ }
+ return nil
+}
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index bac048c88..c662348e1 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -33,7 +33,7 @@ var maxPrice = big.NewInt(500 * params.Shannon)
type Config struct {
Blocks int
Percentile int
- Default *big.Int
+ Default *big.Int `toml:",omitempty"`
}
// Oracle recommends gas prices based on the content of recent
diff --git a/eth/gen_config.go b/eth/gen_config.go
new file mode 100644
index 000000000..56fba1d89
--- /dev/null
+++ b/eth/gen_config.go
@@ -0,0 +1,180 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package eth
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/eth/gasprice"
+)
+
+func (c Config) MarshalTOML() (interface{}, error) {
+ type Config struct {
+ Genesis *core.Genesis `toml:",omitempty"`
+ NetworkId int
+ SyncMode downloader.SyncMode
+ LightServ int `toml:",omitempty"`
+ LightPeers int `toml:",omitempty"`
+ MaxPeers int `toml:"-"`
+ SkipBcVersionCheck bool `toml:"-"`
+ DatabaseHandles int `toml:"-"`
+ DatabaseCache int
+ Etherbase common.Address `toml:",omitempty"`
+ MinerThreads int `toml:",omitempty"`
+ ExtraData hexutil.Bytes `toml:",omitempty"`
+ GasPrice *big.Int
+ EthashCacheDir string
+ EthashCachesInMem int
+ EthashCachesOnDisk int
+ EthashDatasetDir string
+ EthashDatasetsInMem int
+ EthashDatasetsOnDisk int
+ GPO gasprice.Config
+ EnablePreimageRecording bool
+ DocRoot string `toml:"-"`
+ PowFake bool `toml:"-"`
+ PowTest bool `toml:"-"`
+ PowShared bool `toml:"-"`
+ }
+ var enc Config
+ enc.Genesis = c.Genesis
+ enc.NetworkId = c.NetworkId
+ enc.SyncMode = c.SyncMode
+ enc.LightServ = c.LightServ
+ enc.LightPeers = c.LightPeers
+ enc.MaxPeers = c.MaxPeers
+ enc.SkipBcVersionCheck = c.SkipBcVersionCheck
+ enc.DatabaseHandles = c.DatabaseHandles
+ enc.DatabaseCache = c.DatabaseCache
+ enc.Etherbase = c.Etherbase
+ enc.MinerThreads = c.MinerThreads
+ enc.ExtraData = c.ExtraData
+ enc.GasPrice = c.GasPrice
+ enc.EthashCacheDir = c.EthashCacheDir
+ enc.EthashCachesInMem = c.EthashCachesInMem
+ enc.EthashCachesOnDisk = c.EthashCachesOnDisk
+ enc.EthashDatasetDir = c.EthashDatasetDir
+ enc.EthashDatasetsInMem = c.EthashDatasetsInMem
+ enc.EthashDatasetsOnDisk = c.EthashDatasetsOnDisk
+ enc.GPO = c.GPO
+ enc.EnablePreimageRecording = c.EnablePreimageRecording
+ enc.DocRoot = c.DocRoot
+ enc.PowFake = c.PowFake
+ enc.PowTest = c.PowTest
+ enc.PowShared = c.PowShared
+ return &enc, nil
+}
+
+func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
+ type Config struct {
+ Genesis *core.Genesis `toml:",omitempty"`
+ NetworkId *int
+ SyncMode *downloader.SyncMode
+ LightServ *int `toml:",omitempty"`
+ LightPeers *int `toml:",omitempty"`
+ MaxPeers *int `toml:"-"`
+ SkipBcVersionCheck *bool `toml:"-"`
+ DatabaseHandles *int `toml:"-"`
+ DatabaseCache *int
+ Etherbase *common.Address `toml:",omitempty"`
+ MinerThreads *int `toml:",omitempty"`
+ ExtraData hexutil.Bytes `toml:",omitempty"`
+ GasPrice *big.Int
+ EthashCacheDir *string
+ EthashCachesInMem *int
+ EthashCachesOnDisk *int
+ EthashDatasetDir *string
+ EthashDatasetsInMem *int
+ EthashDatasetsOnDisk *int
+ GPO *gasprice.Config
+ EnablePreimageRecording *bool
+ DocRoot *string `toml:"-"`
+ PowFake *bool `toml:"-"`
+ PowTest *bool `toml:"-"`
+ PowShared *bool `toml:"-"`
+ }
+ var dec Config
+ if err := unmarshal(&dec); err != nil {
+ return err
+ }
+ if dec.Genesis != nil {
+ c.Genesis = dec.Genesis
+ }
+ if dec.NetworkId != nil {
+ c.NetworkId = *dec.NetworkId
+ }
+ if dec.SyncMode != nil {
+ c.SyncMode = *dec.SyncMode
+ }
+ if dec.LightServ != nil {
+ c.LightServ = *dec.LightServ
+ }
+ if dec.LightPeers != nil {
+ c.LightPeers = *dec.LightPeers
+ }
+ if dec.MaxPeers != nil {
+ c.MaxPeers = *dec.MaxPeers
+ }
+ if dec.SkipBcVersionCheck != nil {
+ c.SkipBcVersionCheck = *dec.SkipBcVersionCheck
+ }
+ if dec.DatabaseHandles != nil {
+ c.DatabaseHandles = *dec.DatabaseHandles
+ }
+ if dec.DatabaseCache != nil {
+ c.DatabaseCache = *dec.DatabaseCache
+ }
+ if dec.Etherbase != nil {
+ c.Etherbase = *dec.Etherbase
+ }
+ if dec.MinerThreads != nil {
+ c.MinerThreads = *dec.MinerThreads
+ }
+ if dec.ExtraData != nil {
+ c.ExtraData = dec.ExtraData
+ }
+ if dec.GasPrice != nil {
+ c.GasPrice = dec.GasPrice
+ }
+ if dec.EthashCacheDir != nil {
+ c.EthashCacheDir = *dec.EthashCacheDir
+ }
+ if dec.EthashCachesInMem != nil {
+ c.EthashCachesInMem = *dec.EthashCachesInMem
+ }
+ if dec.EthashCachesOnDisk != nil {
+ c.EthashCachesOnDisk = *dec.EthashCachesOnDisk
+ }
+ if dec.EthashDatasetDir != nil {
+ c.EthashDatasetDir = *dec.EthashDatasetDir
+ }
+ if dec.EthashDatasetsInMem != nil {
+ c.EthashDatasetsInMem = *dec.EthashDatasetsInMem
+ }
+ if dec.EthashDatasetsOnDisk != nil {
+ c.EthashDatasetsOnDisk = *dec.EthashDatasetsOnDisk
+ }
+ if dec.GPO != nil {
+ c.GPO = *dec.GPO
+ }
+ if dec.EnablePreimageRecording != nil {
+ c.EnablePreimageRecording = *dec.EnablePreimageRecording
+ }
+ if dec.DocRoot != nil {
+ c.DocRoot = *dec.DocRoot
+ }
+ if dec.PowFake != nil {
+ c.PowFake = *dec.PowFake
+ }
+ if dec.PowTest != nil {
+ c.PowTest = *dec.PowTest
+ }
+ if dec.PowShared != nil {
+ c.PowShared = *dec.PowShared
+ }
+ return nil
+}
diff --git a/eth/handler.go b/eth/handler.go
index 99c2c4b32..fb8a0fd57 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -96,7 +96,7 @@ type ProtocolManager struct {
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network.
-func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
+func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
// Create the protocol manager with the base fields
manager := &ProtocolManager{
networkId: networkId,
@@ -113,18 +113,18 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
quitSync: make(chan struct{}),
}
// Figure out whether to allow fast sync or not
- if fastSync && blockchain.CurrentBlock().NumberU64() > 0 {
+ if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 {
log.Warn("Blockchain not empty, fast sync disabled")
- fastSync = false
+ mode = downloader.FullSync
}
- if fastSync {
+ if mode == downloader.FastSync {
manager.fastSync = uint32(1)
}
// Initiate a sub-protocol for every implemented version we can handle
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
for i, version := range ProtocolVersions {
// Skip protocol version if incompatible with the mode of operation
- if fastSync && version < eth63 {
+ if mode == downloader.FastSync && version < eth63 {
continue
}
// Compatible; initialise the sub-protocol
@@ -159,7 +159,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
return nil, errIncompatibleConfig
}
// Construct the different synchronisation mechanisms
- manager.downloader = downloader.New(downloader.FullSync, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash,
+ manager.downloader = downloader.New(mode, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash,
blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead,
blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback,
manager.removePeer)
diff --git a/eth/handler_test.go b/eth/handler_test.go
index f85d730b6..413ed2bff 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -44,11 +44,11 @@ func TestProtocolCompatibility(t *testing.T) {
// Define the compatibility chart
tests := []struct {
version uint
- fastSync bool
+ mode downloader.SyncMode
compatible bool
}{
- {61, false, true}, {62, false, true}, {63, false, true},
- {61, true, false}, {62, true, false}, {63, true, true},
+ {61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true},
+ {61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true},
}
// Make sure anything we screw up is restored
backup := ProtocolVersions
@@ -58,7 +58,7 @@ func TestProtocolCompatibility(t *testing.T) {
for i, tt := range tests {
ProtocolVersions = []uint{tt.version}
- pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil)
+ pm, err := newTestProtocolManager(tt.mode, 0, nil, nil)
if pm != nil {
defer pm.Stop()
}
@@ -73,7 +73,7 @@ func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
func testGetBlockHeaders(t *testing.T, protocol int) {
- pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
@@ -232,7 +232,7 @@ func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
func testGetBlockBodies(t *testing.T, protocol int) {
- pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
@@ -339,7 +339,7 @@ func testGetNodeData(t *testing.T, protocol int) {
}
}
// Assemble the test environment
- pm := newTestProtocolManagerMust(t, false, 4, generator, nil)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
@@ -431,7 +431,7 @@ func testGetReceipt(t *testing.T, protocol int) {
}
}
// Assemble the test environment
- pm := newTestProtocolManagerMust(t, false, 4, generator, nil)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
@@ -476,7 +476,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, config, pow, evmux, vm.Config{})
)
- pm, err := NewProtocolManager(config, false, NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db)
+ pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db)
if err != nil {
t.Fatalf("failed to start test protocol manager: %v", err)
}
diff --git a/eth/helper_test.go b/eth/helper_test.go
index a8c538e6c..21ac3724e 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
@@ -48,7 +49,7 @@ var (
// newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// channels for different events.
-func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
+func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
var (
evmux = new(event.TypeMux)
engine = ethash.NewFaker()
@@ -65,7 +66,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
panic(err)
}
- pm, err := NewProtocolManager(gspec.Config, fastSync, NetworkId, 1000, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
+ pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, 1000, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
if err != nil {
return nil, err
}
@@ -77,8 +78,8 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
// with the given number of blocks already known, and potential notification
// channels for different events. In case of an error, the constructor force-
// fails the test.
-func newTestProtocolManagerMust(t *testing.T, fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
- pm, err := newTestProtocolManager(fastSync, blocks, generator, newtx)
+func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
+ pm, err := newTestProtocolManager(mode, blocks, generator, newtx)
if err != nil {
t.Fatalf("Failed to create protocol manager: %v", err)
}
@@ -172,7 +173,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
msg := &statusData{
ProtocolVersion: uint32(p.version),
- NetworkId: uint32(NetworkId),
+ NetworkId: uint32(DefaultConfig.NetworkId),
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
diff --git a/eth/protocol.go b/eth/protocol.go
index 7d22b33de..40997da7a 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -41,10 +41,7 @@ var ProtocolVersions = []uint{eth63, eth62}
// Number of implemented message corresponding to different protocol versions.
var ProtocolLengths = []uint64{17, 8}
-const (
- NetworkId = 1
- ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
-)
+const ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
// eth protocol message codes
const (
diff --git a/eth/protocol_test.go b/eth/protocol_test.go
index 3c9a734df..74180bedd 100644
--- a/eth/protocol_test.go
+++ b/eth/protocol_test.go
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -40,7 +41,7 @@ func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) }
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
func testStatusMsgErrors(t *testing.T, protocol int) {
- pm := newTestProtocolManagerMust(t, false, 0, nil, nil)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
td, currentBlock, genesis := pm.blockchain.Status()
defer pm.Stop()
@@ -54,7 +55,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
},
{
- code: StatusMsg, data: statusData{10, NetworkId, td, currentBlock, genesis},
+ code: StatusMsg, data: statusData{10, uint32(DefaultConfig.NetworkId), td, currentBlock, genesis},
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol),
},
{
@@ -62,7 +63,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
},
{
- code: StatusMsg, data: statusData{uint32(protocol), NetworkId, td, currentBlock, common.Hash{3}},
+ code: StatusMsg, data: statusData{uint32(protocol), uint32(DefaultConfig.NetworkId), td, currentBlock, common.Hash{3}},
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis[:8]),
},
}
@@ -93,7 +94,7 @@ func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) }
func testRecvTransactions(t *testing.T, protocol int) {
txAdded := make(chan []*types.Transaction)
- pm := newTestProtocolManagerMust(t, false, 0, nil, txAdded)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded)
pm.acceptTxs = 1 // mark synced to accept transactions
p, _ := newTestPeer("peer", protocol, pm, true)
defer pm.Stop()
@@ -120,7 +121,7 @@ func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) }
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
func testSendTransactions(t *testing.T, protocol int) {
- pm := newTestProtocolManagerMust(t, false, 0, nil, nil)
+ pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
defer pm.Stop()
// Fill the pool with big transactions.
diff --git a/eth/sync_test.go b/eth/sync_test.go
index 198ffaf27..9eaa1156f 100644
--- a/eth/sync_test.go
+++ b/eth/sync_test.go
@@ -21,6 +21,7 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
)
@@ -29,12 +30,12 @@ import (
// imported into the blockchain.
func TestFastSyncDisabling(t *testing.T) {
// Create a pristine protocol manager, check that fast sync is left enabled
- pmEmpty := newTestProtocolManagerMust(t, true, 0, nil, nil)
+ pmEmpty := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil)
if atomic.LoadUint32(&pmEmpty.fastSync) == 0 {
t.Fatalf("fast sync disabled on pristine blockchain")
}
// Create a full protocol manager, check that fast sync gets disabled
- pmFull := newTestProtocolManagerMust(t, true, 1024, nil, nil)
+ pmFull := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil)
if atomic.LoadUint32(&pmFull.fastSync) == 1 {
t.Fatalf("fast sync not disabled on non-empty blockchain")
}
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index aec8eb8bf..c16163ace 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -30,6 +30,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
@@ -54,6 +55,7 @@ type Service struct {
server *p2p.Server // Peer-to-peer server to retrieve networking infos
eth *eth.Ethereum // Full Ethereum service if monitoring a full node
les *les.LightEthereum // Light Ethereum service if monitoring a light node
+ engine consensus.Engine // Consensus engine to retrieve variadic block fields
node string // Name of the node to display on the monitoring page
pass string // Password to authorize access to the monitoring page
@@ -72,9 +74,16 @@ func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Servic
return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url)
}
// Assemble and return the stats service
+ var engine consensus.Engine
+ if ethServ != nil {
+ engine = ethServ.Engine()
+ } else {
+ engine = lesServ.Engine()
+ }
return &Service{
eth: ethServ,
les: lesServ,
+ engine: engine,
node: parts[1],
pass: parts[3],
host: parts[4],
@@ -493,12 +502,14 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats {
td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
}
// Assemble and return the block stats
+ author, _ := s.engine.Author(header)
+
return &blockStats{
Number: header.Number,
Hash: header.Hash(),
ParentHash: header.ParentHash,
Timestamp: header.Time,
- Miner: header.Coinbase,
+ Miner: author,
GasUsed: new(big.Int).Set(header.GasUsed),
GasLimit: new(big.Int).Set(header.GasLimit),
Diff: header.Difficulty.String(),
diff --git a/event/example_feed_test.go b/event/example_feed_test.go
index 63436b226..9b5ad50df 100644
--- a/event/example_feed_test.go
+++ b/event/example_feed_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/event/example_scope_test.go b/event/example_scope_test.go
index c517a8324..825a8deea 100644
--- a/event/example_scope_test.go
+++ b/event/example_scope_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/event/example_subscription_test.go b/event/example_subscription_test.go
index de1126689..5c76b55d9 100644
--- a/event/example_subscription_test.go
+++ b/event/example_subscription_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/event/subscription.go b/event/subscription.go
index 02d7b9d7d..d03f46507 100644
--- a/event/subscription.go
+++ b/event/subscription.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/event/subscription_test.go b/event/subscription_test.go
index aa6d98984..5b8a2c8ed 100644
--- a/event/subscription_test.go
+++ b/event/subscription_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 50cd3801b..42bf26613 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -72,7 +72,7 @@ type State interface {
GetNonce(ctx context.Context, addr common.Address) (uint64, error)
}
-func GetAPIs(apiBackend Backend, solcPath string) []rpc.API {
+func GetAPIs(apiBackend Backend) []rpc.API {
return []rpc.API{
{
Namespace: "eth",
diff --git a/internal/ethapi/tracer.go b/internal/ethapi/tracer.go
index fe2685375..d34363564 100644
--- a/internal/ethapi/tracer.go
+++ b/internal/ethapi/tracer.go
@@ -23,6 +23,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/robertkrimen/otto"
)
@@ -164,20 +165,53 @@ func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value {
return value
}
+// contractWrapper provides a JS wrapper around vm.Contract
+type contractWrapper struct {
+ contract *vm.Contract
+}
+
+func (c *contractWrapper) caller() common.Address {
+ return c.contract.Caller()
+}
+
+func (c *contractWrapper) address() common.Address {
+ return c.contract.Address()
+}
+
+func (c *contractWrapper) value() *big.Int {
+ return c.contract.Value()
+}
+
+func (c *contractWrapper) calldata() []byte {
+ return c.contract.Input
+}
+
+func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value {
+ value, _ := vm.ToValue(c)
+ obj := value.Object()
+ obj.Set("caller", c.caller)
+ obj.Set("address", c.address)
+ obj.Set("value", c.value)
+ obj.Set("calldata", c.calldata)
+ return value
+}
+
// JavascriptTracer provides an implementation of Tracer that evaluates a
// Javascript function for each VM execution step.
type JavascriptTracer struct {
- vm *otto.Otto // Javascript VM instance
- traceobj *otto.Object // User-supplied object to call
- log map[string]interface{} // (Reusable) map for the `log` arg to `step`
- logvalue otto.Value // JS view of `log`
- memory *memoryWrapper // Wrapper around the VM memory
- memvalue otto.Value // JS view of `memory`
- stack *stackWrapper // Wrapper around the VM stack
- stackvalue otto.Value // JS view of `stack`
- db *dbWrapper // Wrapper around the VM environment
- dbvalue otto.Value // JS view of `db`
- err error // Error, if one has occurred
+ vm *otto.Otto // Javascript VM instance
+ traceobj *otto.Object // User-supplied object to call
+ log map[string]interface{} // (Reusable) map for the `log` arg to `step`
+ logvalue otto.Value // JS view of `log`
+ memory *memoryWrapper // Wrapper around the VM memory
+ memvalue otto.Value // JS view of `memory`
+ stack *stackWrapper // Wrapper around the VM stack
+ stackvalue otto.Value // JS view of `stack`
+ db *dbWrapper // Wrapper around the VM environment
+ dbvalue otto.Value // JS view of `db`
+ contract *contractWrapper // Wrapper around the contract object
+ contractvalue otto.Value // JS view of `contract`
+ err error // Error, if one has occurred
}
// NewJavascriptTracer instantiates a new JavascriptTracer instance.
@@ -189,6 +223,7 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
// Set up builtins for this environment
vm.Set("big", &fakeBig{})
+ vm.Set("toHex", hexutil.Encode)
jstracer, err := vm.Object("(" + code + ")")
if err != nil {
@@ -220,19 +255,22 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
mem := &memoryWrapper{}
stack := &stackWrapper{}
db := &dbWrapper{}
+ contract := &contractWrapper{}
return &JavascriptTracer{
- vm: vm,
- traceobj: jstracer,
- log: log,
- logvalue: logvalue,
- memory: mem,
- memvalue: mem.toValue(vm),
- stack: stack,
- stackvalue: stack.toValue(vm),
- db: db,
- dbvalue: db.toValue(vm),
- err: nil,
+ vm: vm,
+ traceobj: jstracer,
+ log: log,
+ logvalue: logvalue,
+ memory: mem,
+ memvalue: mem.toValue(vm),
+ stack: stack,
+ stackvalue: stack.toValue(vm),
+ db: db,
+ dbvalue: db.toValue(vm),
+ contract: contract,
+ contractvalue: contract.toValue(vm),
+ err: nil,
}, nil
}
@@ -283,6 +321,7 @@ func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
jst.memory.memory = memory
jst.stack.stack = stack
jst.db.db = env.StateDB
+ jst.contract.contract = contract
ocw := &opCodeWrapper{op}
@@ -292,6 +331,7 @@ func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
jst.log["gasPrice"] = cost
jst.log["memory"] = jst.memvalue
jst.log["stack"] = jst.stackvalue
+ jst.log["contract"] = jst.contractvalue
jst.log["depth"] = depth
jst.log["account"] = contract.Address()
jst.log["err"] = err
diff --git a/internal/guide/guide.go b/internal/guide/guide.go
index 44a0bc484..3e9416f03 100644
--- a/internal/guide/guide.go
+++ b/internal/guide/guide.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index dd325100f..72c2bd996 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -30,10 +30,8 @@ var Modules = map[string]string{
"shh": Shh_JS,
"swarmfs": SWARMFS_JS,
"txpool": TxPool_JS,
-
}
-
const Chequebook_JS = `
web3._extend({
property: 'chequebook',
@@ -77,6 +75,11 @@ web3._extend({
params: 1,
inputFormatter: [null]
}),
+ new web3._extend.Method({
+ name: 'getSnapshotAtHash',
+ call: 'clique_getSnapshotAtHash',
+ params: 1
+ }),
new web3._extend.Method({
name: 'getSigners',
call: 'clique_getSigners',
@@ -84,6 +87,11 @@ web3._extend({
inputFormatter: [null]
}),
new web3._extend.Method({
+ name: 'getSignersAtHash',
+ call: 'clique_getSignersAtHash',
+ params: 1
+ }),
+ new web3._extend.Method({
name: 'propose',
call: 'clique_propose',
params: 2
@@ -136,11 +144,6 @@ web3._extend({
params: 2
}),
new web3._extend.Method({
- name: 'setSolc',
- call: 'admin_setSolc',
- params: 1
- }),
- new web3._extend.Method({
name: 'startRPC',
call: 'admin_startRPC',
params: 4,
diff --git a/les/api_backend.go b/les/api_backend.go
index 67de3bcd5..7d69046de 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -100,7 +100,7 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state etha
from.SetBalance(math.MaxBig256)
vmstate := light.NewVMState(ctx, stateDb)
- context := core.NewEVMContext(msg, header, b.eth.blockchain)
+ context := core.NewEVMContext(msg, header, b.eth.blockchain, nil)
return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil
}
diff --git a/les/backend.go b/les/backend.go
index 3aad16fa0..783e6e94e 100644
--- a/les/backend.go
+++ b/les/backend.go
@@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
@@ -61,8 +60,6 @@ type LightEthereum struct {
eventMux *event.TypeMux
engine consensus.Engine
accountManager *accounts.Manager
- solcPath string
- solc *compiler.Solidity
netVersionId int
netRPCService *ethapi.PublicNetAPI
@@ -91,7 +88,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
engine: eth.CreateConsensusEngine(ctx, config, chainConfig, chainDb),
shutdownChan: make(chan bool),
netVersionId: config.NetworkId,
- solcPath: config.SolcPath,
}
if eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.engine, eth.eventMux); err != nil {
return nil, err
@@ -104,17 +100,17 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
}
eth.txPool = light.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain, eth.relay)
- if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.LightMode, config.NetworkId, eth.eventMux, eth.engine, eth.blockchain, nil, chainDb, odr, relay); err != nil {
+ lightSync := config.SyncMode == downloader.LightSync
+ if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, lightSync, config.NetworkId, eth.eventMux, eth.engine, eth.blockchain, nil, chainDb, odr, relay); err != nil {
return nil, err
}
relay.ps = eth.protocolManager.peers
relay.reqDist = eth.protocolManager.reqDist
eth.ApiBackend = &LesApiBackend{eth, nil}
- gpoParams := gasprice.Config{
- Blocks: config.GpoBlocks,
- Percentile: config.GpoPercentile,
- Default: config.GasPrice,
+ gpoParams := config.GPO
+ if gpoParams.Default == nil {
+ gpoParams.Default = config.GasPrice
}
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
return eth, nil
@@ -145,7 +141,7 @@ func (s *LightDummyAPI) Mining() bool {
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *LightEthereum) APIs() []rpc.API {
- return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{
+ return append(ethapi.GetAPIs(s.ApiBackend), []rpc.API{
{
Namespace: "eth",
Version: "1.0",
@@ -176,6 +172,7 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) {
func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain }
func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool }
+func (s *LightEthereum) Engine() consensus.Engine { return s.engine }
func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux }
diff --git a/les/distributor.go b/les/distributor.go
index c59b36146..71afe2b73 100644
--- a/les/distributor.go
+++ b/les/distributor.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/les/distributor_test.go b/les/distributor_test.go
index f2eb80729..ae184b21b 100644
--- a/les/distributor_test.go
+++ b/les/distributor_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/les/odr_test.go b/les/odr_test.go
index 6b074f1a2..532de4d80 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -123,7 +123,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
- context := core.NewEVMContext(msg, header, bc)
+ context := core.NewEVMContext(msg, header, bc, nil)
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
@@ -141,7 +141,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
- context := core.NewEVMContext(msg, header, lc)
+ context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
//vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
diff --git a/les/peer.go b/les/peer.go
index 4793da296..f45605593 100644
--- a/les/peer.go
+++ b/les/peer.go
@@ -391,9 +391,10 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version)
}
if server != nil {
- if recv.get("serveStateSince", nil) == nil {
+ // until we have a proper peer connectivity API, allow LES connection to other servers
+ /*if recv.get("serveStateSince", nil) == nil {
return errResp(ErrUselessPeer, "wanted client, got server")
- }
+ }*/
p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams)
} else {
if recv.get("serveChainSince", nil) != nil {
diff --git a/light/lightchain.go b/light/lightchain.go
index 4073e39e5..e8fd0ba5e 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -213,6 +213,9 @@ func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
// Accessors
+// Engine retrieves the light chain's consensus engine.
+func (bc *LightChain) Engine() consensus.Engine { return bc.engine }
+
// Genesis returns the genesis block
func (bc *LightChain) Genesis() *types.Block {
return bc.genesisBlock
diff --git a/light/odr_test.go b/light/odr_test.go
index ca33db246..576e3abc9 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -175,7 +175,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
- context := core.NewEVMContext(msg, header, bc)
+ context := core.NewEVMContext(msg, header, bc, nil)
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
@@ -191,7 +191,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
from.SetBalance(math.MaxBig256)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
- context := core.NewEVMContext(msg, header, lc)
+ context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
diff --git a/miner/worker.go b/miner/worker.go
index 8a67b12a6..01241b3f3 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -252,7 +252,7 @@ func (self *worker) update() {
txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
txset := types.NewTransactionsByPriceAndNonce(txs)
- self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain)
+ self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain, self.coinbase)
self.currentMu.Unlock()
}
}
@@ -460,7 +460,7 @@ func (self *worker) commitNewWork() {
return
}
txs := types.NewTransactionsByPriceAndNonce(pending)
- work.commitTransactions(self.mux, txs, self.gasPrice, self.chain)
+ work.commitTransactions(self.mux, txs, self.gasPrice, self.chain, self.coinbase)
self.eth.TxPool().RemoveBatch(work.lowGasTxs)
self.eth.TxPool().RemoveBatch(work.failedTxs)
@@ -515,7 +515,7 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
return nil
}
-func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) {
+func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain, coinbase common.Address) {
gp := new(core.GasPool).AddGas(env.header.GasLimit)
var coalescedLogs []*types.Log
@@ -553,7 +553,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
// Start executing the transaction
env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount)
- err, logs := env.commitTransaction(tx, bc, gp)
+ err, logs := env.commitTransaction(tx, bc, coinbase, gp)
switch err {
case core.ErrGasLimitReached:
// Pop the current out-of-gas transaction without shifting in the next from the account
@@ -594,10 +594,10 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
}
}
-func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, []*types.Log) {
+func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {
snap := env.state.Snapshot()
- receipt, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
+ receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
if err != nil {
env.state.RevertToSnapshot(snap)
return err, nil
diff --git a/mobile/geth.go b/mobile/geth.go
index 86034df98..be04e4603 100644
--- a/mobile/geth.go
+++ b/mobile/geth.go
@@ -22,15 +22,16 @@ package geth
import (
"encoding/json"
"fmt"
- "math/big"
"path/filepath"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethstats"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
@@ -108,17 +109,19 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
}
// Create the empty networking stack
nodeConf := &node.Config{
- Name: clientIdentifier,
- Version: params.Version,
- DataDir: datadir,
- KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
- NoDiscovery: true,
- DiscoveryV5: true,
- DiscoveryV5Addr: ":0",
- BootstrapNodesV5: config.BootstrapNodes.nodes,
- ListenAddr: ":0",
- NAT: nat.Any(),
- MaxPeers: config.MaxPeers,
+ Name: clientIdentifier,
+ Version: params.Version,
+ DataDir: datadir,
+ KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
+ P2P: p2p.Config{
+ NoDiscovery: true,
+ DiscoveryV5: true,
+ DiscoveryV5Addr: ":0",
+ BootstrapNodesV5: config.BootstrapNodes.nodes,
+ ListenAddr: ":0",
+ NAT: nat.Any(),
+ MaxPeers: config.MaxPeers,
+ },
}
rawStack, err := node.New(nodeConf)
if err != nil {
@@ -142,20 +145,13 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
}
// Register the Ethereum protocol if requested
if config.EthereumEnabled {
- ethConf := &eth.Config{
- Genesis: genesis,
- LightMode: true,
- DatabaseCache: config.EthereumDatabaseCache,
- NetworkId: config.EthereumNetworkID,
- GasPrice: new(big.Int).SetUint64(20 * params.Shannon),
- GpoBlocks: 10,
- GpoPercentile: 50,
- EthashCacheDir: "ethash",
- EthashCachesInMem: 2,
- EthashCachesOnDisk: 3,
- }
+ ethConf := eth.DefaultConfig
+ ethConf.Genesis = genesis
+ ethConf.SyncMode = downloader.LightSync
+ ethConf.NetworkId = config.EthereumNetworkID
+ ethConf.DatabaseCache = config.EthereumDatabaseCache
if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- return les.New(ctx, ethConf)
+ return les.New(ctx, &ethConf)
}); err != nil {
return nil, fmt.Errorf("ethereum init: %v", err)
}
diff --git a/mobile/p2p.go b/mobile/p2p.go
index 8d21639e5..a80d9fff2 100644
--- a/mobile/p2p.go
+++ b/mobile/p2p.go
@@ -11,7 +11,7 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
-// You should have received pi copy of the GNU Lesser General Public License
+// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains wrappers for the p2p package.
diff --git a/mobile/primitives.go b/mobile/primitives.go
index 54b25df59..5c6617fa4 100644
--- a/mobile/primitives.go
+++ b/mobile/primitives.go
@@ -11,7 +11,7 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
-// You should have received s copy of the GNU Lesser General Public License
+// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains various wrappers for primitive types.
diff --git a/node/api.go b/node/api.go
index 3c451fc8a..570cb9d98 100644
--- a/node/api.go
+++ b/node/api.go
@@ -92,8 +92,13 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis
if port == nil {
port = &api.node.config.HTTPPort
}
- if cors == nil {
- cors = &api.node.config.HTTPCors
+
+ allowedOrigins := api.node.config.HTTPCors
+ if cors != nil {
+ allowedOrigins = nil
+ for _, origin := range strings.Split(*cors, ",") {
+ allowedOrigins = append(allowedOrigins, strings.TrimSpace(origin))
+ }
}
modules := api.node.httpWhitelist
@@ -104,7 +109,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis
}
}
- if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, *cors); err != nil {
+ if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins); err != nil {
return false, err
}
return true, nil
@@ -141,8 +146,13 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str
if port == nil {
port = &api.node.config.WSPort
}
- if allowedOrigins == nil {
- allowedOrigins = &api.node.config.WSOrigins
+
+ origins := api.node.config.WSOrigins
+ if allowedOrigins != nil {
+ origins = nil
+ for _, origin := range strings.Split(*allowedOrigins, ",") {
+ origins = append(origins, strings.TrimSpace(origin))
+ }
}
modules := api.node.config.WSModules
@@ -153,7 +163,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str
}
}
- if err := api.node.startWS(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, *allowedOrigins); err != nil {
+ if err := api.node.startWS(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, origins); err != nil {
return false, err
}
return true, nil
diff --git a/node/config.go b/node/config.go
index b060b05f2..1bab4c574 100644
--- a/node/config.go
+++ b/node/config.go
@@ -20,7 +20,6 @@ import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
- "net"
"os"
"path/filepath"
"runtime"
@@ -32,10 +31,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
- "github.com/ethereum/go-ethereum/p2p/discv5"
- "github.com/ethereum/go-ethereum/p2p/nat"
- "github.com/ethereum/go-ethereum/p2p/netutil"
)
var (
@@ -53,14 +50,14 @@ type Config struct {
// Name sets the instance name of the node. It must not contain the / character and is
// used in the devp2p node identifier. The instance name of geth is "geth". If no
// value is specified, the basename of the current executable is used.
- Name string
+ Name string `toml:"-"`
// UserIdent, if set, is used as an additional component in the devp2p node identifier.
- UserIdent string
+ UserIdent string `toml:",omitempty"`
// Version should be set to the version number of the program. It is used
// in the devp2p node identifier.
- Version string
+ Version string `toml:"-"`
// DataDir is the file system folder the node should use for any data storage
// requirements. The configured data directory will not be directly shared with
@@ -69,6 +66,9 @@ type Config struct {
// in memory.
DataDir string
+ // Configuration of peer-to-peer networking.
+ P2P p2p.Config
+
// KeyStoreDir is the file system folder that contains private keys. The directory can
// be specified as a relative path, in which case it is resolved relative to the
// current directory.
@@ -76,106 +76,55 @@ type Config struct {
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of
// DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory
// is created by New and destroyed when the node is stopped.
- KeyStoreDir string
+ KeyStoreDir string `toml:",omitempty"`
// UseLightweightKDF lowers the memory and CPU requirements of the key store
// scrypt KDF at the expense of security.
- UseLightweightKDF bool
+ UseLightweightKDF bool `toml:",omitempty"`
// IPCPath is the requested location to place the IPC endpoint. If the path is
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
// relative), then that specific path is enforced. An empty path disables IPC.
- IPCPath string
-
- // This field should be a valid secp256k1 private key that will be used for both
- // remote peer identification as well as network traffic encryption. If no key
- // is configured, the preset one is loaded from the data dir, generating it if
- // needed.
- PrivateKey *ecdsa.PrivateKey
-
- // NoDiscovery specifies whether the peer discovery mechanism should be started
- // or not. Disabling is usually useful for protocol debugging (manual topology).
- NoDiscovery bool
-
- // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
- // protocol should be started or not.
- DiscoveryV5 bool
-
- // Listener address for the V5 discovery protocol UDP traffic.
- DiscoveryV5Addr string
-
- // Restrict communication to white listed IP networks.
- // The whitelist only applies when non-nil.
- NetRestrict *netutil.Netlist
-
- // BootstrapNodes used to establish connectivity with the rest of the network.
- BootstrapNodes []*discover.Node
-
- // BootstrapNodesV5 used to establish connectivity with the rest of the network
- // using the V5 discovery protocol.
- BootstrapNodesV5 []*discv5.Node
-
- // Network interface address on which the node should listen for inbound peers.
- ListenAddr string
-
- // If set to a non-nil value, the given NAT port mapper is used to make the
- // listening port available to the Internet.
- NAT nat.Interface
-
- // If Dialer is set to a non-nil value, the given Dialer is used to dial outbound
- // peer connections.
- Dialer *net.Dialer
-
- // If NoDial is true, the node will not dial any peers.
- NoDial bool
-
- // MaxPeers is the maximum number of peers that can be connected. If this is
- // set to zero, then only the configured static and trusted peers can connect.
- MaxPeers int
-
- // MaxPendingPeers is the maximum number of peers that can be pending in the
- // handshake phase, counted separately for inbound and outbound connections.
- // Zero defaults to preset values.
- MaxPendingPeers int
+ IPCPath string `toml:",omitempty"`
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
// field is empty, no HTTP API endpoint will be started.
- HTTPHost string
+ HTTPHost string `toml:",omitempty"`
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful
// for ephemeral nodes).
- HTTPPort int
+ HTTPPort int `toml:",omitempty"`
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients.
- HTTPCors string
+ HTTPCors []string `toml:",omitempty"`
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- HTTPModules []string
+ HTTPModules []string `toml:",omitempty"`
// WSHost is the host interface on which to start the websocket RPC server. If
// this field is empty, no websocket API endpoint will be started.
- WSHost string
+ WSHost string `toml:",omitempty"`
// WSPort is the TCP port number on which to start the websocket RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful for
// ephemeral nodes).
- WSPort int
+ WSPort int `toml:",omitempty"`
// WSOrigins is the list of domain to accept websocket requests from. Please be
// aware that the server can only act upon the HTTP request the client sends and
// cannot verify the validity of the request header.
- WSOrigins string
+ WSOrigins []string `toml:",omitempty"`
// WSModules is a list of API modules to expose via the websocket RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- WSModules []string
+ WSModules []string `toml:",omitempty"`
}
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
@@ -326,8 +275,8 @@ func (c *Config) instanceDir() string {
// data folder. If no key can be found, a new one is generated.
func (c *Config) NodeKey() *ecdsa.PrivateKey {
// Use any specifically configured key.
- if c.PrivateKey != nil {
- return c.PrivateKey
+ if c.P2P.PrivateKey != nil {
+ return c.P2P.PrivateKey
}
// Generate ephemeral key if no datadir is being used.
if c.DataDir == "" {
diff --git a/node/config_test.go b/node/config_test.go
index c0eda72c2..b81d3d612 100644
--- a/node/config_test.go
+++ b/node/config_test.go
@@ -25,6 +25,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/p2p"
)
// Tests that datadirs can be successfully created, be them manually configured
@@ -109,7 +110,7 @@ func TestNodeKeyPersistency(t *testing.T) {
if err != nil {
t.Fatalf("failed to generate one-shot node key: %v", err)
}
- config := &Config{Name: "unit-test", DataDir: dir, PrivateKey: key}
+ config := &Config{Name: "unit-test", DataDir: dir, P2P: p2p.Config{PrivateKey: key}}
config.NodeKey()
if _, err := os.Stat(filepath.Join(keyfile)); err == nil {
t.Fatalf("one-shot node key persisted to data directory")
diff --git a/node/defaults.go b/node/defaults.go
index bfe257c8e..d4e148683 100644
--- a/node/defaults.go
+++ b/node/defaults.go
@@ -21,16 +21,32 @@ import (
"os/user"
"path/filepath"
"runtime"
+
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/nat"
)
const (
- DefaultIPCSocket = "geth.ipc" // Default (relative) name of the IPC RPC socket
- DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
- DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
- DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
- DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
+ DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
+ DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
+ DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
+ DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
)
+// DefaultConfig contains reasonable default settings.
+var DefaultConfig = Config{
+ DataDir: DefaultDataDir(),
+ HTTPPort: DefaultHTTPPort,
+ HTTPModules: []string{"net", "web3"},
+ WSPort: DefaultWSPort,
+ WSModules: []string{"net", "web3"},
+ P2P: p2p.Config{
+ ListenAddr: ":30303",
+ MaxPeers: 25,
+ NAT: nat.Any(),
+ },
+}
+
// DefaultDataDir is the default data directory to use for the databases and other
// persistence requirements.
func DefaultDataDir() string {
diff --git a/node/node.go b/node/node.go
index afb676b7f..dc2ff0701 100644
--- a/node/node.go
+++ b/node/node.go
@@ -153,24 +153,17 @@ func (n *Node) Start() error {
// Initialize the p2p server. This creates the node key and
// discovery databases.
- n.serverConfig = p2p.Config{
- PrivateKey: n.config.NodeKey(),
- Name: n.config.NodeName(),
- Discovery: !n.config.NoDiscovery,
- DiscoveryV5: n.config.DiscoveryV5,
- DiscoveryV5Addr: n.config.DiscoveryV5Addr,
- BootstrapNodes: n.config.BootstrapNodes,
- BootstrapNodesV5: n.config.BootstrapNodesV5,
- StaticNodes: n.config.StaticNodes(),
- TrustedNodes: n.config.TrusterNodes(),
- NodeDatabase: n.config.NodeDB(),
- ListenAddr: n.config.ListenAddr,
- NetRestrict: n.config.NetRestrict,
- NAT: n.config.NAT,
- Dialer: n.config.Dialer,
- NoDial: n.config.NoDial,
- MaxPeers: n.config.MaxPeers,
- MaxPendingPeers: n.config.MaxPendingPeers,
+ n.serverConfig = n.config.P2P
+ n.serverConfig.PrivateKey = n.config.NodeKey()
+ n.serverConfig.Name = n.config.NodeName()
+ if n.serverConfig.StaticNodes == nil {
+ n.serverConfig.StaticNodes = n.config.StaticNodes()
+ }
+ if n.serverConfig.TrustedNodes == nil {
+ n.serverConfig.TrustedNodes = n.config.TrusterNodes()
+ }
+ if n.serverConfig.NodeDatabase == "" {
+ n.serverConfig.NodeDatabase = n.config.NodeDB()
}
running := &p2p.Server{Config: n.serverConfig}
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
@@ -379,7 +372,7 @@ func (n *Node) stopIPC() {
}
// startHTTP initializes and starts the HTTP RPC endpoint.
-func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors string) error {
+func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string) error {
// Short circuit if the HTTP endpoint isn't being exposed
if endpoint == "" {
return nil
@@ -433,7 +426,7 @@ func (n *Node) stopHTTP() {
}
// startWS initializes and starts the websocket RPC endpoint.
-func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins string) error {
+func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string) error {
// Short circuit if the WS endpoint isn't being exposed
if endpoint == "" {
return nil
diff --git a/node/node_example_test.go b/node/node_example_test.go
index d2872cf38..ee06f4065 100644
--- a/node/node_example_test.go
+++ b/node/node_example_test.go
@@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -42,23 +41,8 @@ func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starti
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
func ExampleService() {
- // Create a network node to run protocols with the default values. The below list
- // is only used to display each of the configuration options. All of these could
- // have been omitted if the default behavior is desired.
- nodeConfig := &node.Config{
- DataDir: "", // Empty uses ephemeral storage
- PrivateKey: nil, // Nil generates a node key on the fly
- Name: "", // Any textual node name is allowed
- NoDiscovery: false, // Can disable discovering remote nodes
- BootstrapNodes: []*discover.Node{}, // List of bootstrap nodes to use
- ListenAddr: ":0", // Network interface to listen on
- NAT: nil, // UPnP port mapper to use for crossing firewalls
- Dialer: nil, // Custom dialer to use for establishing peer connections
- NoDial: false, // Can prevent this node from dialing out
- MaxPeers: 0, // Number of peers to allow
- MaxPendingPeers: 0, // Number of peers allowed to handshake concurrently
- }
- stack, err := node.New(nodeConfig)
+ // Create a network node to run protocols with the default values.
+ stack, err := node.New(&node.Config{})
if err != nil {
log.Fatalf("Failed to create network node: %v", err)
}
diff --git a/node/node_test.go b/node/node_test.go
index 408d4cfcb..2880efa61 100644
--- a/node/node_test.go
+++ b/node/node_test.go
@@ -35,8 +35,8 @@ var (
func testNodeConfig() *Config {
return &Config{
- PrivateKey: testNodeKey,
- Name: "test node",
+ Name: "test node",
+ P2P: p2p.Config{PrivateKey: testNodeKey},
}
}
diff --git a/p2p/dial.go b/p2p/dial.go
index bb3befab2..b77971396 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -38,6 +38,10 @@ const (
// once every few seconds.
lookupInterval = 4 * time.Second
+ // If no peers are found for this amount of time, the initial bootnodes are
+ // attempted to be connected.
+ fallbackInterval = 20 * time.Second
+
// Endpoint resolution is throttled with bounded backoff.
initialResolveDelay = 60 * time.Second
maxResolveDelay = time.Hour
@@ -57,6 +61,9 @@ type dialstate struct {
randomNodes []*discover.Node // filled from Table
static map[discover.NodeID]*dialTask
hist *dialHistory
+
+ start time.Time // time when the dialer was first used
+ bootnodes []*discover.Node // default dials when there are no peers
}
type discoverTable interface {
@@ -102,16 +109,18 @@ type waitExpireTask struct {
time.Duration
}
-func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
+func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
s := &dialstate{
maxDynDials: maxdyn,
ntab: ntab,
netrestrict: netrestrict,
static: make(map[discover.NodeID]*dialTask),
dialing: make(map[discover.NodeID]connFlag),
+ bootnodes: make([]*discover.Node, len(bootnodes)),
randomNodes: make([]*discover.Node, maxdyn/2),
hist: new(dialHistory),
}
+ copy(s.bootnodes, bootnodes)
for _, n := range static {
s.addStatic(n)
}
@@ -130,6 +139,10 @@ func (s *dialstate) removeStatic(n *discover.Node) {
}
func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
+ if s.start == (time.Time{}) {
+ s.start = now
+ }
+
var newtasks []task
addDial := func(flag connFlag, n *discover.Node) bool {
if err := s.checkDial(n, peers); err != nil {
@@ -169,7 +182,18 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now
newtasks = append(newtasks, t)
}
}
-
+ // If we don't have any peers whatsoever, try to dial a random bootnode. This
+ // scenario is useful for the testnet (and private networks) where the discovery
+ // table might be full of mostly bad peers, making it hard to find good ones.
+ if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && now.Sub(s.start) > fallbackInterval {
+ bootnode := s.bootnodes[0]
+ s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...)
+ s.bootnodes = append(s.bootnodes, bootnode)
+
+ if addDial(dynDialedConn, bootnode) {
+ needDynDials--
+ }
+ }
// Use random nodes from the table for half of the necessary
// dynamic dials.
randomCandidates := needDynDials / 2
diff --git a/p2p/dial_test.go b/p2p/dial_test.go
index c850233db..08e863bae 100644
--- a/p2p/dial_test.go
+++ b/p2p/dial_test.go
@@ -87,7 +87,7 @@ func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf,
// This test checks that dynamic dials are launched from discovery results.
func TestDialStateDynDial(t *testing.T) {
runDialTest(t, dialtest{
- init: newDialState(nil, fakeTable{}, 5, nil),
+ init: newDialState(nil, nil, fakeTable{}, 5, nil),
rounds: []round{
// A discovery query is launched.
{
@@ -219,6 +219,94 @@ func TestDialStateDynDial(t *testing.T) {
})
}
+// Tests that bootnodes are dialed if no peers are connectd, but not otherwise.
+func TestDialStateDynDialBootnode(t *testing.T) {
+ bootnodes := []*discover.Node{
+ {ID: uintID(1)},
+ {ID: uintID(2)},
+ {ID: uintID(3)},
+ }
+ table := fakeTable{
+ {ID: uintID(4)},
+ {ID: uintID(5)},
+ {ID: uintID(6)},
+ {ID: uintID(7)},
+ {ID: uintID(8)},
+ }
+ runDialTest(t, dialtest{
+ init: newDialState(nil, bootnodes, table, 5, nil),
+ rounds: []round{
+ // 2 dynamic dials attempted, bootnodes pending fallback interval
+ {
+ new: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+ &discoverTask{},
+ },
+ },
+ // No dials succeed, bootnodes still pending fallback interval
+ {
+ done: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+ },
+ },
+ // No dials succeed, bootnodes still pending fallback interval
+ {},
+ // No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached
+ {
+ new: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+ },
+ },
+ // No dials succeed, 2nd bootnode is attempted
+ {
+ done: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+ },
+ new: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
+ },
+ },
+ // No dials succeed, 3rd bootnode is attempted
+ {
+ done: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
+ },
+ new: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
+ },
+ },
+ // No dials succeed, 1st bootnode is attempted again, expired random nodes retried
+ {
+ done: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
+ },
+ new: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+ },
+ },
+ // Random dial succeeds, no more bootnodes are attempted
+ {
+ peers: []*Peer{
+ {rw: &conn{flags: dynDialedConn, id: uintID(4)}},
+ },
+ done: []task{
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+ },
+ },
+ },
+ })
+}
+
func TestDialStateDynDialFromTable(t *testing.T) {
// This table always returns the same random nodes
// in the order given below.
@@ -234,7 +322,7 @@ func TestDialStateDynDialFromTable(t *testing.T) {
}
runDialTest(t, dialtest{
- init: newDialState(nil, table, 10, nil),
+ init: newDialState(nil, nil, table, 10, nil),
rounds: []round{
// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
{
@@ -332,7 +420,7 @@ func TestDialStateNetRestrict(t *testing.T) {
restrict.Add("127.0.2.0/24")
runDialTest(t, dialtest{
- init: newDialState(nil, table, 10, restrict),
+ init: newDialState(nil, nil, table, 10, restrict),
rounds: []round{
{
new: []task{
@@ -355,7 +443,7 @@ func TestDialStateStaticDial(t *testing.T) {
}
runDialTest(t, dialtest{
- init: newDialState(wantStatic, fakeTable{}, 0, nil),
+ init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
rounds: []round{
// Static dials are launched for the nodes that
// aren't yet connected.
@@ -436,7 +524,7 @@ func TestDialStateCache(t *testing.T) {
}
runDialTest(t, dialtest{
- init: newDialState(wantStatic, fakeTable{}, 0, nil),
+ init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
rounds: []round{
// Static dials are launched for the nodes that
// aren't yet connected.
@@ -498,7 +586,7 @@ func TestDialStateCache(t *testing.T) {
func TestDialResolve(t *testing.T) {
resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
table := &resolveMock{answer: resolved}
- state := newDialState(nil, table, 0, nil)
+ state := newDialState(nil, nil, table, 0, nil)
// Check that the task is generated with an incomplete ID.
dest := discover.NewNode(uintID(1), nil, 0, 0)
diff --git a/p2p/discover/node.go b/p2p/discover/node.go
index 6a7ab814e..d9cbd9448 100644
--- a/p2p/discover/node.go
+++ b/p2p/discover/node.go
@@ -207,6 +207,20 @@ func MustParseNode(rawurl string) *Node {
return n
}
+// MarshalText implements encoding.TextMarshaler.
+func (n *Node) MarshalText() ([]byte, error) {
+ return []byte(n.String()), nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (n *Node) UnmarshalText(text []byte) error {
+ dec, err := ParseNode(string(text))
+ if err == nil {
+ *n = *dec
+ }
+ return err
+}
+
// NodeID is a unique identifier for each node.
// The node identifier is a marshaled elliptic curve public key.
type NodeID [NodeIDBits / 8]byte
diff --git a/p2p/discv5/node.go b/p2p/discv5/node.go
index c99b4da14..2db7a508f 100644
--- a/p2p/discv5/node.go
+++ b/p2p/discv5/node.go
@@ -215,6 +215,20 @@ func MustParseNode(rawurl string) *Node {
return n
}
+// MarshalText implements encoding.TextMarshaler.
+func (n *Node) MarshalText() ([]byte, error) {
+ return []byte(n.String()), nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (n *Node) UnmarshalText(text []byte) error {
+ dec, err := ParseNode(string(text))
+ if err == nil {
+ *n = *dec
+ }
+ return err
+}
+
// type nodeQueue []*Node
//
// // pushNew adds n to the end if it is not present.
diff --git a/p2p/netutil/net.go b/p2p/netutil/net.go
index 3c3715788..f6005afd2 100644
--- a/p2p/netutil/net.go
+++ b/p2p/netutil/net.go
@@ -84,6 +84,31 @@ func ParseNetlist(s string) (*Netlist, error) {
return &l, nil
}
+// MarshalTOML implements toml.MarshalerRec.
+func (l Netlist) MarshalTOML() interface{} {
+ list := make([]string, 0, len(l))
+ for _, net := range l {
+ list = append(list, net.String())
+ }
+ return list
+}
+
+// UnmarshalTOML implements toml.UnmarshalerRec.
+func (l *Netlist) UnmarshalTOML(fn func(interface{}) error) error {
+ var masks []string
+ if err := fn(&masks); err != nil {
+ return err
+ }
+ for _, mask := range masks {
+ _, n, err := net.ParseCIDR(mask)
+ if err != nil {
+ return err
+ }
+ *l = append(*l, *n)
+ }
+ return nil
+}
+
// Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is
// intended to be used for setting up static lists.
func (l *Netlist) Add(cidr string) {
diff --git a/p2p/server.go b/p2p/server.go
index 48b4e8be3..d7909d53a 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -58,7 +58,7 @@ var errServerStopped = errors.New("server stopped")
// Config holds Server options.
type Config struct {
// This field must be set to a valid secp256k1 private key.
- PrivateKey *ecdsa.PrivateKey
+ PrivateKey *ecdsa.PrivateKey `toml:"-"`
// MaxPeers is the maximum number of peers that can be
// connected. It must be greater than zero.
@@ -67,22 +67,22 @@ type Config struct {
// MaxPendingPeers is the maximum number of peers that can be pending in the
// handshake phase, counted separately for inbound and outbound connections.
// Zero defaults to preset values.
- MaxPendingPeers int
+ MaxPendingPeers int `toml:",omitempty"`
- // Discovery specifies whether the peer discovery mechanism should be started
- // or not. Disabling is usually useful for protocol debugging (manual topology).
- Discovery bool
+ // NoDiscovery can be used to disable the peer discovery mechanism.
+ // Disabling is useful for protocol debugging (manual topology).
+ NoDiscovery bool
// DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
// protocol should be started or not.
- DiscoveryV5 bool
+ DiscoveryV5 bool `toml:",omitempty"`
// Listener address for the V5 discovery protocol UDP traffic.
- DiscoveryV5Addr string
+ DiscoveryV5Addr string `toml:",omitempty"`
// Name sets the node name of this server.
// Use common.MakeName to create a name that follows existing conventions.
- Name string
+ Name string `toml:"-"`
// BootstrapNodes are used to establish connectivity
// with the rest of the network.
@@ -91,7 +91,7 @@ type Config struct {
// BootstrapNodesV5 are used to establish connectivity
// with the rest of the network using the V5 discovery
// protocol.
- BootstrapNodesV5 []*discv5.Node
+ BootstrapNodesV5 []*discv5.Node `toml:",omitempty"`
// Static nodes are used as pre-configured connections which are always
// maintained and re-connected on disconnects.
@@ -104,16 +104,16 @@ type Config struct {
// Connectivity can be restricted to certain IP networks.
// If this option is set to a non-nil value, only hosts which match one of the
// IP networks contained in the list are considered.
- NetRestrict *netutil.Netlist
+ NetRestrict *netutil.Netlist `toml:",omitempty"`
// NodeDatabase is the path to the database containing the previously seen
// live nodes in the network.
- NodeDatabase string
+ NodeDatabase string `toml:",omitempty"`
// Protocols should contain the protocols supported
// by the server. Matching protocols are launched for
// each peer.
- Protocols []Protocol
+ Protocols []Protocol `toml:"-"`
// If ListenAddr is set to a non-nil address, the server
// will listen for incoming connections.
@@ -126,14 +126,14 @@ type Config struct {
// If set to a non-nil value, the given NAT port mapper
// is used to make the listening port available to the
// Internet.
- NAT nat.Interface
+ NAT nat.Interface `toml:",omitempty"`
// If Dialer is set to a non-nil value, the given Dialer
// is used to dial outbound peer connections.
- Dialer *net.Dialer
+ Dialer *net.Dialer `toml:"-"`
// If NoDial is true, the server will not dial any peers.
- NoDial bool
+ NoDial bool `toml:",omitempty"`
}
// Server manages all peer connections.
@@ -370,7 +370,7 @@ func (srv *Server) Start() (err error) {
srv.peerOpDone = make(chan struct{})
// node table
- if srv.Discovery {
+ if !srv.NoDiscovery {
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict)
if err != nil {
return err
@@ -393,10 +393,10 @@ func (srv *Server) Start() (err error) {
}
dynPeers := (srv.MaxPeers + 1) / 2
- if !srv.Discovery {
+ if srv.NoDiscovery {
dynPeers = 0
}
- dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers, srv.NetRestrict)
+ dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
// handshake
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
diff --git a/params/bootnodes.go b/params/bootnodes.go
index ab7ec4659..496ab68ec 100644
--- a/params/bootnodes.go
+++ b/params/bootnodes.go
@@ -33,10 +33,10 @@ var MainnetBootnodes = []string{
}
// TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the
-// Morden test network.
+// Ropsten test network.
var TestnetBootnodes = []string{
- "enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404",
- "enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303",
+ "enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303", // US-TX
+ "enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303", // IE
}
// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the
diff --git a/params/version.go b/params/version.go
index fef360473..b46c980dd 100644
--- a/params/version.go
+++ b/params/version.go
@@ -1,27 +1,29 @@
// Copyright 2016 The go-ethereum Authors
-// This file is part of go-ethereum.
+// This file is part of the go-ethereum library.
//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
-// go-ethereum is distributed in the hope that it will be useful,
+// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Lesser General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package params
-import "fmt"
+import (
+ "fmt"
+)
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 6 // Minor version component of the current release
- VersionPatch = 0 // Patch version component of the current release
+ VersionPatch = 1 // Patch version component of the current release
VersionMeta = "unstable" // Version metadata to append to the version string
)
@@ -33,3 +35,11 @@ var Version = func() string {
}
return v
}()
+
+func VersionWithCommit(gitCommit string) string {
+ vsn := Version
+ if len(gitCommit) >= 8 {
+ vsn += "-" + gitCommit[:8]
+ }
+ return vsn
+}
diff --git a/rpc/client_test.go b/rpc/client_test.go
index 41471dcea..10d74670b 100644
--- a/rpc/client_test.go
+++ b/rpc/client_test.go
@@ -394,7 +394,7 @@ func TestClientReconnect(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- go http.Serve(l, srv.WebsocketHandler("*"))
+ go http.Serve(l, srv.WebsocketHandler([]string{"*"}))
return srv, l
}
@@ -466,7 +466,7 @@ func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client,
var hs *httptest.Server
switch transport {
case "ws":
- hs = httptest.NewUnstartedServer(srv.WebsocketHandler("*"))
+ hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"}))
case "http":
hs = httptest.NewUnstartedServer(srv)
default:
diff --git a/rpc/http.go b/rpc/http.go
index 89175b149..022f9ce8f 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -25,7 +25,6 @@ import (
"io/ioutil"
"net"
"net/http"
- "strings"
"sync"
"time"
@@ -140,8 +139,8 @@ func (t *httpReadWriteNopCloser) Close() error {
// NewHTTPServer creates a new HTTP RPC server around an API provider.
//
// Deprecated: Server implements http.Handler
-func NewHTTPServer(corsString string, srv *Server) *http.Server {
- return &http.Server{Handler: newCorsHandler(srv, corsString)}
+func NewHTTPServer(cors []string, srv *Server) *http.Server {
+ return &http.Server{Handler: newCorsHandler(srv, cors)}
}
// ServeHTTP serves JSON-RPC requests over HTTP.
@@ -162,11 +161,7 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
srv.ServeSingleRequest(codec, OptionMethodInvocation)
}
-func newCorsHandler(srv *Server, corsString string) http.Handler {
- var allowedOrigins []string
- for _, domain := range strings.Split(corsString, ",") {
- allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
- }
+func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
c := cors.New(cors.Options{
AllowedOrigins: allowedOrigins,
AllowedMethods: []string{"POST", "GET"},
diff --git a/rpc/server.go b/rpc/server.go
index 8627b5592..78df37e52 100644
--- a/rpc/server.go
+++ b/rpc/server.go
@@ -31,9 +31,7 @@ import (
const (
notificationBufferSize = 10000 // max buffered notifications before codec is closed
- MetadataApi = "rpc"
- DefaultIPCApis = "admin,debug,eth,miner,net,personal,shh,txpool,web3"
- DefaultHTTPApis = "eth,net,web3"
+ MetadataApi = "rpc"
)
// CodecOption specifies which type of messages this codec supports
diff --git a/rpc/websocket.go b/rpc/websocket.go
index 587010820..5f9593a43 100644
--- a/rpc/websocket.go
+++ b/rpc/websocket.go
@@ -36,9 +36,9 @@ import (
//
// allowedOrigins should be a comma-separated list of allowed origin URLs.
// To allow connections with any origin, pass "*".
-func (srv *Server) WebsocketHandler(allowedOrigins string) http.Handler {
+func (srv *Server) WebsocketHandler(allowedOrigins []string) http.Handler {
return websocket.Server{
- Handshake: wsHandshakeValidator(strings.Split(allowedOrigins, ",")),
+ Handshake: wsHandshakeValidator(allowedOrigins),
Handler: func(conn *websocket.Conn) {
srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions)
},
@@ -48,7 +48,7 @@ func (srv *Server) WebsocketHandler(allowedOrigins string) http.Handler {
// NewWSServer creates a new websocket RPC server around an API provider.
//
// Deprecated: use Server.WebsocketHandler
-func NewWSServer(allowedOrigins string, srv *Server) *http.Server {
+func NewWSServer(allowedOrigins []string, srv *Server) *http.Server {
return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)}
}
diff --git a/swarm/api/api.go b/swarm/api/api.go
index ba1156f7e..26a9445d5 100644
--- a/swarm/api/api.go
+++ b/swarm/api/api.go
@@ -17,7 +17,6 @@
package api
import (
- "errors"
"fmt"
"io"
"net/http"
@@ -25,9 +24,13 @@ import (
"strings"
"sync"
+ "bytes"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/storage"
+ "mime"
+ "path/filepath"
+ "time"
)
var (
@@ -59,6 +62,13 @@ func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
return
}
+// to be used only in TEST
+func (self *Api) Upload(uploadDir, index string) (hash string, err error) {
+ fs := NewFileSystem(self)
+ hash, err = fs.Upload(uploadDir, index)
+ return hash, err
+}
+
// DPA reader API
func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader {
return self.dpa.Retrieve(key)
@@ -73,25 +83,28 @@ type ErrResolve error
// DNS Resolver
func (self *Api) Resolve(uri *URI) (storage.Key, error) {
log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr))
+
+ var err error
+ if !uri.Immutable() {
+ if self.dns != nil {
+ resolved, err := self.dns.Resolve(uri.Addr)
+ if err == nil {
+ return resolved[:], nil
+ }
+ } else {
+ err = fmt.Errorf("no DNS to resolve name")
+ }
+ }
if hashMatcher.MatchString(uri.Addr) {
- log.Trace(fmt.Sprintf("addr is a hash: %q", uri.Addr))
return storage.Key(common.Hex2Bytes(uri.Addr)), nil
}
- if uri.Immutable() {
- return nil, errors.New("refusing to resolve immutable address")
- }
- if self.dns == nil {
- return nil, fmt.Errorf("unable to resolve addr %q, resolver not configured", uri.Addr)
- }
- hash, err := self.dns.Resolve(uri.Addr)
if err != nil {
- log.Warn(fmt.Sprintf("DNS error resolving addr %q: %s", uri.Addr, err))
- return nil, ErrResolve(err)
+ return nil, fmt.Errorf("'%s' does not resolve: %v but is not a content hash", uri.Addr, err)
}
- log.Trace(fmt.Sprintf("addr lookup: %v -> %v", uri.Addr, hash))
- return hash[:], nil
+ return nil, fmt.Errorf("'%s' is not a content hash", uri.Addr)
}
+
// Put provides singleton manifest creation on top of dpa store
func (self *Api) Put(content, contentType string) (storage.Key, error) {
r := strings.NewReader(content)
@@ -111,7 +124,7 @@ func (self *Api) Put(content, contentType string) (storage.Key, error) {
}
// Get uses iterative manifest retrieval and prefix matching
-// to resolve path to content using dpa retrieve
+// to resolve basePath to content using dpa retrieve
// it returns a section reader, mimeType, status and an error
func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) {
trie, err := loadManifest(self.dpa, key, nil)
@@ -160,3 +173,181 @@ func (self *Api) Modify(key storage.Key, path, contentHash, contentType string)
}
return trie.hash, nil
}
+
+func (self *Api) AddFile(mhash, path, fname string, content []byte, nameresolver bool) (storage.Key, string, error) {
+
+ uri, err := Parse("bzz:/" + mhash)
+ if err != nil {
+ return nil, "", err
+ }
+ mkey, err := self.Resolve(uri)
+ if err != nil {
+ return nil, "", err
+ }
+
+ // trim the root dir we added
+ if path[:1] == "/" {
+ path = path[1:]
+ }
+
+ entry := &ManifestEntry{
+ Path: filepath.Join(path, fname),
+ ContentType: mime.TypeByExtension(filepath.Ext(fname)),
+ Mode: 0700,
+ Size: int64(len(content)),
+ ModTime: time.Now(),
+ }
+
+ mw, err := self.NewManifestWriter(mkey, nil)
+ if err != nil {
+ return nil, "", err
+ }
+
+ fkey, err := mw.AddEntry(bytes.NewReader(content), entry)
+ if err != nil {
+ return nil, "", err
+ }
+
+ newMkey, err := mw.Store()
+ if err != nil {
+ return nil, "", err
+
+ }
+
+ return fkey, newMkey.String(), nil
+
+}
+
+func (self *Api) RemoveFile(mhash, path, fname string, nameresolver bool) (string, error) {
+
+ uri, err := Parse("bzz:/" + mhash)
+ if err != nil {
+ return "", err
+ }
+ mkey, err := self.Resolve(uri)
+ if err != nil {
+ return "", err
+ }
+
+ // trim the root dir we added
+ if path[:1] == "/" {
+ path = path[1:]
+ }
+
+ mw, err := self.NewManifestWriter(mkey, nil)
+ if err != nil {
+ return "", err
+ }
+
+ err = mw.RemoveEntry(filepath.Join(path, fname))
+ if err != nil {
+ return "", err
+ }
+
+ newMkey, err := mw.Store()
+ if err != nil {
+ return "", err
+
+ }
+
+ return newMkey.String(), nil
+}
+
+func (self *Api) AppendFile(mhash, path, fname string, existingSize int64, content []byte, oldKey storage.Key, offset int64, addSize int64, nameresolver bool) (storage.Key, string, error) {
+
+ buffSize := offset + addSize
+ if buffSize < existingSize {
+ buffSize = existingSize
+ }
+
+ buf := make([]byte, buffSize)
+
+ oldReader := self.Retrieve(oldKey)
+ io.ReadAtLeast(oldReader, buf, int(offset))
+
+ newReader := bytes.NewReader(content)
+ io.ReadAtLeast(newReader, buf[offset:], int(addSize))
+
+ if buffSize < existingSize {
+ io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize))
+ }
+
+ combinedReader := bytes.NewReader(buf)
+ totalSize := int64(len(buf))
+
+ // TODO(jmozah): to append using pyramid chunker when it is ready
+ //oldReader := self.Retrieve(oldKey)
+ //newReader := bytes.NewReader(content)
+ //combinedReader := io.MultiReader(oldReader, newReader)
+
+ uri, err := Parse("bzz:/" + mhash)
+ if err != nil {
+ return nil, "", err
+ }
+ mkey, err := self.Resolve(uri)
+ if err != nil {
+ return nil, "", err
+ }
+
+ // trim the root dir we added
+ if path[:1] == "/" {
+ path = path[1:]
+ }
+
+ mw, err := self.NewManifestWriter(mkey, nil)
+ if err != nil {
+ return nil, "", err
+ }
+
+ err = mw.RemoveEntry(filepath.Join(path, fname))
+ if err != nil {
+ return nil, "", err
+ }
+
+ entry := &ManifestEntry{
+ Path: filepath.Join(path, fname),
+ ContentType: mime.TypeByExtension(filepath.Ext(fname)),
+ Mode: 0700,
+ Size: totalSize,
+ ModTime: time.Now(),
+ }
+
+ fkey, err := mw.AddEntry(io.Reader(combinedReader), entry)
+ if err != nil {
+ return nil, "", err
+ }
+
+ newMkey, err := mw.Store()
+ if err != nil {
+ return nil, "", err
+
+ }
+
+ return fkey, newMkey.String(), nil
+
+}
+
+func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storage.Key, manifestEntryMap map[string]*manifestTrieEntry, err error) {
+
+ uri, err := Parse("bzz:/" + mhash)
+ if err != nil {
+ return nil, nil, err
+ }
+ key, err = self.Resolve(uri)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ quitC := make(chan bool)
+ rootTrie, err := loadManifest(self.dpa, key, quitC)
+ if err != nil {
+ return nil, nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err)
+ }
+
+ manifestEntryMap = map[string]*manifestTrieEntry{}
+ err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) {
+ manifestEntryMap[suffix] = entry
+ })
+
+ return key, manifestEntryMap, nil
+}
diff --git a/swarm/api/client/client.go b/swarm/api/client/client.go
index f9c3e51e8..7952d3fb6 100644
--- a/swarm/api/client/client.go
+++ b/swarm/api/client/client.go
@@ -1,18 +1,18 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of go-ethereum.
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
-// go-ethereum is distributed in the hope that it will be useful,
+// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Lesser General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package client
diff --git a/swarm/api/client/client_test.go b/swarm/api/client/client_test.go
index 4d02ceaf4..03d25049d 100644
--- a/swarm/api/client/client_test.go
+++ b/swarm/api/client/client_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/swarm/api/filesystem.go b/swarm/api/filesystem.go
index e7deaa32f..f5dc90e2e 100644
--- a/swarm/api/filesystem.go
+++ b/swarm/api/filesystem.go
@@ -68,7 +68,6 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) {
log.Debug(fmt.Sprintf("uploading '%s'", localpath))
err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error {
if (err == nil) && !info.IsDir() {
- //fmt.Printf("lp %s path %s\n", localpath, path)
if len(path) <= start {
return fmt.Errorf("Path is too short")
}
@@ -170,7 +169,7 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) {
return hs, err2
}
-// Download replicates the manifest path structure on the local filesystem
+// Download replicates the manifest basePath structure on the local filesystem
// under localpath
//
// DEPRECATED: Use the HTTP API instead
@@ -269,7 +268,7 @@ func (self *FileSystem) Download(bzzpath, localpath string) error {
}
func retrieveToFile(quitC chan bool, dpa *storage.DPA, key storage.Key, path string) error {
- f, err := os.Create(path) // TODO: path separators
+ f, err := os.Create(path) // TODO: basePath separators
if err != nil {
return err
}
diff --git a/swarm/api/fuse.go b/swarm/api/fuse.go
deleted file mode 100644
index 2a1cc9bf1..000000000
--- a/swarm/api/fuse.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-// +build linux darwin freebsd
-
-// Data structures used for Fuse filesystem, serving directories and serving files to Fuse driver.
-
-package api
-
-import (
- "io"
- "os"
-
- "bazil.org/fuse"
- "bazil.org/fuse/fs"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/swarm/storage"
- "golang.org/x/net/context"
-)
-
-type FS struct {
- root *Dir
-}
-
-type Dir struct {
- inode uint64
- name string
- path string
- directories []*Dir
- files []*File
-}
-
-type File struct {
- inode uint64
- name string
- path string
- key storage.Key
- swarmApi *Api
- fileSize uint64
- reader storage.LazySectionReader
-}
-
-// Functions which satisfy the Fuse File System requests
-func (filesystem *FS) Root() (fs.Node, error) {
- return filesystem.root, nil
-}
-
-func (directory *Dir) Attr(ctx context.Context, a *fuse.Attr) error {
- a.Inode = directory.inode
- //TODO: need to get permission as argument
- a.Mode = os.ModeDir | 0500
- a.Uid = uint32(os.Getuid())
- a.Gid = uint32(os.Getegid())
- return nil
-}
-
-func (directory *Dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
- if directory.files != nil {
- for _, n := range directory.files {
- if n.name == name {
- return n, nil
- }
- }
- }
- if directory.directories != nil {
- for _, n := range directory.directories {
- if n.name == name {
- return n, nil
- }
- }
- }
- return nil, fuse.ENOENT
-}
-
-func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
- var children []fuse.Dirent
- if d.files != nil {
- for _, file := range d.files {
- children = append(children, fuse.Dirent{Inode: file.inode, Type: fuse.DT_File, Name: file.name})
- }
- }
- if d.directories != nil {
- for _, dir := range d.directories {
- children = append(children, fuse.Dirent{Inode: dir.inode, Type: fuse.DT_Dir, Name: dir.name})
- }
- }
- return children, nil
-}
-
-func (file *File) Attr(ctx context.Context, a *fuse.Attr) error {
- a.Inode = file.inode
- //TODO: need to get permission as argument
- a.Mode = 0500
- a.Uid = uint32(os.Getuid())
- a.Gid = uint32(os.Getegid())
-
- reader := file.swarmApi.Retrieve(file.key)
- quitC := make(chan bool)
- size, err := reader.Size(quitC)
- if err != nil {
- log.Warn("Couldnt file size of file %s : %v", file.path, err)
- a.Size = uint64(0)
- }
- a.Size = uint64(size)
- file.fileSize = a.Size
- return nil
-}
-
-var _ = fs.HandleReader(&File{})
-
-func (file *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
- buf := make([]byte, req.Size)
- reader := file.swarmApi.Retrieve(file.key)
- n, err := reader.ReadAt(buf, req.Offset)
- if err == io.ErrUnexpectedEOF || err == io.EOF {
- err = nil
- }
- resp.Data = buf[:n]
- return err
-}
diff --git a/swarm/api/http/server_test.go b/swarm/api/http/server_test.go
index 942f3ba0b..ceb8db75b 100644
--- a/swarm/api/http/server_test.go
+++ b/swarm/api/http/server_test.go
@@ -99,4 +99,32 @@ func TestBzzrGetPath(t *testing.T) {
}
}
+ nonhashtests := []string{
+ srv.URL + "/bzz:/name",
+ srv.URL + "/bzzi:/nonhash",
+ srv.URL + "/bzzr:/nonhash",
+ }
+
+ nonhashresponses := []string{
+ "error resolving name: 'name' does not resolve: no DNS to resolve name but is not a content hash\n",
+ "error resolving nonhash: 'nonhash' is not a content hash\n",
+ "error resolving nonhash: 'nonhash' does not resolve: no DNS to resolve name but is not a content hash\n",
+ }
+
+ for i, url := range nonhashtests {
+ var resp *http.Response
+ var respbody []byte
+
+ resp, err = http.Get(url)
+
+ if err != nil {
+ t.Fatalf("Request failed: %v", err)
+ }
+ defer resp.Body.Close()
+ respbody, err = ioutil.ReadAll(resp.Body)
+ if string(respbody) != nonhashresponses[i] {
+ t.Fatalf("Non-Hash response body does not match, expected: %v, got: %v", nonhashresponses[i], string(respbody))
+ }
+ }
+
}
diff --git a/swarm/api/http/templates.go b/swarm/api/http/templates.go
index c3ef8c0f4..9ae434a7e 100644
--- a/swarm/api/http/templates.go
+++ b/swarm/api/http/templates.go
@@ -1,18 +1,18 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of go-ethereum.
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
-// go-ethereum is distributed in the hope that it will be useful,
+// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Lesser General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package http
diff --git a/swarm/api/manifest.go b/swarm/api/manifest.go
index 6b3630fd0..dbaaf4bff 100644
--- a/swarm/api/manifest.go
+++ b/swarm/api/manifest.go
@@ -162,7 +162,7 @@ func (m *ManifestWalker) walk(trie *manifestTrie, prefix string, walkFn WalkFn)
type manifestTrie struct {
dpa *storage.DPA
- entries [257]*manifestTrieEntry // indexed by first character of path, entries[256] is the empty path entry
+ entries [257]*manifestTrieEntry // indexed by first character of basePath, entries[256] is the empty basePath entry
hash storage.Key // if hash != nil, it is stored
}
@@ -340,6 +340,7 @@ func (self *manifestTrie) recalcAndStore() error {
}
list.Entries = append(list.Entries, entry.ManifestEntry)
}
+
}
manifest, err := json.Marshal(list)
diff --git a/swarm/api/storage.go b/swarm/api/storage.go
index 7e94a9653..0e3abecfe 100644
--- a/swarm/api/storage.go
+++ b/swarm/api/storage.go
@@ -83,7 +83,7 @@ func (self *Storage) Get(bzzpath string) (*Response, error) {
return &Response{mimeType, status, expsize, string(body[:size])}, err
}
-// Modify(rootHash, path, contentHash, contentType) takes th e manifest trie rooted in rootHash,
+// Modify(rootHash, basePath, contentHash, contentType) takes th e manifest trie rooted in rootHash,
// and merge on to it. creating an entry w conentType (mime)
//
// DEPRECATED: Use the HTTP API instead
diff --git a/swarm/api/swarmfs_test.go b/swarm/api/swarmfs_test.go
deleted file mode 100644
index 45d2dc169..000000000
--- a/swarm/api/swarmfs_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package api
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-)
-
-var testUploadDir, _ = ioutil.TempDir(os.TempDir(), "fuse-source")
-var testMountDir, _ = ioutil.TempDir(os.TempDir(), "fuse-dest")
-
-func testFuseFileSystem(t *testing.T, f func(*FileSystem)) {
- testApi(t, func(api *Api) {
- f(NewFileSystem(api))
- })
-}
-
-func createTestFiles(t *testing.T, files []string) {
- os.RemoveAll(testUploadDir)
- os.RemoveAll(testMountDir)
- defer os.MkdirAll(testMountDir, 0777)
-
- for f := range files {
- actualPath := filepath.Join(testUploadDir, files[f])
- filePath := filepath.Dir(actualPath)
-
- err := os.MkdirAll(filePath, 0777)
- if err != nil {
- t.Fatalf("Error creating directory '%v' : %v", filePath, err)
- }
-
- _, err1 := os.OpenFile(actualPath, os.O_RDONLY|os.O_CREATE, 0666)
- if err1 != nil {
- t.Fatalf("Error creating file %v: %v", actualPath, err1)
- }
- }
-
-}
-
-func compareFiles(t *testing.T, files []string) {
- for f := range files {
- sourceFile := filepath.Join(testUploadDir, files[f])
- destinationFile := filepath.Join(testMountDir, files[f])
-
- sfinfo, err := os.Stat(sourceFile)
- if err != nil {
- t.Fatalf("Source file %v missing in mount: %v", files[f], err)
- }
-
- dfinfo, err := os.Stat(destinationFile)
- if err != nil {
- t.Fatalf("Destination file %v missing in mount: %v", files[f], err)
- }
-
- if sfinfo.Size() != dfinfo.Size() {
- t.Fatalf("Size mismatch source (%v) vs destination(%v)", sfinfo.Size(), dfinfo.Size())
- }
-
- if dfinfo.Mode().Perm().String() != "-r-x------" {
- t.Fatalf("Permission is not 0500for file: %v", err)
- }
- }
-}
-
-func doHashTest(fs *FileSystem, t *testing.T, ensName string, files ...string) {
- createTestFiles(t, files)
- bzzhash, err := fs.Upload(testUploadDir, "")
- if err != nil {
- t.Fatalf("Error uploading directory %v: %v", testUploadDir, err)
- }
-
- swarmfs := NewSwarmFS(fs.api)
- defer swarmfs.Stop()
-
- _, err = swarmfs.Mount(bzzhash, testMountDir)
- if isFUSEUnsupportedError(err) {
- t.Skip("FUSE not supported:", err)
- } else if err != nil {
- t.Fatalf("Error mounting hash %v: %v", bzzhash, err)
- }
-
- compareFiles(t, files)
-
- if _, err := swarmfs.Unmount(testMountDir); err != nil {
- t.Fatalf("Error unmounting path %v: %v", testMountDir, err)
- }
-}
-
-// mounting with manifest Hash
-func TestFuseMountingScenarios(t *testing.T) {
- testFuseFileSystem(t, func(fs *FileSystem) {
- //doHashTest(fs,t, "test","1.txt")
- doHashTest(fs, t, "", "1.txt")
- doHashTest(fs, t, "", "1.txt", "11.txt", "111.txt", "two/2.txt", "two/two/2.txt", "three/3.txt")
- doHashTest(fs, t, "", "1/2/3/4/5/6/7/8/9/10/11/12/1.txt")
- doHashTest(fs, t, "", "one/one.txt", "one.txt", "once/one.txt", "one/one/one.txt")
- })
-}
diff --git a/swarm/api/uri.go b/swarm/api/uri.go
index 68ce04835..caed4212d 100644
--- a/swarm/api/uri.go
+++ b/swarm/api/uri.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/swarm/api/uri_test.go b/swarm/api/uri_test.go
index dcb5fbbff..7d4160601 100644
--- a/swarm/api/uri_test.go
+++ b/swarm/api/uri_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
diff --git a/swarm/fuse/fuse_dir.go b/swarm/fuse/fuse_dir.go
new file mode 100644
index 000000000..91b236ae8
--- /dev/null
+++ b/swarm/fuse/fuse_dir.go
@@ -0,0 +1,155 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build linux darwin freebsd
+
+package fuse
+
+import (
+ "bazil.org/fuse"
+ "bazil.org/fuse/fs"
+ "golang.org/x/net/context"
+ "os"
+ "path/filepath"
+ "sync"
+)
+
+var (
+ _ fs.Node = (*SwarmDir)(nil)
+ _ fs.NodeRequestLookuper = (*SwarmDir)(nil)
+ _ fs.HandleReadDirAller = (*SwarmDir)(nil)
+ _ fs.NodeCreater = (*SwarmDir)(nil)
+ _ fs.NodeRemover = (*SwarmDir)(nil)
+ _ fs.NodeMkdirer = (*SwarmDir)(nil)
+)
+
+type SwarmDir struct {
+ inode uint64
+ name string
+ path string
+ directories []*SwarmDir
+ files []*SwarmFile
+
+ mountInfo *MountInfo
+ lock *sync.RWMutex
+}
+
+func NewSwarmDir(fullpath string, minfo *MountInfo) *SwarmDir {
+ newdir := &SwarmDir{
+ inode: NewInode(),
+ name: filepath.Base(fullpath),
+ path: fullpath,
+ directories: []*SwarmDir{},
+ files: []*SwarmFile{},
+ mountInfo: minfo,
+ lock: &sync.RWMutex{},
+ }
+ return newdir
+}
+
+func (sd *SwarmDir) Attr(ctx context.Context, a *fuse.Attr) error {
+ a.Inode = sd.inode
+ a.Mode = os.ModeDir | 0700
+ a.Uid = uint32(os.Getuid())
+ a.Gid = uint32(os.Getegid())
+ return nil
+}
+
+func (sd *SwarmDir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
+
+ for _, n := range sd.files {
+ if n.name == req.Name {
+ return n, nil
+ }
+ }
+ for _, n := range sd.directories {
+ if n.name == req.Name {
+ return n, nil
+ }
+ }
+ return nil, fuse.ENOENT
+}
+
+func (sd *SwarmDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
+ var children []fuse.Dirent
+ for _, file := range sd.files {
+ children = append(children, fuse.Dirent{Inode: file.inode, Type: fuse.DT_File, Name: file.name})
+ }
+ for _, dir := range sd.directories {
+ children = append(children, fuse.Dirent{Inode: dir.inode, Type: fuse.DT_Dir, Name: dir.name})
+ }
+ return children, nil
+}
+
+func (sd *SwarmDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
+
+ newFile := NewSwarmFile(sd.path, req.Name, sd.mountInfo)
+ newFile.fileSize = 0 // 0 means, file is not in swarm yet and it is just created
+
+ sd.lock.Lock()
+ defer sd.lock.Unlock()
+ sd.files = append(sd.files, newFile)
+
+ return newFile, newFile, nil
+}
+
+func (sd *SwarmDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
+
+ if req.Dir && sd.directories != nil {
+ newDirs := []*SwarmDir{}
+ for _, dir := range sd.directories {
+ if dir.name == req.Name {
+ removeDirectoryFromSwarm(dir)
+ } else {
+ newDirs = append(newDirs, dir)
+ }
+ }
+ if len(sd.directories) > len(newDirs) {
+ sd.lock.Lock()
+ defer sd.lock.Unlock()
+ sd.directories = newDirs
+ }
+ return nil
+ } else if !req.Dir && sd.files != nil {
+ newFiles := []*SwarmFile{}
+ for _, f := range sd.files {
+ if f.name == req.Name {
+ removeFileFromSwarm(f)
+ } else {
+ newFiles = append(newFiles, f)
+ }
+ }
+ if len(sd.files) > len(newFiles) {
+ sd.lock.Lock()
+ defer sd.lock.Unlock()
+ sd.files = newFiles
+ }
+ return nil
+ }
+ return fuse.ENOENT
+}
+
+func (sd *SwarmDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
+
+ newDir := NewSwarmDir(req.Name, sd.mountInfo)
+
+ sd.lock.Lock()
+ defer sd.lock.Unlock()
+ sd.directories = append(sd.directories, newDir)
+
+ return newDir, nil
+
+}
diff --git a/swarm/fuse/fuse_file.go b/swarm/fuse/fuse_file.go
new file mode 100644
index 000000000..0cb59dfb3
--- /dev/null
+++ b/swarm/fuse/fuse_file.go
@@ -0,0 +1,144 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build linux darwin freebsd
+
+package fuse
+
+import (
+ "bazil.org/fuse"
+ "bazil.org/fuse/fs"
+ "errors"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/swarm/storage"
+ "golang.org/x/net/context"
+ "io"
+ "os"
+ "sync"
+)
+
+const (
+ MaxAppendFileSize = 10485760 // 10Mb
+)
+
+var (
+ errInvalidOffset = errors.New("Invalid offset during write")
+ errFileSizeMaxLimixReached = errors.New("File size exceeded max limit")
+)
+
+var (
+ _ fs.Node = (*SwarmFile)(nil)
+ _ fs.HandleReader = (*SwarmFile)(nil)
+ _ fs.HandleWriter = (*SwarmFile)(nil)
+)
+
+type SwarmFile struct {
+ inode uint64
+ name string
+ path string
+ key storage.Key
+ fileSize int64
+ reader storage.LazySectionReader
+
+ mountInfo *MountInfo
+ lock *sync.RWMutex
+}
+
+func NewSwarmFile(path, fname string, minfo *MountInfo) *SwarmFile {
+ newFile := &SwarmFile{
+ inode: NewInode(),
+ name: fname,
+ path: path,
+ key: nil,
+ fileSize: -1, // -1 means , file already exists in swarm and you need to just get the size from swarm
+ reader: nil,
+
+ mountInfo: minfo,
+ lock: &sync.RWMutex{},
+ }
+ return newFile
+}
+
+func (file *SwarmFile) Attr(ctx context.Context, a *fuse.Attr) error {
+
+ a.Inode = file.inode
+ //TODO: need to get permission as argument
+ a.Mode = 0700
+ a.Uid = uint32(os.Getuid())
+ a.Gid = uint32(os.Getegid())
+
+ if file.fileSize == -1 {
+ reader := file.mountInfo.swarmApi.Retrieve(file.key)
+ quitC := make(chan bool)
+ size, err := reader.Size(quitC)
+ if err != nil {
+ log.Warn("Couldnt get size of file %s : %v", file.path, err)
+ }
+ file.fileSize = int64(size)
+ }
+ a.Size = uint64(file.fileSize)
+ return nil
+}
+
+func (sf *SwarmFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
+
+ sf.lock.RLock()
+ defer sf.lock.RUnlock()
+ if sf.reader == nil {
+ sf.reader = sf.mountInfo.swarmApi.Retrieve(sf.key)
+ }
+ buf := make([]byte, req.Size)
+ n, err := sf.reader.ReadAt(buf, req.Offset)
+ if err == io.ErrUnexpectedEOF || err == io.EOF {
+ err = nil
+ }
+ resp.Data = buf[:n]
+ sf.reader = nil
+ return err
+
+}
+
+func (sf *SwarmFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
+
+ if sf.fileSize == 0 && req.Offset == 0 {
+
+ // A new file is created
+ err := addFileToSwarm(sf, req.Data, len(req.Data))
+ if err != nil {
+ return err
+ }
+ resp.Size = len(req.Data)
+
+ } else if req.Offset <= sf.fileSize {
+
+ totalSize := sf.fileSize + int64(len(req.Data))
+ if totalSize > MaxAppendFileSize {
+ log.Warn("Append file size reached (%v) : (%v)", sf.fileSize, len(req.Data))
+ return errFileSizeMaxLimixReached
+ }
+
+ err := appendToExistingFileInSwarm(sf, req.Data, req.Offset, int64(len(req.Data)))
+ if err != nil {
+ return err
+ }
+ resp.Size = int(sf.fileSize)
+ } else {
+ log.Warn("Invalid write request size(%v) : off(%v)", sf.fileSize, req.Offset)
+ return errInvalidOffset
+ }
+
+ return nil
+}
diff --git a/swarm/api/swarmfs.go b/swarm/fuse/fuse_root.go
index 78a61cf9d..b2262d1c5 100644
--- a/swarm/api/swarmfs.go
+++ b/swarm/fuse/fuse_root.go
@@ -14,30 +14,22 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-package api
+// +build linux darwin freebsd
+
+package fuse
import (
- "sync"
- "time"
+ "bazil.org/fuse/fs"
)
-const (
- Swarmfs_Version = "0.1"
- mountTimeout = time.Second * 5
- maxFuseMounts = 5
+var (
+ _ fs.Node = (*SwarmDir)(nil)
)
-type SwarmFS struct {
- swarmApi *Api
- activeMounts map[string]*MountInfo
- activeLock *sync.RWMutex
+type SwarmRoot struct {
+ root *SwarmDir
}
-func NewSwarmFS(api *Api) *SwarmFS {
- swarmfs := &SwarmFS{
- swarmApi: api,
- activeLock: &sync.RWMutex{},
- activeMounts: map[string]*MountInfo{},
- }
- return swarmfs
+func (filesystem *SwarmRoot) Root() (fs.Node, error) {
+ return filesystem.root, nil
}
diff --git a/swarm/fuse/swarmfs.go b/swarm/fuse/swarmfs.go
new file mode 100644
index 000000000..2493bdab1
--- /dev/null
+++ b/swarm/fuse/swarmfs.go
@@ -0,0 +1,64 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package fuse
+
+import (
+ "github.com/ethereum/go-ethereum/swarm/api"
+ "sync"
+ "time"
+)
+
+const (
+ Swarmfs_Version = "0.1"
+ mountTimeout = time.Second * 5
+ unmountTimeout = time.Second * 10
+ maxFuseMounts = 5
+)
+
+var (
+ swarmfs *SwarmFS // Swarm file system singleton
+ swarmfsLock sync.Once
+
+ inode uint64 = 1 // global inode
+ inodeLock sync.RWMutex
+)
+
+type SwarmFS struct {
+ swarmApi *api.Api
+ activeMounts map[string]*MountInfo
+ swarmFsLock *sync.RWMutex
+}
+
+func NewSwarmFS(api *api.Api) *SwarmFS {
+ swarmfsLock.Do(func() {
+ swarmfs = &SwarmFS{
+ swarmApi: api,
+ swarmFsLock: &sync.RWMutex{},
+ activeMounts: map[string]*MountInfo{},
+ }
+ })
+ return swarmfs
+
+}
+
+// Inode numbers need to be unique, they are used for caching inside fuse
+func NewInode() uint64 {
+ inodeLock.Lock()
+ defer inodeLock.Unlock()
+ inode += 1
+ return inode
+}
diff --git a/swarm/api/swarmfs_fallback.go b/swarm/fuse/swarmfs_fallback.go
index c6ac07d14..4864c8689 100644
--- a/swarm/api/swarmfs_fallback.go
+++ b/swarm/fuse/swarmfs_fallback.go
@@ -16,7 +16,7 @@
// +build !linux,!darwin,!freebsd
-package api
+package fuse
import (
"errors"
@@ -29,8 +29,9 @@ func isFUSEUnsupportedError(err error) bool {
}
type MountInfo struct {
- MountPoint string
- ManifestHash string
+ MountPoint string
+ StartManifest string
+ LatestManifest string
}
func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
diff --git a/swarm/fuse/swarmfs_test.go b/swarm/fuse/swarmfs_test.go
new file mode 100644
index 000000000..f307b38ea
--- /dev/null
+++ b/swarm/fuse/swarmfs_test.go
@@ -0,0 +1,897 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build linux darwin freebsd
+
+package fuse
+
+import (
+ "bytes"
+ "crypto/rand"
+ "github.com/ethereum/go-ethereum/swarm/api"
+ "github.com/ethereum/go-ethereum/swarm/storage"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+type fileInfo struct {
+ perm uint64
+ uid int
+ gid int
+ contents []byte
+}
+
+func testFuseFileSystem(t *testing.T, f func(*api.Api)) {
+
+ datadir, err := ioutil.TempDir("", "fuse")
+ if err != nil {
+ t.Fatalf("unable to create temp dir: %v", err)
+ }
+ os.RemoveAll(datadir)
+
+ dpa, err := storage.NewLocalDPA(datadir)
+ if err != nil {
+ return
+ }
+ api := api.NewApi(dpa, nil)
+ dpa.Start()
+ f(api)
+ dpa.Stop()
+}
+
+func createTestFilesAndUploadToSwarm(t *testing.T, api *api.Api, files map[string]fileInfo, uploadDir string) string {
+
+ os.RemoveAll(uploadDir)
+
+ for fname, finfo := range files {
+ actualPath := filepath.Join(uploadDir, fname)
+ filePath := filepath.Dir(actualPath)
+
+ err := os.MkdirAll(filePath, 0777)
+ if err != nil {
+ t.Fatalf("Error creating directory '%v' : %v", filePath, err)
+ }
+
+ fd, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(finfo.perm))
+ if err1 != nil {
+ t.Fatalf("Error creating file %v: %v", actualPath, err1)
+ }
+
+ fd.Write(finfo.contents)
+ fd.Chown(finfo.uid, finfo.gid)
+ fd.Chmod(os.FileMode(finfo.perm))
+ fd.Sync()
+ fd.Close()
+ }
+
+ bzzhash, err := api.Upload(uploadDir, "")
+ if err != nil {
+ t.Fatalf("Error uploading directory %v: %v", uploadDir, err)
+ }
+
+ return bzzhash
+}
+
+func mountDir(t *testing.T, api *api.Api, files map[string]fileInfo, bzzHash string, mountDir string) *SwarmFS {
+
+ // Test Mount
+ os.RemoveAll(mountDir)
+ os.MkdirAll(mountDir, 0777)
+ swarmfs := NewSwarmFS(api)
+ _, err := swarmfs.Mount(bzzHash, mountDir)
+ if isFUSEUnsupportedError(err) {
+ t.Skip("FUSE not supported:", err)
+ } else if err != nil {
+ t.Fatalf("Error mounting hash %v: %v", bzzHash, err)
+ }
+
+ found := false
+ mi := swarmfs.Listmounts()
+ for _, minfo := range mi {
+ if minfo.MountPoint == mountDir {
+ if minfo.StartManifest != bzzHash ||
+ minfo.LatestManifest != bzzHash ||
+ minfo.fuseConnection == nil {
+ t.Fatalf("Error mounting: exp(%s): act(%s)", bzzHash, minfo.StartManifest)
+ }
+ found = true
+ }
+ }
+
+ // Test listMounts
+ if found == false {
+ t.Fatalf("Error getting mounts information for %v: %v", mountDir, err)
+ }
+
+ // Check if file and their attributes are as expected
+ compareGeneratedFileWithFileInMount(t, files, mountDir)
+
+ return swarmfs
+
+}
+
+func compareGeneratedFileWithFileInMount(t *testing.T, files map[string]fileInfo, mountDir string) {
+
+ err := filepath.Walk(mountDir, func(path string, f os.FileInfo, err error) error {
+ if f.IsDir() {
+ return nil
+ }
+ fname := path[len(mountDir)+1:]
+ if _, ok := files[fname]; !ok {
+ t.Fatalf(" file %v present in mount dir and is not expected", fname)
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatalf("Error walking dir %v", mountDir)
+ }
+
+ for fname, finfo := range files {
+
+ destinationFile := filepath.Join(mountDir, fname)
+
+ dfinfo, err := os.Stat(destinationFile)
+ if err != nil {
+ t.Fatalf("Destination file %v missing in mount: %v", fname, err)
+ }
+
+ if int64(len(finfo.contents)) != dfinfo.Size() {
+ t.Fatalf("file %v Size mismatch source (%v) vs destination(%v)", fname, int64(len(finfo.contents)), dfinfo.Size())
+ }
+
+ if dfinfo.Mode().Perm().String() != "-rwx------" {
+ t.Fatalf("file %v Permission mismatch source (-rwx------) vs destination(%v)", fname, dfinfo.Mode().Perm())
+ }
+
+ fileContents, err := ioutil.ReadFile(filepath.Join(mountDir, fname))
+ if err != nil {
+ t.Fatalf("Could not readfile %v : %v", fname, err)
+ }
+
+ if bytes.Compare(fileContents, finfo.contents) != 0 {
+ t.Fatalf("File %v contents mismatch: %v , %v", fname, fileContents, finfo.contents)
+
+ }
+
+ // TODO: check uid and gid
+ }
+}
+
+func checkFile(t *testing.T, testMountDir, fname string, contents []byte) {
+
+ destinationFile := filepath.Join(testMountDir, fname)
+ dfinfo, err1 := os.Stat(destinationFile)
+ if err1 != nil {
+ t.Fatalf("Could not stat file %v", destinationFile)
+ }
+ if dfinfo.Size() != int64(len(contents)) {
+ t.Fatalf("Mismatch in size actual(%v) vs expected(%v)", dfinfo.Size(), int64(len(contents)))
+ }
+
+ fd, err2 := os.OpenFile(destinationFile, os.O_RDONLY, os.FileMode(0665))
+ if err2 != nil {
+ t.Fatalf("Could not open file %v", destinationFile)
+ }
+ newcontent := make([]byte, len(contents))
+ fd.Read(newcontent)
+ fd.Close()
+
+ if !bytes.Equal(contents, newcontent) {
+ t.Fatalf("File content mismatch expected (%v): received (%v) ", contents, newcontent)
+ }
+}
+
+func getRandomBtes(size int) []byte {
+ contents := make([]byte, size)
+ rand.Read(contents)
+ return contents
+
+}
+
+func IsDirEmpty(name string) bool {
+ f, err := os.Open(name)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+
+ _, err = f.Readdirnames(1)
+ if err == io.EOF {
+ return true
+ }
+ return false
+}
+
+func testMountListAndUnmount(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "fuse-source")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "fuse-dest")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["2.txt"] = fileInfo{0711, 333, 444, getRandomBtes(10)}
+ files["3.txt"] = fileInfo{0622, 333, 444, getRandomBtes(100)}
+ files["4.txt"] = fileInfo{0533, 333, 444, getRandomBtes(1024)}
+ files["5.txt"] = fileInfo{0544, 333, 444, getRandomBtes(10)}
+ files["6.txt"] = fileInfo{0555, 333, 444, getRandomBtes(10)}
+ files["7.txt"] = fileInfo{0666, 333, 444, getRandomBtes(10)}
+ files["8.txt"] = fileInfo{0777, 333, 333, getRandomBtes(10)}
+ files["11.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)}
+ files["111.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)}
+ files["two/2.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)}
+ files["two/2/2.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)}
+ files["two/2./2.txt"] = fileInfo{0777, 444, 444, getRandomBtes(10)}
+ files["twice/2.txt"] = fileInfo{0777, 444, 333, getRandomBtes(200)}
+ files["one/two/three/four/five/six/seven/eight/nine/10.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10240)}
+ files["one/two/three/four/five/six/six"] = fileInfo{0777, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs.Stop()
+
+ // Check unmount
+ _, err := swarmfs.Unmount(testMountDir)
+ if err != nil {
+ t.Fatalf("could not unmount %v", bzzHash)
+ }
+ if !IsDirEmpty(testMountDir) {
+ t.Fatalf("unmount didnt work for %v", testMountDir)
+ }
+
+}
+
+func testMaxMounts(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir1, _ := ioutil.TempDir(os.TempDir(), "max-upload1")
+ bzzHash1 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir1)
+ mount1, _ := ioutil.TempDir(os.TempDir(), "max-mount1")
+ swarmfs1 := mountDir(t, api, files, bzzHash1, mount1)
+ defer swarmfs1.Stop()
+
+ files["2.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir2, _ := ioutil.TempDir(os.TempDir(), "max-upload2")
+ bzzHash2 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir2)
+ mount2, _ := ioutil.TempDir(os.TempDir(), "max-mount2")
+ swarmfs2 := mountDir(t, api, files, bzzHash2, mount2)
+ defer swarmfs2.Stop()
+
+ files["3.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir3, _ := ioutil.TempDir(os.TempDir(), "max-upload3")
+ bzzHash3 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir3)
+ mount3, _ := ioutil.TempDir(os.TempDir(), "max-mount3")
+ swarmfs3 := mountDir(t, api, files, bzzHash3, mount3)
+ defer swarmfs3.Stop()
+
+ files["4.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir4, _ := ioutil.TempDir(os.TempDir(), "max-upload4")
+ bzzHash4 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir4)
+ mount4, _ := ioutil.TempDir(os.TempDir(), "max-mount4")
+ swarmfs4 := mountDir(t, api, files, bzzHash4, mount4)
+ defer swarmfs4.Stop()
+
+ files["5.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir5, _ := ioutil.TempDir(os.TempDir(), "max-upload5")
+ bzzHash5 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir5)
+ mount5, _ := ioutil.TempDir(os.TempDir(), "max-mount5")
+ swarmfs5 := mountDir(t, api, files, bzzHash5, mount5)
+ defer swarmfs5.Stop()
+
+ files["6.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir6, _ := ioutil.TempDir(os.TempDir(), "max-upload6")
+ bzzHash6 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir6)
+ mount6, _ := ioutil.TempDir(os.TempDir(), "max-mount6")
+
+ os.RemoveAll(mount6)
+ os.MkdirAll(mount6, 0777)
+ _, err := swarmfs.Mount(bzzHash6, mount6)
+ if err == nil {
+ t.Fatalf("Error: Going beyond max mounts %v", bzzHash6)
+ }
+
+}
+
+func testReMounts(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ uploadDir1, _ := ioutil.TempDir(os.TempDir(), "re-upload1")
+ bzzHash1 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir1)
+ testMountDir1, _ := ioutil.TempDir(os.TempDir(), "re-mount1")
+ swarmfs := mountDir(t, api, files, bzzHash1, testMountDir1)
+ defer swarmfs.Stop()
+
+ uploadDir2, _ := ioutil.TempDir(os.TempDir(), "re-upload2")
+ bzzHash2 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir2)
+ testMountDir2, _ := ioutil.TempDir(os.TempDir(), "re-mount2")
+
+ // try mounting the same hash second time
+ os.RemoveAll(testMountDir2)
+ os.MkdirAll(testMountDir2, 0777)
+ _, err := swarmfs.Mount(bzzHash1, testMountDir2)
+ if err != nil {
+ t.Fatalf("Error mounting hash %v", bzzHash1)
+ }
+
+ // mount a different hash in already mounted point
+ _, err = swarmfs.Mount(bzzHash2, testMountDir1)
+ if err == nil {
+ t.Fatalf("Error mounting hash %v", bzzHash2)
+ }
+
+ // mount nonexistent hash
+ _, err = swarmfs.Mount("0xfea11223344", testMountDir1)
+ if err == nil {
+ t.Fatalf("Error mounting hash %v", bzzHash2)
+ }
+
+}
+
+func testUnmount(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ uploadDir, _ := ioutil.TempDir(os.TempDir(), "ex-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "ex-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, uploadDir)
+
+ swarmfs := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs.Stop()
+
+ swarmfs.Unmount(testMountDir)
+
+ mi := swarmfs.Listmounts()
+ for _, minfo := range mi {
+ if minfo.MountPoint == testMountDir {
+ t.Fatalf("mount state not cleaned up in unmount case %v", testMountDir)
+ }
+ }
+
+}
+
+func testUnmountWhenResourceBusy(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "ex-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "ex-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs.Stop()
+
+ actualPath := filepath.Join(testMountDir, "2.txt")
+ d, err := os.OpenFile(actualPath, os.O_RDWR, os.FileMode(0700))
+ d.Write(getRandomBtes(10))
+
+ _, err = swarmfs.Unmount(testMountDir)
+ if err != nil {
+ t.Fatalf("could not unmount %v", bzzHash)
+ }
+ d.Close()
+
+ mi := swarmfs.Listmounts()
+ for _, minfo := range mi {
+ if minfo.MountPoint == testMountDir {
+ t.Fatalf("mount state not cleaned up in unmount case %v", testMountDir)
+ }
+ }
+
+}
+func testSeekInMultiChunkFile(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "seek-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "seek-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10240)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs.Stop()
+
+ // Create a new file seek the second chunk
+ actualPath := filepath.Join(testMountDir, "1.txt")
+ d, _ := os.OpenFile(actualPath, os.O_RDONLY, os.FileMode(0700))
+
+ d.Seek(5000, 0)
+
+ contents := make([]byte, 1024)
+ d.Read(contents)
+ finfo := files["1.txt"]
+
+ if bytes.Compare(finfo.contents[:6024][5000:], contents) != 0 {
+ t.Fatalf("File seek contents mismatch")
+ }
+ d.Close()
+
+}
+
+func testCreateNewFile(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "create-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "create-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Create a new file in the root dir and check
+ actualPath := filepath.Join(testMountDir, "2.txt")
+ d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665))
+ if err1 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err1)
+ }
+ contents := make([]byte, 11)
+ rand.Read(contents)
+ d.Write(contents)
+ d.Close()
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ files["2.txt"] = fileInfo{0700, 333, 444, contents}
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+ checkFile(t, testMountDir, "2.txt", contents)
+
+}
+
+func testCreateNewFileInsideDirectory(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "createinsidedir-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "createinsidedir-mount")
+
+ files["one/1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Create a new file inside a existing dir and check
+ dirToCreate := filepath.Join(testMountDir, "one")
+ actualPath := filepath.Join(dirToCreate, "2.txt")
+ d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665))
+ if err1 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err1)
+ }
+ contents := make([]byte, 11)
+ rand.Read(contents)
+ d.Write(contents)
+ d.Close()
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ files["one/2.txt"] = fileInfo{0700, 333, 444, contents}
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+ checkFile(t, testMountDir, "one/2.txt", contents)
+
+}
+
+func testCreateNewFileInsideNewDirectory(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "createinsidenewdir-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "createinsidenewdir-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Create a new file inside a existing dir and check
+ dirToCreate := filepath.Join(testMountDir, "one")
+ os.MkdirAll(dirToCreate, 0777)
+ actualPath := filepath.Join(dirToCreate, "2.txt")
+ d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665))
+ if err1 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err1)
+ }
+ contents := make([]byte, 11)
+ rand.Read(contents)
+ d.Write(contents)
+ d.Close()
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ files["one/2.txt"] = fileInfo{0700, 333, 444, contents}
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+ checkFile(t, testMountDir, "one/2.txt", contents)
+
+}
+
+func testRemoveExistingFile(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "remove-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "remove-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Remove a file in the root dir and check
+ actualPath := filepath.Join(testMountDir, "five.txt")
+ os.Remove(actualPath)
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ delete(files, "five.txt")
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+}
+
+func testRemoveExistingFileInsideADir(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "remove-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "remove-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["one/five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["one/six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Remove a file in the root dir and check
+ actualPath := filepath.Join(testMountDir, "one/five.txt")
+ os.Remove(actualPath)
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ delete(files, "one/five.txt")
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+}
+
+func testRemoveNewlyAddedFile(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "removenew-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "removenew-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Adda a new file and remove it
+ dirToCreate := filepath.Join(testMountDir, "one")
+ os.MkdirAll(dirToCreate, os.FileMode(0665))
+ actualPath := filepath.Join(dirToCreate, "2.txt")
+ d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665))
+ if err1 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err1)
+ }
+ contents := make([]byte, 11)
+ rand.Read(contents)
+ d.Write(contents)
+ d.Close()
+
+ checkFile(t, testMountDir, "one/2.txt", contents)
+
+ os.Remove(actualPath)
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+ if bzzHash != mi.LatestManifest {
+ t.Fatalf("same contents different hash orig(%v): new(%v)", bzzHash, mi.LatestManifest)
+ }
+
+}
+
+func testAddNewFileAndModifyContents(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "modifyfile-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "modifyfile-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ // Create a new file in the root dir and check
+ actualPath := filepath.Join(testMountDir, "2.txt")
+ d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665))
+ if err1 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err1)
+ }
+ line1 := []byte("Line 1")
+ rand.Read(line1)
+ d.Write(line1)
+ d.Close()
+
+ mi1, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v", err2)
+ }
+
+ // mount again and see if things are okay
+ files["2.txt"] = fileInfo{0700, 333, 444, line1}
+ swarmfs2 := mountDir(t, api, files, mi1.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+ checkFile(t, testMountDir, "2.txt", line1)
+
+ mi2, err3 := swarmfs2.Unmount(testMountDir)
+ if err3 != nil {
+ t.Fatalf("Could not unmount %v", err3)
+ }
+
+ // mount again and modify
+ swarmfs3 := mountDir(t, api, files, mi2.LatestManifest, testMountDir)
+ defer swarmfs3.Stop()
+
+ fd, err4 := os.OpenFile(actualPath, os.O_RDWR|os.O_APPEND, os.FileMode(0665))
+ if err4 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err4)
+ }
+ line2 := []byte("Line 2")
+ rand.Read(line2)
+ fd.Seek(int64(len(line1)), 0)
+ fd.Write(line2)
+ fd.Close()
+
+ mi3, err5 := swarmfs3.Unmount(testMountDir)
+ if err5 != nil {
+ t.Fatalf("Could not unmount %v", err5)
+ }
+
+ // mount again and see if things are okay
+ b := [][]byte{line1, line2}
+ line1and2 := bytes.Join(b, []byte(""))
+ files["2.txt"] = fileInfo{0700, 333, 444, line1and2}
+ swarmfs4 := mountDir(t, api, files, mi3.LatestManifest, testMountDir)
+ defer swarmfs4.Stop()
+
+ checkFile(t, testMountDir, "2.txt", line1and2)
+
+}
+
+func testRemoveEmptyDir(api *api.Api, t *testing.T) {
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-mount")
+
+ files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ os.MkdirAll(filepath.Join(testMountDir, "newdir"), 0777)
+
+ mi, err3 := swarmfs1.Unmount(testMountDir)
+ if err3 != nil {
+ t.Fatalf("Could not unmount %v", err3)
+ }
+
+ if bzzHash != mi.LatestManifest {
+ t.Fatalf("same contents different hash orig(%v): new(%v)", bzzHash, mi.LatestManifest)
+ }
+
+}
+
+func testRemoveDirWhichHasFiles(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-mount")
+
+ files["one/1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ dirPath := filepath.Join(testMountDir, "two")
+ os.RemoveAll(dirPath)
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v ", err2)
+ }
+
+ // mount again and see if things are okay
+ delete(files, "two/five.txt")
+ delete(files, "two/six.txt")
+
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+}
+
+func testRemoveDirWhichHasSubDirs(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "rmsubdir-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "rmsubdir-mount")
+
+ files["one/1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/three/2.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/three/3.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/four/5.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/four/6.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+ files["two/four/six/7.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)}
+
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ dirPath := filepath.Join(testMountDir, "two")
+ os.RemoveAll(dirPath)
+
+ mi, err2 := swarmfs1.Unmount(testMountDir)
+ if err2 != nil {
+ t.Fatalf("Could not unmount %v ", err2)
+ }
+
+ // mount again and see if things are okay
+ delete(files, "two/three/2.txt")
+ delete(files, "two/three/3.txt")
+ delete(files, "two/four/5.txt")
+ delete(files, "two/four/6.txt")
+ delete(files, "two/four/six/7.txt")
+
+ swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+}
+
+func testAppendFileContentsToEnd(api *api.Api, t *testing.T) {
+
+ files := make(map[string]fileInfo)
+ testUploadDir, _ := ioutil.TempDir(os.TempDir(), "appendlargefile-upload")
+ testMountDir, _ := ioutil.TempDir(os.TempDir(), "appendlargefile-mount")
+
+ line1 := make([]byte, 10)
+ rand.Read(line1)
+ files["1.txt"] = fileInfo{0700, 333, 444, line1}
+ bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir)
+
+ swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir)
+ defer swarmfs1.Stop()
+
+ actualPath := filepath.Join(testMountDir, "1.txt")
+ fd, err4 := os.OpenFile(actualPath, os.O_RDWR|os.O_APPEND, os.FileMode(0665))
+ if err4 != nil {
+ t.Fatalf("Could not create file %s : %v", actualPath, err4)
+ }
+ line2 := make([]byte, 5)
+ rand.Read(line2)
+ fd.Seek(int64(len(line1)), 0)
+ fd.Write(line2)
+ fd.Close()
+
+ mi1, err5 := swarmfs1.Unmount(testMountDir)
+ if err5 != nil {
+ t.Fatalf("Could not unmount %v ", err5)
+ }
+
+ // mount again and see if things are okay
+ b := [][]byte{line1, line2}
+ line1and2 := bytes.Join(b, []byte(""))
+ files["1.txt"] = fileInfo{0700, 333, 444, line1and2}
+ swarmfs2 := mountDir(t, api, files, mi1.LatestManifest, testMountDir)
+ defer swarmfs2.Stop()
+
+ checkFile(t, testMountDir, "1.txt", line1and2)
+
+}
+
+func TestSwarmFileSystem(t *testing.T) {
+ testFuseFileSystem(t, func(api *api.Api) {
+
+ testMountListAndUnmount(api, t)
+
+ testMaxMounts(api, t)
+
+ testReMounts(api, t)
+
+ testUnmount(api, t)
+
+ testUnmountWhenResourceBusy(api, t)
+
+ testSeekInMultiChunkFile(api, t)
+
+ testCreateNewFile(api, t)
+
+ testCreateNewFileInsideDirectory(api, t)
+
+ testCreateNewFileInsideNewDirectory(api, t)
+
+ testRemoveExistingFile(api, t)
+
+ testRemoveExistingFileInsideADir(api, t)
+
+ testRemoveNewlyAddedFile(api, t)
+
+ testAddNewFileAndModifyContents(api, t)
+
+ testRemoveEmptyDir(api, t)
+
+ testRemoveDirWhichHasFiles(api, t)
+
+ testRemoveDirWhichHasSubDirs(api, t)
+
+ testAppendFileContentsToEnd(api, t)
+
+ })
+}
diff --git a/swarm/api/swarmfs_unix.go b/swarm/fuse/swarmfs_unix.go
index a704c1ec2..f4eecef24 100644
--- a/swarm/api/swarmfs_unix.go
+++ b/swarm/fuse/swarmfs_unix.go
@@ -16,33 +16,28 @@
// +build linux darwin freebsd
-package api
+package fuse
import (
+ "bazil.org/fuse"
+ "bazil.org/fuse/fs"
"errors"
"fmt"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/swarm/api"
"os"
"path/filepath"
"strings"
"sync"
"time"
-
- "bazil.org/fuse"
- "bazil.org/fuse/fs"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/swarm/storage"
-)
-
-var (
- inode uint64 = 1
- inodeLock sync.RWMutex
)
var (
errEmptyMountPoint = errors.New("need non-empty mount point")
errMaxMountCount = errors.New("max FUSE mount count reached")
errMountTimeout = errors.New("mount timeout")
+ errAlreadyMounted = errors.New("mount point is already serving")
)
func isFUSEUnsupportedError(err error) bool {
@@ -52,16 +47,17 @@ func isFUSEUnsupportedError(err error) bool {
return err == fuse.ErrOSXFUSENotFound
}
-// MountInfo contains information about every active mount
+// information about every active mount
type MountInfo struct {
MountPoint string
- ManifestHash string
- resolvedKey storage.Key
- rootDir *Dir
+ StartManifest string
+ LatestManifest string
+ rootDir *SwarmDir
fuseConnection *fuse.Conn
+ swarmApi *api.Api
+ lock *sync.RWMutex
}
-// newInode creates a new inode number.
// Inode numbers need to be unique, they are used for caching inside fuse
func newInode() uint64 {
inodeLock.Lock()
@@ -70,7 +66,21 @@ func newInode() uint64 {
return inode
}
+func NewMountInfo(mhash, mpoint string, sapi *api.Api) *MountInfo {
+ newMountInfo := &MountInfo{
+ MountPoint: mpoint,
+ StartManifest: mhash,
+ LatestManifest: mhash,
+ rootDir: nil,
+ fuseConnection: nil,
+ swarmApi: sapi,
+ lock: &sync.RWMutex{},
+ }
+ return newMountInfo
+}
+
func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
+
if mountpoint == "" {
return nil, errEmptyMountPoint
}
@@ -79,8 +89,8 @@ func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
return nil, err
}
- self.activeLock.Lock()
- defer self.activeLock.Unlock()
+ self.swarmFsLock.Lock()
+ defer self.swarmFsLock.Unlock()
noOfActiveMounts := len(self.activeMounts)
if noOfActiveMounts >= maxFuseMounts {
@@ -88,44 +98,27 @@ func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
}
if _, ok := self.activeMounts[cleanedMountPoint]; ok {
- return nil, fmt.Errorf("%s is already mounted", cleanedMountPoint)
+ return nil, errAlreadyMounted
}
- uri, err := Parse("bzz:/" + mhash)
- if err != nil {
- return nil, err
- }
- key, err := self.swarmApi.Resolve(uri)
+ log.Info(fmt.Sprintf("Attempting to mount %s ", cleanedMountPoint))
+ key, manifestEntryMap, err := self.swarmApi.BuildDirectoryTree(mhash, true)
if err != nil {
return nil, err
}
- path := uri.Path
- if len(path) > 0 {
- path += "/"
- }
+ mi := NewMountInfo(mhash, cleanedMountPoint, self.swarmApi)
- quitC := make(chan bool)
- trie, err := loadManifest(self.swarmApi.dpa, key, quitC)
- if err != nil {
- return nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err)
- }
+ dirTree := map[string]*SwarmDir{}
+ rootDir := NewSwarmDir("/", mi)
+ dirTree["/"] = rootDir
+ mi.rootDir = rootDir
- dirTree := map[string]*Dir{}
+ for suffix, entry := range manifestEntryMap {
- rootDir := &Dir{
- inode: newInode(),
- name: "root",
- directories: nil,
- files: nil,
- }
- dirTree["root"] = rootDir
-
- err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) {
key = common.Hex2Bytes(entry.Hash)
fullpath := "/" + suffix
basepath := filepath.Dir(fullpath)
- filename := filepath.Base(fullpath)
parentDir := rootDir
dirUntilNow := ""
@@ -136,13 +129,7 @@ func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
dirUntilNow = dirUntilNow + "/" + thisDir
if _, ok := dirTree[dirUntilNow]; !ok {
- dirTree[dirUntilNow] = &Dir{
- inode: newInode(),
- name: thisDir,
- path: dirUntilNow,
- directories: nil,
- files: nil,
- }
+ dirTree[dirUntilNow] = NewSwarmDir(dirUntilNow, mi)
parentDir.directories = append(parentDir.directories, dirTree[dirUntilNow])
parentDir = dirTree[dirUntilNow]
@@ -152,29 +139,32 @@ func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
}
}
- thisFile := &File{
- inode: newInode(),
- name: filename,
- path: fullpath,
- key: key,
- swarmApi: self.swarmApi,
- }
+ thisFile := NewSwarmFile(basepath, filepath.Base(fullpath), mi)
+ thisFile.key = key
+
parentDir.files = append(parentDir.files, thisFile)
- })
+ }
fconn, err := fuse.Mount(cleanedMountPoint, fuse.FSName("swarmfs"), fuse.VolumeName(mhash))
- if err != nil {
+ if isFUSEUnsupportedError(err) {
+ log.Warn("Fuse not installed", "mountpoint", cleanedMountPoint, "err", err)
+ return nil, err
+ } else if err != nil {
fuse.Unmount(cleanedMountPoint)
log.Warn("Error mounting swarm manifest", "mountpoint", cleanedMountPoint, "err", err)
return nil, err
}
+ mi.fuseConnection = fconn
- mounterr := make(chan error, 1)
+ serverr := make(chan error, 1)
go func() {
- filesys := &FS{root: rootDir}
+ log.Info(fmt.Sprintf("Serving %s at %s", mhash, cleanedMountPoint))
+ filesys := &SwarmRoot{root: rootDir}
if err := fs.Serve(fconn, filesys); err != nil {
- mounterr <- err
+ log.Warn(fmt.Sprintf("Could not Serve SwarmFileSystem error: %v", err))
+ serverr <- err
}
+
}()
// Check if the mount process has an error to report.
@@ -183,7 +173,8 @@ func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
fuse.Unmount(cleanedMountPoint)
return nil, errMountTimeout
- case err := <-mounterr:
+ case err := <-serverr:
+ fuse.Unmount(cleanedMountPoint)
log.Warn("Error serving swarm FUSE FS", "mountpoint", cleanedMountPoint, "err", err)
return nil, err
@@ -191,46 +182,47 @@ func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) {
log.Info("Now serving swarm FUSE FS", "manifest", mhash, "mountpoint", cleanedMountPoint)
}
- // Assemble and Store the mount information for future use
- mi := &MountInfo{
- MountPoint: cleanedMountPoint,
- ManifestHash: mhash,
- resolvedKey: key,
- rootDir: rootDir,
- fuseConnection: fconn,
- }
self.activeMounts[cleanedMountPoint] = mi
return mi, nil
}
-func (self *SwarmFS) Unmount(mountpoint string) (bool, error) {
- self.activeLock.Lock()
- defer self.activeLock.Unlock()
+func (self *SwarmFS) Unmount(mountpoint string) (*MountInfo, error) {
+
+ self.swarmFsLock.Lock()
+ defer self.swarmFsLock.Unlock()
cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint))
if err != nil {
- return false, err
+ return nil, err
}
mountInfo := self.activeMounts[cleanedMountPoint]
+
if mountInfo == nil || mountInfo.MountPoint != cleanedMountPoint {
- return false, fmt.Errorf("%s is not mounted", cleanedMountPoint)
+ return nil, fmt.Errorf("%s is not mounted", cleanedMountPoint)
}
err = fuse.Unmount(cleanedMountPoint)
if err != nil {
- // TODO(jmozah): try forceful unmount if normal unmount fails
- return false, err
+ err1 := externalUnMount(cleanedMountPoint)
+ if err1 != nil {
+ errStr := fmt.Sprintf("UnMount error: %v", err)
+ log.Warn(errStr)
+ return nil, err1
+ }
}
- // remove the mount information from the active map
mountInfo.fuseConnection.Close()
delete(self.activeMounts, cleanedMountPoint)
- return true, nil
+
+ succString := fmt.Sprintf("UnMounting %v succeeded", cleanedMountPoint)
+ log.Info(succString)
+
+ return mountInfo, nil
}
func (self *SwarmFS) Listmounts() []*MountInfo {
- self.activeLock.RLock()
- defer self.activeLock.RUnlock()
+ self.swarmFsLock.RLock()
+ defer self.swarmFsLock.RUnlock()
rows := make([]*MountInfo, 0, len(self.activeMounts))
for _, mi := range self.activeMounts {
diff --git a/swarm/fuse/swarmfs_util.go b/swarm/fuse/swarmfs_util.go
new file mode 100644
index 000000000..d20ab258e
--- /dev/null
+++ b/swarm/fuse/swarmfs_util.go
@@ -0,0 +1,144 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build linux darwin freebsd
+
+package fuse
+
+import (
+ "fmt"
+ "github.com/ethereum/go-ethereum/log"
+ "os/exec"
+ "runtime"
+ "time"
+)
+
+func externalUnMount(mountPoint string) error {
+
+ var cmd *exec.Cmd
+
+ switch runtime.GOOS {
+
+ case "darwin":
+ cmd = exec.Command("/usr/bin/diskutil", "umount", "force", mountPoint)
+
+ case "linux":
+ cmd = exec.Command("fusermount", "-u", mountPoint)
+
+ default:
+ return fmt.Errorf("unmount: unimplemented")
+ }
+
+ errc := make(chan error, 1)
+ go func() {
+ defer close(errc)
+
+ if err := exec.Command("umount", mountPoint).Run(); err == nil {
+ return
+ }
+ errc <- cmd.Run()
+ }()
+
+ select {
+
+ case <-time.After(unmountTimeout):
+ return fmt.Errorf("umount timeout")
+
+ case err := <-errc:
+ return err
+ }
+}
+
+func addFileToSwarm(sf *SwarmFile, content []byte, size int) error {
+
+ fkey, mhash, err := sf.mountInfo.swarmApi.AddFile(sf.mountInfo.LatestManifest, sf.path, sf.name, content, true)
+ if err != nil {
+ return err
+ }
+
+ sf.lock.Lock()
+ defer sf.lock.Unlock()
+ sf.key = fkey
+ sf.fileSize = int64(size)
+
+ sf.mountInfo.lock.Lock()
+ defer sf.mountInfo.lock.Unlock()
+ sf.mountInfo.LatestManifest = mhash
+
+ log.Info("Added new file:", "fname", sf.name, "New Manifest hash", mhash)
+ return nil
+
+}
+
+func removeFileFromSwarm(sf *SwarmFile) error {
+
+ mkey, err := sf.mountInfo.swarmApi.RemoveFile(sf.mountInfo.LatestManifest, sf.path, sf.name, true)
+ if err != nil {
+ return err
+ }
+
+ sf.mountInfo.lock.Lock()
+ defer sf.mountInfo.lock.Unlock()
+ sf.mountInfo.LatestManifest = mkey
+
+ log.Info("Removed file:", "fname", sf.name, "New Manifest hash", mkey)
+ return nil
+}
+
+func removeDirectoryFromSwarm(sd *SwarmDir) error {
+
+ if len(sd.directories) == 0 && len(sd.files) == 0 {
+ return nil
+ }
+
+ for _, d := range sd.directories {
+ err := removeDirectoryFromSwarm(d)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, f := range sd.files {
+ err := removeFileFromSwarm(f)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+
+}
+
+func appendToExistingFileInSwarm(sf *SwarmFile, content []byte, offset int64, length int64) error {
+
+ fkey, mhash, err := sf.mountInfo.swarmApi.AppendFile(sf.mountInfo.LatestManifest, sf.path, sf.name, sf.fileSize, content, sf.key, offset, length, true)
+ if err != nil {
+ return err
+ }
+
+ sf.lock.Lock()
+ defer sf.lock.Unlock()
+ sf.key = fkey
+ sf.fileSize = sf.fileSize + int64(len(content))
+
+ sf.mountInfo.lock.Lock()
+ defer sf.mountInfo.lock.Unlock()
+ sf.mountInfo.LatestManifest = mhash
+
+ log.Info("Appended file:", "fname", sf.name, "New Manifest hash", mhash)
+ return nil
+
+}
diff --git a/swarm/swarm.go b/swarm/swarm.go
index 5a7f43f8b..442e68d51 100644
--- a/swarm/swarm.go
+++ b/swarm/swarm.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/contracts/chequebook"
"github.com/ethereum/go-ethereum/contracts/ens"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
@@ -34,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/api"
httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
+ "github.com/ethereum/go-ethereum/swarm/fuse"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage"
)
@@ -54,7 +56,7 @@ type Swarm struct {
corsString string
swapEnabled bool
lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped
- sfs *api.SwarmFS // need this to cleanup all the active mounts on node exit
+ sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit
}
type SwarmAPI struct {
@@ -133,9 +135,13 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
// set up high level api
transactOpts := bind.NewKeyedTransactor(self.privateKey)
- self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend)
- if err != nil {
- return nil, err
+ if backend == (*ethclient.Client)(nil) {
+ log.Warn("No ENS, please specify non-empty --ethapi to use domain name resolution")
+ } else {
+ self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend)
+ if err != nil {
+ return nil, err
+ }
}
log.Debug(fmt.Sprintf("-> Swarm Domain Name Registrar @ address %v", config.EnsRoot.Hex()))
@@ -143,7 +149,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
// Manifests for Smart Hosting
log.Debug(fmt.Sprintf("-> Web3 virtual server API"))
- self.sfs = api.NewSwarmFS(self.api)
+ self.sfs = fuse.NewSwarmFS(self.api)
log.Debug("-> Initializing Fuse file system")
return self, nil
@@ -262,7 +268,7 @@ func (self *Swarm) APIs() []rpc.API {
},
{
Namespace: "swarmfs",
- Version: api.Swarmfs_Version,
+ Version: fuse.Swarmfs_Version,
Service: self.sfs,
Public: false,
},
diff --git a/swarm/testutil/http.go b/swarm/testutil/http.go
index bf98d16eb..f2922fab0 100644
--- a/swarm/testutil/http.go
+++ b/swarm/testutil/http.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
package testutil
import (
diff --git a/trie/iterator.go b/trie/iterator.go
index ddc674d2b..42149a7d3 100644
--- a/trie/iterator.go
+++ b/trie/iterator.go
@@ -18,7 +18,7 @@ package trie
import (
"bytes"
-
+ "container/heap"
"github.com/ethereum/go-ethereum/common"
)
@@ -268,6 +268,26 @@ outer:
return nil
}
+func compareNodes(a, b NodeIterator) int {
+ cmp := bytes.Compare(a.Path(), b.Path())
+ if cmp != 0 {
+ return cmp
+ }
+
+ if a.Leaf() && !b.Leaf() {
+ return -1
+ } else if b.Leaf() && !a.Leaf() {
+ return 1
+ }
+
+ cmp = bytes.Compare(a.Hash().Bytes(), b.Hash().Bytes())
+ if cmp != 0 {
+ return cmp
+ }
+
+ return bytes.Compare(a.LeafBlob(), b.LeafBlob())
+}
+
type differenceIterator struct {
a, b NodeIterator // Nodes returned are those in b - a.
eof bool // Indicates a has run out of elements
@@ -321,8 +341,7 @@ func (it *differenceIterator) Next(bool) bool {
}
for {
- apath, bpath := it.a.Path(), it.b.Path()
- switch bytes.Compare(apath, bpath) {
+ switch compareNodes(it.a, it.b) {
case -1:
// b jumped past a; advance a
if !it.a.Next(true) {
@@ -334,15 +353,6 @@ func (it *differenceIterator) Next(bool) bool {
// b is before a
return true
case 0:
- if it.a.Hash() != it.b.Hash() || it.a.Leaf() != it.b.Leaf() {
- // Keys are identical, but hashes or leaf status differs
- return true
- }
- if it.a.Leaf() && it.b.Leaf() && !bytes.Equal(it.a.LeafBlob(), it.b.LeafBlob()) {
- // Both are leaf nodes, but with different values
- return true
- }
-
// a and b are identical; skip this whole subtree if the nodes have hashes
hasHash := it.a.Hash() == common.Hash{}
if !it.b.Next(hasHash) {
@@ -364,3 +374,107 @@ func (it *differenceIterator) Error() error {
}
return it.b.Error()
}
+
+type nodeIteratorHeap []NodeIterator
+
+func (h nodeIteratorHeap) Len() int { return len(h) }
+func (h nodeIteratorHeap) Less(i, j int) bool { return compareNodes(h[i], h[j]) < 0 }
+func (h nodeIteratorHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+func (h *nodeIteratorHeap) Push(x interface{}) { *h = append(*h, x.(NodeIterator)) }
+func (h *nodeIteratorHeap) Pop() interface{} {
+ n := len(*h)
+ x := (*h)[n-1]
+ *h = (*h)[0 : n-1]
+ return x
+}
+
+type unionIterator struct {
+ items *nodeIteratorHeap // Nodes returned are the union of the ones in these iterators
+ count int // Number of nodes scanned across all tries
+ err error // The error, if one has been encountered
+}
+
+// NewUnionIterator constructs a NodeIterator that iterates over elements in the union
+// of the provided NodeIterators. Returns the iterator, and a pointer to an integer
+// recording the number of nodes visited.
+func NewUnionIterator(iters []NodeIterator) (NodeIterator, *int) {
+ h := make(nodeIteratorHeap, len(iters))
+ copy(h, iters)
+ heap.Init(&h)
+
+ ui := &unionIterator{
+ items: &h,
+ }
+ return ui, &ui.count
+}
+
+func (it *unionIterator) Hash() common.Hash {
+ return (*it.items)[0].Hash()
+}
+
+func (it *unionIterator) Parent() common.Hash {
+ return (*it.items)[0].Parent()
+}
+
+func (it *unionIterator) Leaf() bool {
+ return (*it.items)[0].Leaf()
+}
+
+func (it *unionIterator) LeafBlob() []byte {
+ return (*it.items)[0].LeafBlob()
+}
+
+func (it *unionIterator) Path() []byte {
+ return (*it.items)[0].Path()
+}
+
+// Next returns the next node in the union of tries being iterated over.
+//
+// It does this by maintaining a heap of iterators, sorted by the iteration
+// order of their next elements, with one entry for each source trie. Each
+// time Next() is called, it takes the least element from the heap to return,
+// advancing any other iterators that also point to that same element. These
+// iterators are called with descend=false, since we know that any nodes under
+// these nodes will also be duplicates, found in the currently selected iterator.
+// Whenever an iterator is advanced, it is pushed back into the heap if it still
+// has elements remaining.
+//
+// In the case that descend=false - eg, we're asked to ignore all subnodes of the
+// current node - we also advance any iterators in the heap that have the current
+// path as a prefix.
+func (it *unionIterator) Next(descend bool) bool {
+ if len(*it.items) == 0 {
+ return false
+ }
+
+ // Get the next key from the union
+ least := heap.Pop(it.items).(NodeIterator)
+
+ // Skip over other nodes as long as they're identical, or, if we're not descending, as
+ // long as they have the same prefix as the current node.
+ for len(*it.items) > 0 && ((!descend && bytes.HasPrefix((*it.items)[0].Path(), least.Path())) || compareNodes(least, (*it.items)[0]) == 0) {
+ skipped := heap.Pop(it.items).(NodeIterator)
+ // Skip the whole subtree if the nodes have hashes; otherwise just skip this node
+ if skipped.Next(skipped.Hash() == common.Hash{}) {
+ it.count += 1
+ // If there are more elements, push the iterator back on the heap
+ heap.Push(it.items, skipped)
+ }
+ }
+
+ if least.Next(descend) {
+ it.count += 1
+ heap.Push(it.items, least)
+ }
+
+ return len(*it.items) > 0
+}
+
+func (it *unionIterator) Error() error {
+ for i := 0; i < len(*it.items); i++ {
+ if err := (*it.items)[i].Error(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/trie/iterator_test.go b/trie/iterator_test.go
index 0ad9711ed..c101bb7b0 100644
--- a/trie/iterator_test.go
+++ b/trie/iterator_test.go
@@ -117,36 +117,38 @@ func TestNodeIteratorCoverage(t *testing.T) {
}
}
+var testdata1 = []struct{ k, v string }{
+ {"bar", "b"},
+ {"barb", "ba"},
+ {"bars", "bb"},
+ {"bard", "bc"},
+ {"fab", "z"},
+ {"foo", "a"},
+ {"food", "ab"},
+ {"foos", "aa"},
+}
+
+var testdata2 = []struct{ k, v string }{
+ {"aardvark", "c"},
+ {"bar", "b"},
+ {"barb", "bd"},
+ {"bars", "be"},
+ {"fab", "z"},
+ {"foo", "a"},
+ {"foos", "aa"},
+ {"food", "ab"},
+ {"jars", "d"},
+}
+
func TestDifferenceIterator(t *testing.T) {
triea := newEmpty()
- valsa := []struct{ k, v string }{
- {"bar", "b"},
- {"barb", "ba"},
- {"bars", "bb"},
- {"bard", "bc"},
- {"fab", "z"},
- {"foo", "a"},
- {"food", "ab"},
- {"foos", "aa"},
- }
- for _, val := range valsa {
+ for _, val := range testdata1 {
triea.Update([]byte(val.k), []byte(val.v))
}
triea.Commit()
trieb := newEmpty()
- valsb := []struct{ k, v string }{
- {"aardvark", "c"},
- {"bar", "b"},
- {"barb", "bd"},
- {"bars", "be"},
- {"fab", "z"},
- {"foo", "a"},
- {"foos", "aa"},
- {"food", "ab"},
- {"jars", "d"},
- }
- for _, val := range valsb {
+ for _, val := range testdata2 {
trieb.Update([]byte(val.k), []byte(val.v))
}
trieb.Commit()
@@ -166,10 +168,57 @@ func TestDifferenceIterator(t *testing.T) {
}
for _, item := range all {
if found[item.k] != item.v {
- t.Errorf("iterator value mismatch for %s: got %q want %q", item.k, found[item.k], item.v)
+ t.Errorf("iterator value mismatch for %s: got %v want %v", item.k, found[item.k], item.v)
}
}
if len(found) != len(all) {
t.Errorf("iterator count mismatch: got %d values, want %d", len(found), len(all))
}
}
+
+func TestUnionIterator(t *testing.T) {
+ triea := newEmpty()
+ for _, val := range testdata1 {
+ triea.Update([]byte(val.k), []byte(val.v))
+ }
+ triea.Commit()
+
+ trieb := newEmpty()
+ for _, val := range testdata2 {
+ trieb.Update([]byte(val.k), []byte(val.v))
+ }
+ trieb.Commit()
+
+ di, _ := NewUnionIterator([]NodeIterator{NewNodeIterator(triea), NewNodeIterator(trieb)})
+ it := NewIteratorFromNodeIterator(di)
+
+ all := []struct{ k, v string }{
+ {"aardvark", "c"},
+ {"barb", "bd"},
+ {"barb", "ba"},
+ {"bard", "bc"},
+ {"bars", "bb"},
+ {"bars", "be"},
+ {"bar", "b"},
+ {"fab", "z"},
+ {"food", "ab"},
+ {"foos", "aa"},
+ {"foo", "a"},
+ {"jars", "d"},
+ }
+
+ for i, kv := range all {
+ if !it.Next() {
+ t.Errorf("Iterator ends prematurely at element %d", i)
+ }
+ if kv.k != string(it.Key) {
+ t.Errorf("iterator value mismatch for element %d: got key %s want %s", i, it.Key, kv.k)
+ }
+ if kv.v != string(it.Value) {
+ t.Errorf("iterator value mismatch for element %d: got value %s want %s", i, it.Value, kv.v)
+ }
+ }
+ if it.Next() {
+ t.Errorf("Iterator returned extra values.")
+ }
+}
diff --git a/vendor/github.com/naoina/go-stringutil/LICENSE b/vendor/github.com/naoina/go-stringutil/LICENSE
new file mode 100644
index 000000000..0fff1c58b
--- /dev/null
+++ b/vendor/github.com/naoina/go-stringutil/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Naoya Inada <naoina@kuune.org>
+
+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/naoina/go-stringutil/README.md b/vendor/github.com/naoina/go-stringutil/README.md
new file mode 100644
index 000000000..ecf7a5fae
--- /dev/null
+++ b/vendor/github.com/naoina/go-stringutil/README.md
@@ -0,0 +1,13 @@
+# stringutil [![Build Status](https://travis-ci.org/naoina/go-stringutil.svg?branch=master)](https://travis-ci.org/naoina/go-stringutil)
+
+## Installation
+
+ go get -u github.com/naoina/go-stringutil
+
+## Documentation
+
+See https://godoc.org/github.com/naoina/go-stringutil
+
+## License
+
+MIT
diff --git a/vendor/github.com/naoina/go-stringutil/da.go b/vendor/github.com/naoina/go-stringutil/da.go
new file mode 100644
index 000000000..8fe651659
--- /dev/null
+++ b/vendor/github.com/naoina/go-stringutil/da.go
@@ -0,0 +1,253 @@
+package stringutil
+
+import (
+ "fmt"
+ "sort"
+ "unicode/utf8"
+)
+
+const (
+ terminationCharacter = '#'
+)
+
+func mustDoubleArray(da *doubleArray, err error) *doubleArray {
+ if err != nil {
+ panic(err)
+ }
+ return da
+}
+
+func (da *doubleArray) Build(keys []string) error {
+ records := makeRecords(keys)
+ if err := da.build(records, 1, 0, make(map[int]struct{})); err != nil {
+ return err
+ }
+ return nil
+}
+
+type doubleArray struct {
+ bc []baseCheck
+ node []int
+}
+
+func newDoubleArray(keys []string) (*doubleArray, error) {
+ da := &doubleArray{
+ bc: []baseCheck{0},
+ node: []int{-1}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
+ }
+ if err := da.Build(keys); err != nil {
+ return nil, err
+ }
+ return da, nil
+}
+
+// baseCheck contains BASE, CHECK and Extra flags.
+// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
+//
+// BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
+// |----------------------|--|--------|
+// 32 10 8 0
+type baseCheck uint32
+
+func (bc baseCheck) Base() int {
+ return int(bc >> 10)
+}
+
+func (bc *baseCheck) SetBase(base int) {
+ *bc |= baseCheck(base) << 10
+}
+
+func (bc baseCheck) Check() byte {
+ return byte(bc)
+}
+
+func (bc *baseCheck) SetCheck(check byte) {
+ *bc |= baseCheck(check)
+}
+
+func (bc baseCheck) IsEmpty() bool {
+ return bc&0xfffffcff == 0
+}
+
+func (da *doubleArray) Lookup(path string) (length int) {
+ idx := 1
+ tmpIdx := idx
+ for i := 0; i < len(path); i++ {
+ c := path[i]
+ tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c)
+ if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c {
+ break
+ }
+ idx = tmpIdx
+ }
+ if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter {
+ return da.node[da.bc[next].Base()]
+ }
+ return -1
+}
+
+func (da *doubleArray) LookupByBytes(path []byte) (length int) {
+ idx := 1
+ tmpIdx := idx
+ for i := 0; i < len(path); i++ {
+ c := path[i]
+ tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c)
+ if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c {
+ break
+ }
+ idx = tmpIdx
+ }
+ if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter {
+ return da.node[da.bc[next].Base()]
+ }
+ return -1
+}
+
+func (da *doubleArray) build(srcs []record, idx, depth int, usedBase map[int]struct{}) error {
+ sort.Stable(recordSlice(srcs))
+ base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
+ if err != nil {
+ return err
+ }
+ if leaf != nil {
+ da.bc[idx].SetBase(len(da.node))
+ da.node = append(da.node, leaf.value)
+ }
+ for _, sib := range siblings {
+ da.setCheck(da.nextIndex(base, sib.c), sib.c)
+ }
+ for _, sib := range siblings {
+ if err := da.build(srcs[sib.start:sib.end], da.nextIndex(base, sib.c), depth+1, usedBase); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (da *doubleArray) setBase(i, base int) {
+ da.bc[i].SetBase(base)
+}
+
+func (da *doubleArray) setCheck(i int, check byte) {
+ da.bc[i].SetCheck(check)
+}
+
+func (da *doubleArray) findEmptyIndex(start int) int {
+ i := start
+ for ; i < len(da.bc); i++ {
+ if da.bc[i].IsEmpty() {
+ break
+ }
+ }
+ return i
+}
+
+// findBase returns good BASE.
+func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
+ for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
+ base = da.nextIndex(idx, firstChar)
+ if _, used := usedBase[base]; used {
+ continue
+ }
+ i := 0
+ for ; i < len(siblings); i++ {
+ next := da.nextIndex(base, siblings[i].c)
+ if len(da.bc) <= next {
+ da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
+ }
+ if !da.bc[next].IsEmpty() {
+ break
+ }
+ }
+ if i == len(siblings) {
+ break
+ }
+ }
+ usedBase[base] = struct{}{}
+ return base
+}
+
+func (da *doubleArray) arrange(records []record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
+ siblings, leaf, err = makeSiblings(records, depth)
+ if err != nil {
+ return -1, nil, nil, err
+ }
+ if len(siblings) < 1 {
+ return -1, nil, leaf, nil
+ }
+ base = da.findBase(siblings, idx, usedBase)
+ da.setBase(idx, base)
+ return base, siblings, leaf, err
+}
+
+type sibling struct {
+ start int
+ end int
+ c byte
+}
+
+func (da *doubleArray) nextIndex(base int, c byte) int {
+ return base ^ int(c)
+}
+
+func makeSiblings(records []record, depth int) (sib []sibling, leaf *record, err error) {
+ var (
+ pc byte
+ n int
+ )
+ for i, r := range records {
+ if len(r.key) <= depth {
+ leaf = &r
+ continue
+ }
+ c := r.key[depth]
+ switch {
+ case pc < c:
+ sib = append(sib, sibling{start: i, c: c})
+ case pc == c:
+ continue
+ default:
+ return nil, nil, fmt.Errorf("stringutil: BUG: records hasn't been sorted")
+ }
+ if n > 0 {
+ sib[n-1].end = i
+ }
+ pc = c
+ n++
+ }
+ if n == 0 {
+ return nil, leaf, nil
+ }
+ sib[n-1].end = len(records)
+ return sib, leaf, nil
+}
+
+type record struct {
+ key string
+ value int
+}
+
+func makeRecords(srcs []string) (records []record) {
+ termChar := string(terminationCharacter)
+ for _, s := range srcs {
+ records = append(records, record{
+ key: string(s + termChar),
+ value: utf8.RuneCountInString(s),
+ })
+ }
+ return records
+}
+
+type recordSlice []record
+
+func (rs recordSlice) Len() int {
+ return len(rs)
+}
+
+func (rs recordSlice) Less(i, j int) bool {
+ return rs[i].key < rs[j].key
+}
+
+func (rs recordSlice) Swap(i, j int) {
+ rs[i], rs[j] = rs[j], rs[i]
+}
diff --git a/vendor/github.com/naoina/go-stringutil/strings.go b/vendor/github.com/naoina/go-stringutil/strings.go
new file mode 100644
index 000000000..881ca2c8f
--- /dev/null
+++ b/vendor/github.com/naoina/go-stringutil/strings.go
@@ -0,0 +1,320 @@
+package stringutil
+
+import (
+ "sync"
+ "unicode"
+ "unicode/utf8"
+)
+
+var (
+ mu sync.Mutex
+
+ // Based on https://github.com/golang/lint/blob/32a87160691b3c96046c0c678fe57c5bef761456/lint.go#L702
+ commonInitialismMap = map[string]struct{}{
+ "API": struct{}{},
+ "ASCII": struct{}{},
+ "CPU": struct{}{},
+ "CSRF": struct{}{},
+ "CSS": struct{}{},
+ "DNS": struct{}{},
+ "EOF": struct{}{},
+ "GUID": struct{}{},
+ "HTML": struct{}{},
+ "HTTP": struct{}{},
+ "HTTPS": struct{}{},
+ "ID": struct{}{},
+ "IP": struct{}{},
+ "JSON": struct{}{},
+ "LHS": struct{}{},
+ "QPS": struct{}{},
+ "RAM": struct{}{},
+ "RHS": struct{}{},
+ "RPC": struct{}{},
+ "SLA": struct{}{},
+ "SMTP": struct{}{},
+ "SQL": struct{}{},
+ "SSH": struct{}{},
+ "TCP": struct{}{},
+ "TLS": struct{}{},
+ "TTL": struct{}{},
+ "UDP": struct{}{},
+ "UI": struct{}{},
+ "UID": struct{}{},
+ "UUID": struct{}{},
+ "URI": struct{}{},
+ "URL": struct{}{},
+ "UTF8": struct{}{},
+ "VM": struct{}{},
+ "XML": struct{}{},
+ "XSRF": struct{}{},
+ "XSS": struct{}{},
+ }
+ commonInitialisms = keys(commonInitialismMap)
+ commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms))
+ longestLen = longestLength(commonInitialisms)
+ shortestLen = shortestLength(commonInitialisms, longestLen)
+)
+
+// ToUpperCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case.
+// It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'.
+func ToUpperCamelCase(s string) string {
+ if s == "" {
+ return ""
+ }
+ upper := true
+ start := 0
+ result := make([]byte, 0, len(s))
+ var runeBuf [utf8.UTFMax]byte
+ var initialism []byte
+ for _, c := range s {
+ if c == '_' {
+ upper = true
+ candidate := string(result[start:])
+ initialism = initialism[:0]
+ for _, r := range candidate {
+ if r < utf8.RuneSelf {
+ initialism = append(initialism, toUpperASCII(byte(r)))
+ } else {
+ n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r))
+ initialism = append(initialism, runeBuf[:n]...)
+ }
+ }
+ if length := commonInitialism.LookupByBytes(initialism); length > 0 {
+ result = append(result[:start], initialism...)
+ }
+ start = len(result)
+ continue
+ }
+ if upper {
+ if c < utf8.RuneSelf {
+ result = append(result, toUpperASCII(byte(c)))
+ } else {
+ n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(c))
+ result = append(result, runeBuf[:n]...)
+ }
+ upper = false
+ continue
+ }
+ if c < utf8.RuneSelf {
+ result = append(result, byte(c))
+ } else {
+ n := utf8.EncodeRune(runeBuf[:], c)
+ result = append(result, runeBuf[:n]...)
+ }
+ }
+ candidate := string(result[start:])
+ initialism = initialism[:0]
+ for _, r := range candidate {
+ if r < utf8.RuneSelf {
+ initialism = append(initialism, toUpperASCII(byte(r)))
+ } else {
+ n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r))
+ initialism = append(initialism, runeBuf[:n]...)
+ }
+ }
+ if length := commonInitialism.LookupByBytes(initialism); length > 0 {
+ result = append(result[:start], initialism...)
+ }
+ return string(result)
+}
+
+// ToUpperCamelCaseASCII is similar to ToUpperCamelCase, but optimized for
+// only the ASCII characters.
+// ToUpperCamelCaseASCII is faster than ToUpperCamelCase, but doesn't work if
+// contains non-ASCII characters.
+func ToUpperCamelCaseASCII(s string) string {
+ if s == "" {
+ return ""
+ }
+ upper := true
+ start := 0
+ result := make([]byte, 0, len(s))
+ var initialism []byte
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ if c == '_' {
+ upper = true
+ candidate := result[start:]
+ initialism = initialism[:0]
+ for _, b := range candidate {
+ initialism = append(initialism, toUpperASCII(b))
+ }
+ if length := commonInitialism.LookupByBytes(initialism); length > 0 {
+ result = append(result[:start], initialism...)
+ }
+ start = len(result)
+ continue
+ }
+ if upper {
+ result = append(result, toUpperASCII(c))
+ upper = false
+ continue
+ }
+ result = append(result, c)
+ }
+ candidate := result[start:]
+ initialism = initialism[:0]
+ for _, b := range candidate {
+ initialism = append(initialism, toUpperASCII(b))
+ }
+ if length := commonInitialism.LookupByBytes(initialism); length > 0 {
+ result = append(result[:start], initialism...)
+ }
+ return string(result)
+}
+
+// ToSnakeCase returns a copy of the string s with all Unicode letters mapped to their snake case.
+// It will insert letter of '_' at position of previous letter of uppercase and all
+// letters convert to lower case.
+// ToSnakeCase does not insert '_' letter into a common initialism word like ID, URL and so on.
+func ToSnakeCase(s string) string {
+ if s == "" {
+ return ""
+ }
+ result := make([]byte, 0, len(s))
+ var runeBuf [utf8.UTFMax]byte
+ var j, skipCount int
+ for i, c := range s {
+ if i < skipCount {
+ continue
+ }
+ if unicode.IsUpper(c) {
+ if i != 0 {
+ result = append(result, '_')
+ }
+ next := nextIndex(j, len(s))
+ if length := commonInitialism.Lookup(s[j:next]); length > 0 {
+ for _, r := range s[j : j+length] {
+ if r < utf8.RuneSelf {
+ result = append(result, toLowerASCII(byte(r)))
+ } else {
+ n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(r))
+ result = append(result, runeBuf[:n]...)
+ }
+ }
+ j += length - 1
+ skipCount = i + length
+ continue
+ }
+ }
+ if c < utf8.RuneSelf {
+ result = append(result, toLowerASCII(byte(c)))
+ } else {
+ n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(c))
+ result = append(result, runeBuf[:n]...)
+ }
+ j++
+ }
+ return string(result)
+}
+
+// ToSnakeCaseASCII is similar to ToSnakeCase, but optimized for only the ASCII
+// characters.
+// ToSnakeCaseASCII is faster than ToSnakeCase, but doesn't work correctly if
+// contains non-ASCII characters.
+func ToSnakeCaseASCII(s string) string {
+ if s == "" {
+ return ""
+ }
+ result := make([]byte, 0, len(s))
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ if isUpperASCII(c) {
+ if i != 0 {
+ result = append(result, '_')
+ }
+ if k := i + shortestLen - 1; k < len(s) && isUpperASCII(s[k]) {
+ if length := commonInitialism.Lookup(s[i:nextIndex(i, len(s))]); length > 0 {
+ for j, buf := 0, s[i:i+length]; j < len(buf); j++ {
+ result = append(result, toLowerASCII(buf[j]))
+ }
+ i += length - 1
+ continue
+ }
+ }
+ }
+ result = append(result, toLowerASCII(c))
+ }
+ return string(result)
+}
+
+// AddCommonInitialism adds ss to list of common initialisms.
+func AddCommonInitialism(ss ...string) {
+ mu.Lock()
+ defer mu.Unlock()
+ for _, s := range ss {
+ commonInitialismMap[s] = struct{}{}
+ }
+ commonInitialisms = keys(commonInitialismMap)
+ commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms))
+ longestLen = longestLength(commonInitialisms)
+ shortestLen = shortestLength(commonInitialisms, longestLen)
+}
+
+// DelCommonInitialism deletes ss from list of common initialisms.
+func DelCommonInitialism(ss ...string) {
+ mu.Lock()
+ defer mu.Unlock()
+ for _, s := range ss {
+ delete(commonInitialismMap, s)
+ }
+ commonInitialisms = keys(commonInitialismMap)
+ commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms))
+ longestLen = longestLength(commonInitialisms)
+ shortestLen = shortestLength(commonInitialisms, longestLen)
+}
+
+func isUpperASCII(c byte) bool {
+ return 'A' <= c && c <= 'Z'
+}
+
+func isLowerASCII(c byte) bool {
+ return 'a' <= c && c <= 'z'
+}
+
+func toUpperASCII(c byte) byte {
+ if isLowerASCII(c) {
+ return c - ('a' - 'A')
+ }
+ return c
+}
+
+func toLowerASCII(c byte) byte {
+ if isUpperASCII(c) {
+ return c + 'a' - 'A'
+ }
+ return c
+}
+
+func nextIndex(i, maxlen int) int {
+ if n := i + longestLen; n < maxlen {
+ return n
+ }
+ return maxlen
+}
+
+func keys(m map[string]struct{}) []string {
+ result := make([]string, 0, len(m))
+ for k := range m {
+ result = append(result, k)
+ }
+ return result
+}
+
+func shortestLength(strs []string, shortest int) int {
+ for _, s := range strs {
+ if candidate := utf8.RuneCountInString(s); candidate < shortest {
+ shortest = candidate
+ }
+ }
+ return shortest
+}
+
+func longestLength(strs []string) (longest int) {
+ for _, s := range strs {
+ if candidate := utf8.RuneCountInString(s); candidate > longest {
+ longest = candidate
+ }
+ }
+ return longest
+}
diff --git a/vendor/github.com/naoina/toml/LICENSE b/vendor/github.com/naoina/toml/LICENSE
new file mode 100644
index 000000000..e65039ad8
--- /dev/null
+++ b/vendor/github.com/naoina/toml/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Naoya Inada <naoina@kuune.org>
+
+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/naoina/toml/README.md b/vendor/github.com/naoina/toml/README.md
new file mode 100644
index 000000000..1c0143348
--- /dev/null
+++ b/vendor/github.com/naoina/toml/README.md
@@ -0,0 +1,392 @@
+# TOML parser and encoder library for Golang [![Build Status](https://travis-ci.org/naoina/toml.png?branch=master)](https://travis-ci.org/naoina/toml)
+
+[TOML](https://github.com/toml-lang/toml) parser and encoder library for [Golang](http://golang.org/).
+
+This library is compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md).
+
+## Installation
+
+ go get -u github.com/naoina/toml
+
+## Usage
+
+The following TOML save as `example.toml`.
+
+```toml
+# This is a TOML document. Boom.
+
+title = "TOML Example"
+
+[owner]
+name = "Lance Uppercut"
+dob = 1979-05-27T07:32:00-08:00 # First class dates? Why not?
+
+[database]
+server = "192.168.1.1"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = "10.0.0.1"
+ dc = "eqdc10"
+
+ [servers.beta]
+ ip = "10.0.0.2"
+ dc = "eqdc10"
+
+[clients]
+data = [ ["gamma", "delta"], [1, 2] ]
+
+# Line breaks are OK when inside arrays
+hosts = [
+ "alpha",
+ "omega"
+]
+```
+
+Then above TOML will mapping to `tomlConfig` struct using `toml.Unmarshal`.
+
+```go
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "time"
+
+ "github.com/naoina/toml"
+)
+
+type tomlConfig struct {
+ Title string
+ Owner struct {
+ Name string
+ Dob time.Time
+ }
+ Database struct {
+ Server string
+ Ports []int
+ ConnectionMax uint
+ Enabled bool
+ }
+ Servers map[string]ServerInfo
+ Clients struct {
+ Data [][]interface{}
+ Hosts []string
+ }
+}
+
+type ServerInfo struct {
+ IP net.IP
+ DC string
+}
+
+func main() {
+ f, err := os.Open("example.toml")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ var config Config
+ if err := toml.NewDecoder(f).Decode(&config); err != nil {
+ panic(err)
+ }
+
+ // then to use the unmarshaled config...
+ fmt.Println("IP of server 'alpha':", config.Servers["alpha"].IP)
+}
+```
+
+## Mappings
+
+A key and value of TOML will map to the corresponding field.
+The fields of struct for mapping must be exported.
+
+The rules of the mapping of key are following:
+
+#### Exact matching
+
+```toml
+timeout_seconds = 256
+```
+
+```go
+type Config struct {
+ Timeout_seconds int
+}
+```
+
+#### Camelcase matching
+
+```toml
+server_name = "srv1"
+```
+
+```go
+type Config struct {
+ ServerName string
+}
+```
+
+#### Uppercase matching
+
+```toml
+ip = "10.0.0.1"
+```
+
+```go
+type Config struct {
+ IP string
+}
+```
+
+See the following examples for the value mappings.
+
+### String
+
+```toml
+val = "string"
+```
+
+```go
+type Config struct {
+ Val string
+}
+```
+
+### Integer
+
+```toml
+val = 100
+```
+
+```go
+type Config struct {
+ Val int
+}
+```
+
+All types that can be used are following:
+
+* int8 (from `-128` to `127`)
+* int16 (from `-32768` to `32767`)
+* int32 (from `-2147483648` to `2147483647`)
+* int64 (from `-9223372036854775808` to `9223372036854775807`)
+* int (same as `int32` on 32bit environment, or `int64` on 64bit environment)
+* uint8 (from `0` to `255`)
+* uint16 (from `0` to `65535`)
+* uint32 (from `0` to `4294967295`)
+* uint64 (from `0` to `18446744073709551615`)
+* uint (same as `uint` on 32bit environment, or `uint64` on 64bit environment)
+
+### Float
+
+```toml
+val = 3.1415
+```
+
+```go
+type Config struct {
+ Val float32
+}
+```
+
+All types that can be used are following:
+
+* float32
+* float64
+
+### Boolean
+
+```toml
+val = true
+```
+
+```go
+type Config struct {
+ Val bool
+}
+```
+
+### Datetime
+
+```toml
+val = 2014-09-28T21:27:39Z
+```
+
+```go
+type Config struct {
+ Val time.Time
+}
+```
+
+### Array
+
+```toml
+val = ["a", "b", "c"]
+```
+
+```go
+type Config struct {
+ Val []string
+}
+```
+
+Also following examples all can be mapped:
+
+```toml
+val1 = [1, 2, 3]
+val2 = [["a", "b"], ["c", "d"]]
+val3 = [[1, 2, 3], ["a", "b", "c"]]
+val4 = [[1, 2, 3], [["a", "b"], [true, false]]]
+```
+
+```go
+type Config struct {
+ Val1 []int
+ Val2 [][]string
+ Val3 [][]interface{}
+ Val4 [][]interface{}
+}
+```
+
+### Table
+
+```toml
+[server]
+type = "app"
+
+ [server.development]
+ ip = "10.0.0.1"
+
+ [server.production]
+ ip = "10.0.0.2"
+```
+
+```go
+type Config struct {
+ Server map[string]Server
+}
+
+type Server struct {
+ IP string
+}
+```
+
+You can also use the following struct instead of map of struct.
+
+```go
+type Config struct {
+ Server struct {
+ Development Server
+ Production Server
+ }
+}
+
+type Server struct {
+ IP string
+}
+```
+
+### Array of Tables
+
+```toml
+[[fruit]]
+ name = "apple"
+
+ [fruit.physical]
+ color = "red"
+ shape = "round"
+
+ [[fruit.variety]]
+ name = "red delicious"
+
+ [[fruit.variety]]
+ name = "granny smith"
+
+[[fruit]]
+ name = "banana"
+
+ [[fruit.variety]]
+ name = "plantain"
+```
+
+```go
+type Config struct {
+ Fruit []struct {
+ Name string
+ Physical struct {
+ Color string
+ Shape string
+ }
+ Variety []struct {
+ Name string
+ }
+ }
+}
+```
+
+### Using the `encoding.TextUnmarshaler` interface
+
+Package toml supports `encoding.TextUnmarshaler` (and `encoding.TextMarshaler`). You can
+use it to apply custom marshaling rules for certain types. The `UnmarshalText` method is
+called with the value text found in the TOML input. TOML strings are passed unquoted.
+
+```toml
+duration = "10s"
+```
+
+```go
+import time
+
+type Duration time.Duration
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (d *Duration) UnmarshalText(data []byte) error {
+ duration, err := time.ParseDuration(string(data))
+ if err == nil {
+ *d = Duration(duration)
+ }
+ return err
+}
+
+// MarshalText implements encoding.TextMarshaler
+func (d Duration) MarshalText() ([]byte, error) {
+ return []byte(time.Duration(d).String()), nil
+}
+
+type ConfigWithDuration struct {
+ Duration Duration
+}
+```
+### Using the `toml.UnmarshalerRec` interface
+
+You can also override marshaling rules specifically for TOML using the `UnmarshalerRec`
+and `MarshalerRec` interfaces. These are useful if you want to control how structs or
+arrays are handled. You can apply additional validation or set unexported struct fields.
+
+Note: `encoding.TextUnmarshaler` and `encoding.TextMarshaler` should be preferred for
+simple (scalar) values because they're also compatible with other formats like JSON or
+YAML.
+
+[See the UnmarshalerRec example](https://godoc.org/github.com/naoina/toml/#example_UnmarshalerRec).
+
+### Using the `toml.Unmarshaler` interface
+
+If you want to deal with raw TOML syntax, use the `Unmarshaler` and `Marshaler`
+interfaces. Their input and output is raw TOML syntax. As such, these interfaces are
+useful if you want to handle TOML at the syntax level.
+
+[See the Unmarshaler example](https://godoc.org/github.com/naoina/toml/#example_Unmarshaler).
+
+## API documentation
+
+See [Godoc](http://godoc.org/github.com/naoina/toml).
+
+## License
+
+MIT
diff --git a/vendor/github.com/naoina/toml/ast/ast.go b/vendor/github.com/naoina/toml/ast/ast.go
new file mode 100644
index 000000000..4868e2e1a
--- /dev/null
+++ b/vendor/github.com/naoina/toml/ast/ast.go
@@ -0,0 +1,192 @@
+package ast
+
+import (
+ "strconv"
+ "strings"
+ "time"
+)
+
+type Position struct {
+ Begin int
+ End int
+}
+
+type Value interface {
+ Pos() int
+ End() int
+ Source() string
+}
+
+type String struct {
+ Position Position
+ Value string
+ Data []rune
+}
+
+func (s *String) Pos() int {
+ return s.Position.Begin
+}
+
+func (s *String) End() int {
+ return s.Position.End
+}
+
+func (s *String) Source() string {
+ return string(s.Data)
+}
+
+type Integer struct {
+ Position Position
+ Value string
+ Data []rune
+}
+
+func (i *Integer) Pos() int {
+ return i.Position.Begin
+}
+
+func (i *Integer) End() int {
+ return i.Position.End
+}
+
+func (i *Integer) Source() string {
+ return string(i.Data)
+}
+
+func (i *Integer) Int() (int64, error) {
+ return strconv.ParseInt(i.Value, 10, 64)
+}
+
+type Float struct {
+ Position Position
+ Value string
+ Data []rune
+}
+
+func (f *Float) Pos() int {
+ return f.Position.Begin
+}
+
+func (f *Float) End() int {
+ return f.Position.End
+}
+
+func (f *Float) Source() string {
+ return string(f.Data)
+}
+
+func (f *Float) Float() (float64, error) {
+ return strconv.ParseFloat(f.Value, 64)
+}
+
+type Boolean struct {
+ Position Position
+ Value string
+ Data []rune
+}
+
+func (b *Boolean) Pos() int {
+ return b.Position.Begin
+}
+
+func (b *Boolean) End() int {
+ return b.Position.End
+}
+
+func (b *Boolean) Source() string {
+ return string(b.Data)
+}
+
+func (b *Boolean) Boolean() (bool, error) {
+ return strconv.ParseBool(b.Value)
+}
+
+type Datetime struct {
+ Position Position
+ Value string
+ Data []rune
+}
+
+func (d *Datetime) Pos() int {
+ return d.Position.Begin
+}
+
+func (d *Datetime) End() int {
+ return d.Position.End
+}
+
+func (d *Datetime) Source() string {
+ return string(d.Data)
+}
+
+func (d *Datetime) Time() (time.Time, error) {
+ switch {
+ case !strings.Contains(d.Value, ":"):
+ return time.Parse("2006-01-02", d.Value)
+ case !strings.Contains(d.Value, "-"):
+ return time.Parse("15:04:05.999999999", d.Value)
+ default:
+ return time.Parse(time.RFC3339Nano, d.Value)
+ }
+}
+
+type Array struct {
+ Position Position
+ Value []Value
+ Data []rune
+}
+
+func (a *Array) Pos() int {
+ return a.Position.Begin
+}
+
+func (a *Array) End() int {
+ return a.Position.End
+}
+
+func (a *Array) Source() string {
+ return string(a.Data)
+}
+
+type TableType uint8
+
+const (
+ TableTypeNormal TableType = iota
+ TableTypeArray
+)
+
+var tableTypes = [...]string{
+ "normal",
+ "array",
+}
+
+func (t TableType) String() string {
+ return tableTypes[t]
+}
+
+type Table struct {
+ Position Position
+ Line int
+ Name string
+ Fields map[string]interface{}
+ Type TableType
+ Data []rune
+}
+
+func (t *Table) Pos() int {
+ return t.Position.Begin
+}
+
+func (t *Table) End() int {
+ return t.Position.End
+}
+
+func (t *Table) Source() string {
+ return string(t.Data)
+}
+
+type KeyValue struct {
+ Key string
+ Value Value
+ Line int
+}
diff --git a/vendor/github.com/naoina/toml/config.go b/vendor/github.com/naoina/toml/config.go
new file mode 100644
index 000000000..06bb9493b
--- /dev/null
+++ b/vendor/github.com/naoina/toml/config.go
@@ -0,0 +1,86 @@
+package toml
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+
+ stringutil "github.com/naoina/go-stringutil"
+ "github.com/naoina/toml/ast"
+)
+
+// Config contains options for encoding and decoding.
+type Config struct {
+ // NormFieldName is used to match TOML keys to struct fields. The function runs for
+ // both input keys and struct field names and should return a string that makes the
+ // two match. You must set this field to use the decoder.
+ //
+ // Example: The function in the default config removes _ and lowercases all keys. This
+ // allows a key called 'api_key' to match the struct field 'APIKey' because both are
+ // normalized to 'apikey'.
+ //
+ // Note that NormFieldName is not used for fields which define a TOML
+ // key through the struct tag.
+ NormFieldName func(typ reflect.Type, keyOrField string) string
+
+ // FieldToKey determines the TOML key of a struct field when encoding.
+ // You must set this field to use the encoder.
+ //
+ // Note that FieldToKey is not used for fields which define a TOML
+ // key through the struct tag.
+ FieldToKey func(typ reflect.Type, field string) string
+
+ // MissingField, if non-nil, is called when the decoder encounters a key for which no
+ // matching struct field exists. The default behavior is to return an error.
+ MissingField func(typ reflect.Type, key string) error
+}
+
+// DefaultConfig contains the default options for encoding and decoding.
+// Snake case (i.e. 'foo_bar') is used for key names.
+var DefaultConfig = Config{
+ NormFieldName: defaultNormFieldName,
+ FieldToKey: snakeCase,
+}
+
+func defaultNormFieldName(typ reflect.Type, s string) string {
+ return strings.Replace(strings.ToLower(s), "_", "", -1)
+}
+
+func snakeCase(typ reflect.Type, s string) string {
+ return stringutil.ToSnakeCase(s)
+}
+
+func defaultMissingField(typ reflect.Type, key string) error {
+ return fmt.Errorf("field corresponding to `%s' is not defined in %v", key, typ)
+}
+
+// NewEncoder returns a new Encoder that writes to w.
+// It is shorthand for DefaultConfig.NewEncoder(w).
+func NewEncoder(w io.Writer) *Encoder {
+ return DefaultConfig.NewEncoder(w)
+}
+
+// Marshal returns the TOML encoding of v.
+// It is shorthand for DefaultConfig.Marshal(v).
+func Marshal(v interface{}) ([]byte, error) {
+ return DefaultConfig.Marshal(v)
+}
+
+// Unmarshal parses the TOML data and stores the result in the value pointed to by v.
+// It is shorthand for DefaultConfig.Unmarshal(data, v).
+func Unmarshal(data []byte, v interface{}) error {
+ return DefaultConfig.Unmarshal(data, v)
+}
+
+// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
+// It is shorthand for DefaultConfig.UnmarshalTable(t, v).
+func UnmarshalTable(t *ast.Table, v interface{}) error {
+ return DefaultConfig.UnmarshalTable(t, v)
+}
+
+// NewDecoder returns a new Decoder that reads from r.
+// It is shorthand for DefaultConfig.NewDecoder(r).
+func NewDecoder(r io.Reader) *Decoder {
+ return DefaultConfig.NewDecoder(r)
+}
diff --git a/vendor/github.com/naoina/toml/decode.go b/vendor/github.com/naoina/toml/decode.go
new file mode 100644
index 000000000..b3c169eb1
--- /dev/null
+++ b/vendor/github.com/naoina/toml/decode.go
@@ -0,0 +1,478 @@
+// Package toml encodes and decodes the TOML configuration format using reflection.
+//
+// This library is compatible with TOML version v0.4.0.
+package toml
+
+import (
+ "encoding"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/naoina/toml/ast"
+)
+
+const (
+ tableSeparator = '.'
+)
+
+var (
+ escapeReplacer = strings.NewReplacer(
+ "\b", "\\n",
+ "\f", "\\f",
+ "\n", "\\n",
+ "\r", "\\r",
+ "\t", "\\t",
+ )
+ underscoreReplacer = strings.NewReplacer(
+ "_", "",
+ )
+)
+
+var timeType = reflect.TypeOf(time.Time{})
+
+// Unmarshal parses the TOML data and stores the result in the value pointed to by v.
+//
+// Unmarshal will mapped to v that according to following rules:
+//
+// TOML strings to string
+// TOML integers to any int type
+// TOML floats to float32 or float64
+// TOML booleans to bool
+// TOML datetimes to time.Time
+// TOML arrays to any type of slice
+// TOML tables to struct or map
+// TOML array tables to slice of struct or map
+func (cfg *Config) Unmarshal(data []byte, v interface{}) error {
+ table, err := Parse(data)
+ if err != nil {
+ return err
+ }
+ if err := cfg.UnmarshalTable(table, v); err != nil {
+ return err
+ }
+ return nil
+}
+
+// A Decoder reads and decodes TOML from an input stream.
+type Decoder struct {
+ r io.Reader
+ cfg *Config
+}
+
+// NewDecoder returns a new Decoder that reads from r.
+// Note that it reads all from r before parsing it.
+func (cfg *Config) NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{r, cfg}
+}
+
+// Decode parses the TOML data from its input and stores it in the value pointed to by v.
+// See the documentation for Unmarshal for details about the conversion of TOML into a Go value.
+func (d *Decoder) Decode(v interface{}) error {
+ b, err := ioutil.ReadAll(d.r)
+ if err != nil {
+ return err
+ }
+ return d.cfg.Unmarshal(b, v)
+}
+
+// UnmarshalerRec may be implemented by types to customize their behavior when being
+// unmarshaled from TOML. You can use it to implement custom validation or to set
+// unexported fields.
+//
+// UnmarshalTOML receives a function that can be called to unmarshal the original TOML
+// value into a field or variable. It is safe to call the function more than once if
+// necessary.
+type UnmarshalerRec interface {
+ UnmarshalTOML(fn func(interface{}) error) error
+}
+
+// Unmarshaler can be used to capture and process raw TOML source of a table or value.
+// UnmarshalTOML must copy the input if it wishes to retain it after returning.
+//
+// Note: this interface is retained for backwards compatibility. You probably want
+// to implement encoding.TextUnmarshaler or UnmarshalerRec instead.
+type Unmarshaler interface {
+ UnmarshalTOML(input []byte) error
+}
+
+// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
+//
+// UnmarshalTable will mapped to v that according to following rules:
+//
+// TOML strings to string
+// TOML integers to any int type
+// TOML floats to float32 or float64
+// TOML booleans to bool
+// TOML datetimes to time.Time
+// TOML arrays to any type of slice
+// TOML tables to struct or map
+// TOML array tables to slice of struct or map
+func (cfg *Config) UnmarshalTable(t *ast.Table, v interface{}) error {
+ rv := reflect.ValueOf(v)
+ toplevelMap := rv.Kind() == reflect.Map
+ if (!toplevelMap && rv.Kind() != reflect.Ptr) || rv.IsNil() {
+ return &invalidUnmarshalError{reflect.TypeOf(v)}
+ }
+ return unmarshalTable(cfg, rv, t, toplevelMap)
+}
+
+// used for UnmarshalerRec.
+func unmarshalTableOrValue(cfg *Config, rv reflect.Value, av interface{}) error {
+ if (rv.Kind() != reflect.Ptr && rv.Kind() != reflect.Map) || rv.IsNil() {
+ return &invalidUnmarshalError{rv.Type()}
+ }
+ rv = indirect(rv)
+
+ switch av.(type) {
+ case *ast.KeyValue, *ast.Table, []*ast.Table:
+ if err := unmarshalField(cfg, rv, av); err != nil {
+ return lineError(fieldLineNumber(av), err)
+ }
+ return nil
+ case ast.Value:
+ return setValue(cfg, rv, av.(ast.Value))
+ default:
+ panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
+ }
+}
+
+// unmarshalTable unmarshals the fields of a table into a struct or map.
+//
+// toplevelMap is true when rv is an (unadressable) map given to UnmarshalTable. In this
+// (special) case, the map is used as-is instead of creating a new map.
+func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error {
+ rv = indirect(rv)
+ if err, ok := setUnmarshaler(cfg, rv, t); ok {
+ return lineError(t.Line, err)
+ }
+ switch {
+ case rv.Kind() == reflect.Struct:
+ fc := makeFieldCache(cfg, rv.Type())
+ for key, fieldAst := range t.Fields {
+ fv, fieldName, err := fc.findField(cfg, rv, key)
+ if err != nil {
+ return lineError(fieldLineNumber(fieldAst), err)
+ }
+ if fv.IsValid() {
+ if err := unmarshalField(cfg, fv, fieldAst); err != nil {
+ return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, err)
+ }
+ }
+ }
+ case rv.Kind() == reflect.Map || isEface(rv):
+ m := rv
+ if !toplevelMap {
+ if rv.Kind() == reflect.Interface {
+ m = reflect.ValueOf(make(map[string]interface{}))
+ } else {
+ m = reflect.MakeMap(rv.Type())
+ }
+ }
+ elemtyp := m.Type().Elem()
+ for key, fieldAst := range t.Fields {
+ kv, err := unmarshalMapKey(m.Type().Key(), key)
+ if err != nil {
+ return lineError(fieldLineNumber(fieldAst), err)
+ }
+ fv := reflect.New(elemtyp).Elem()
+ if err := unmarshalField(cfg, fv, fieldAst); err != nil {
+ return lineError(fieldLineNumber(fieldAst), err)
+ }
+ m.SetMapIndex(kv, fv)
+ }
+ if !toplevelMap {
+ rv.Set(m)
+ }
+ default:
+ return lineError(t.Line, &unmarshalTypeError{"table", "struct or map", rv.Type()})
+ }
+ return nil
+}
+
+func fieldLineNumber(fieldAst interface{}) int {
+ switch av := fieldAst.(type) {
+ case *ast.KeyValue:
+ return av.Line
+ case *ast.Table:
+ return av.Line
+ case []*ast.Table:
+ return av[0].Line
+ default:
+ panic(fmt.Sprintf("BUG: unhandled node type %T", fieldAst))
+ }
+}
+
+func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error {
+ switch av := fieldAst.(type) {
+ case *ast.KeyValue:
+ return setValue(cfg, rv, av.Value)
+ case *ast.Table:
+ return unmarshalTable(cfg, rv, av, false)
+ case []*ast.Table:
+ rv = indirect(rv)
+ if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok {
+ return err
+ }
+ var slice reflect.Value
+ switch {
+ case rv.Kind() == reflect.Slice:
+ slice = reflect.MakeSlice(rv.Type(), len(av), len(av))
+ case isEface(rv):
+ slice = reflect.ValueOf(make([]interface{}, len(av)))
+ default:
+ return &unmarshalTypeError{"array table", "slice", rv.Type()}
+ }
+ for i, tbl := range av {
+ vv := reflect.New(slice.Type().Elem()).Elem()
+ if err := unmarshalTable(cfg, vv, tbl, false); err != nil {
+ return err
+ }
+ slice.Index(i).Set(vv)
+ }
+ rv.Set(slice)
+ default:
+ panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
+ }
+ return nil
+}
+
+func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) {
+ rv := reflect.New(typ).Elem()
+ if u, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok {
+ return rv, u.UnmarshalText([]byte(key))
+ }
+ switch typ.Kind() {
+ case reflect.String:
+ rv.SetString(key)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i, err := strconv.ParseInt(key, 10, int(typ.Size()*8))
+ if err != nil {
+ return rv, convertNumError(typ.Kind(), err)
+ }
+ rv.SetInt(i)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ i, err := strconv.ParseUint(key, 10, int(typ.Size()*8))
+ if err != nil {
+ return rv, convertNumError(typ.Kind(), err)
+ }
+ rv.SetUint(i)
+ default:
+ return rv, fmt.Errorf("invalid map key type %s", typ)
+ }
+ return rv, nil
+}
+
+func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error {
+ lhs = indirect(lhs)
+ if err, ok := setUnmarshaler(cfg, lhs, val); ok {
+ return err
+ }
+ if err, ok := setTextUnmarshaler(lhs, val); ok {
+ return err
+ }
+ switch v := val.(type) {
+ case *ast.Integer:
+ return setInt(lhs, v)
+ case *ast.Float:
+ return setFloat(lhs, v)
+ case *ast.String:
+ return setString(lhs, v)
+ case *ast.Boolean:
+ return setBoolean(lhs, v)
+ case *ast.Datetime:
+ return setDatetime(lhs, v)
+ case *ast.Array:
+ return setArray(cfg, lhs, v)
+ default:
+ panic(fmt.Sprintf("BUG: unhandled node type %T", v))
+ }
+}
+
+func indirect(rv reflect.Value) reflect.Value {
+ for rv.Kind() == reflect.Ptr {
+ if rv.IsNil() {
+ rv.Set(reflect.New(rv.Type().Elem()))
+ }
+ rv = rv.Elem()
+ }
+ return rv
+}
+
+func setUnmarshaler(cfg *Config, lhs reflect.Value, av interface{}) (error, bool) {
+ if lhs.CanAddr() {
+ if u, ok := lhs.Addr().Interface().(UnmarshalerRec); ok {
+ err := u.UnmarshalTOML(func(v interface{}) error {
+ return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av)
+ })
+ return err, true
+ }
+ if u, ok := lhs.Addr().Interface().(Unmarshaler); ok {
+ return u.UnmarshalTOML(unmarshalerSource(av)), true
+ }
+ }
+ return nil, false
+}
+
+func unmarshalerSource(av interface{}) []byte {
+ var source []byte
+ switch av := av.(type) {
+ case []*ast.Table:
+ for i, tab := range av {
+ source = append(source, tab.Source()...)
+ if i != len(av)-1 {
+ source = append(source, '\n')
+ }
+ }
+ case ast.Value:
+ source = []byte(av.Source())
+ default:
+ panic(fmt.Sprintf("BUG: unhandled node type %T", av))
+ }
+ return source
+}
+
+func setTextUnmarshaler(lhs reflect.Value, val ast.Value) (error, bool) {
+ if !lhs.CanAddr() {
+ return nil, false
+ }
+ u, ok := lhs.Addr().Interface().(encoding.TextUnmarshaler)
+ if !ok || lhs.Type() == timeType {
+ return nil, false
+ }
+ var data string
+ switch val := val.(type) {
+ case *ast.Array:
+ return &unmarshalTypeError{"array", "", lhs.Type()}, true
+ case *ast.String:
+ data = val.Value
+ default:
+ data = val.Source()
+ }
+ return u.UnmarshalText([]byte(data)), true
+}
+
+func setInt(fv reflect.Value, v *ast.Integer) error {
+ k := fv.Kind()
+ switch {
+ case k >= reflect.Int && k <= reflect.Int64:
+ i, err := strconv.ParseInt(v.Value, 10, int(fv.Type().Size()*8))
+ if err != nil {
+ return convertNumError(fv.Kind(), err)
+ }
+ fv.SetInt(i)
+ case k >= reflect.Uint && k <= reflect.Uintptr:
+ i, err := strconv.ParseUint(v.Value, 10, int(fv.Type().Size()*8))
+ if err != nil {
+ return convertNumError(fv.Kind(), err)
+ }
+ fv.SetUint(i)
+ case isEface(fv):
+ i, err := strconv.ParseInt(v.Value, 10, 64)
+ if err != nil {
+ return convertNumError(reflect.Int64, err)
+ }
+ fv.Set(reflect.ValueOf(i))
+ default:
+ return &unmarshalTypeError{"integer", "", fv.Type()}
+ }
+ return nil
+}
+
+func setFloat(fv reflect.Value, v *ast.Float) error {
+ f, err := v.Float()
+ if err != nil {
+ return err
+ }
+ switch {
+ case fv.Kind() == reflect.Float32 || fv.Kind() == reflect.Float64:
+ if fv.OverflowFloat(f) {
+ return &overflowError{fv.Kind(), v.Value}
+ }
+ fv.SetFloat(f)
+ case isEface(fv):
+ fv.Set(reflect.ValueOf(f))
+ default:
+ return &unmarshalTypeError{"float", "", fv.Type()}
+ }
+ return nil
+}
+
+func setString(fv reflect.Value, v *ast.String) error {
+ switch {
+ case fv.Kind() == reflect.String:
+ fv.SetString(v.Value)
+ case isEface(fv):
+ fv.Set(reflect.ValueOf(v.Value))
+ default:
+ return &unmarshalTypeError{"string", "", fv.Type()}
+ }
+ return nil
+}
+
+func setBoolean(fv reflect.Value, v *ast.Boolean) error {
+ b, _ := v.Boolean()
+ switch {
+ case fv.Kind() == reflect.Bool:
+ fv.SetBool(b)
+ case isEface(fv):
+ fv.Set(reflect.ValueOf(b))
+ default:
+ return &unmarshalTypeError{"boolean", "", fv.Type()}
+ }
+ return nil
+}
+
+func setDatetime(rv reflect.Value, v *ast.Datetime) error {
+ t, err := v.Time()
+ if err != nil {
+ return err
+ }
+ if !timeType.AssignableTo(rv.Type()) {
+ return &unmarshalTypeError{"datetime", "", rv.Type()}
+ }
+ rv.Set(reflect.ValueOf(t))
+ return nil
+}
+
+func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error {
+ var slicetyp reflect.Type
+ switch {
+ case rv.Kind() == reflect.Slice:
+ slicetyp = rv.Type()
+ case isEface(rv):
+ slicetyp = reflect.SliceOf(rv.Type())
+ default:
+ return &unmarshalTypeError{"array", "slice", rv.Type()}
+ }
+
+ if len(v.Value) == 0 {
+ // Ensure defined slices are always set to a non-nil value.
+ rv.Set(reflect.MakeSlice(slicetyp, 0, 0))
+ return nil
+ }
+
+ tomltyp := reflect.TypeOf(v.Value[0])
+ slice := reflect.MakeSlice(slicetyp, len(v.Value), len(v.Value))
+ typ := slicetyp.Elem()
+ for i, vv := range v.Value {
+ if i > 0 && tomltyp != reflect.TypeOf(vv) {
+ return errArrayMultiType
+ }
+ tmp := reflect.New(typ).Elem()
+ if err := setValue(cfg, tmp, vv); err != nil {
+ return err
+ }
+ slice.Index(i).Set(tmp)
+ }
+ rv.Set(slice)
+ return nil
+}
+
+func isEface(rv reflect.Value) bool {
+ return rv.Kind() == reflect.Interface && rv.Type().NumMethod() == 0
+}
diff --git a/vendor/github.com/naoina/toml/encode.go b/vendor/github.com/naoina/toml/encode.go
new file mode 100644
index 000000000..ae6bfd575
--- /dev/null
+++ b/vendor/github.com/naoina/toml/encode.go
@@ -0,0 +1,398 @@
+package toml
+
+import (
+ "bytes"
+ "encoding"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+ "time"
+
+ "github.com/naoina/toml/ast"
+)
+
+const (
+ tagOmitempty = "omitempty"
+ tagSkip = "-"
+)
+
+// Marshal returns the TOML encoding of v.
+//
+// Struct values encode as TOML. Each exported struct field becomes a field of
+// the TOML structure unless
+// - the field's tag is "-", or
+// - the field is empty and its tag specifies the "omitempty" option.
+//
+// The "toml" key in the struct field's tag value is the key name, followed by
+// an optional comma and options. Examples:
+//
+// // Field is ignored by this package.
+// Field int `toml:"-"`
+//
+// // Field appears in TOML as key "myName".
+// Field int `toml:"myName"`
+//
+// // Field appears in TOML as key "myName" and the field is omitted from the
+// // result of encoding if its value is empty.
+// Field int `toml:"myName,omitempty"`
+//
+// // Field appears in TOML as key "field", but the field is skipped if
+// // empty. Note the leading comma.
+// Field int `toml:",omitempty"`
+func (cfg *Config) Marshal(v interface{}) ([]byte, error) {
+ buf := new(bytes.Buffer)
+ err := cfg.NewEncoder(buf).Encode(v)
+ return buf.Bytes(), err
+}
+
+// A Encoder writes TOML to an output stream.
+type Encoder struct {
+ w io.Writer
+ cfg *Config
+}
+
+// NewEncoder returns a new Encoder that writes to w.
+func (cfg *Config) NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{w, cfg}
+}
+
+// Encode writes the TOML of v to the stream.
+// See the documentation for Marshal for details about the conversion of Go values to TOML.
+func (e *Encoder) Encode(v interface{}) error {
+ rv := reflect.ValueOf(v)
+ for rv.Kind() == reflect.Ptr {
+ if rv.IsNil() {
+ return &marshalNilError{rv.Type()}
+ }
+ rv = rv.Elem()
+ }
+ buf := &tableBuf{typ: ast.TableTypeNormal}
+ var err error
+ switch rv.Kind() {
+ case reflect.Struct:
+ err = buf.structFields(e.cfg, rv)
+ case reflect.Map:
+ err = buf.mapFields(e.cfg, rv)
+ default:
+ err = &marshalTableError{rv.Type()}
+ }
+ if err != nil {
+ return err
+ }
+ return buf.writeTo(e.w, "")
+}
+
+// Marshaler can be implemented to override the encoding of TOML values. The returned text
+// must be a simple TOML value (i.e. not a table) and is inserted into marshaler output.
+//
+// This interface exists for backwards-compatibility reasons. You probably want to
+// implement encoding.TextMarshaler or MarshalerRec instead.
+type Marshaler interface {
+ MarshalTOML() ([]byte, error)
+}
+
+// MarshalerRec can be implemented to override the TOML encoding of a type.
+// The returned value is marshaled in place of the receiver.
+type MarshalerRec interface {
+ MarshalTOML() (interface{}, error)
+}
+
+type tableBuf struct {
+ name string // already escaped / quoted
+ body []byte
+ children []*tableBuf
+ typ ast.TableType
+ arrayDepth int
+}
+
+func (b *tableBuf) writeTo(w io.Writer, prefix string) error {
+ key := b.name // TODO: escape dots
+ if prefix != "" {
+ key = prefix + "." + key
+ }
+
+ if b.name != "" {
+ head := "[" + key + "]"
+ if b.typ == ast.TableTypeArray {
+ head = "[" + head + "]"
+ }
+ head += "\n"
+ if _, err := io.WriteString(w, head); err != nil {
+ return err
+ }
+ }
+ if _, err := w.Write(b.body); err != nil {
+ return err
+ }
+
+ for i, child := range b.children {
+ if len(b.body) > 0 || i > 0 {
+ if _, err := w.Write([]byte("\n")); err != nil {
+ return err
+ }
+ }
+ if err := child.writeTo(w, key); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (b *tableBuf) newChild(name string) *tableBuf {
+ child := &tableBuf{name: quoteName(name), typ: ast.TableTypeNormal}
+ if b.arrayDepth > 0 {
+ child.typ = ast.TableTypeArray
+ }
+ return child
+}
+
+func (b *tableBuf) addChild(child *tableBuf) {
+ // Empty table elision: we can avoid writing a table that doesn't have any keys on its
+ // own. Array tables can't be elided because they define array elements (which would
+ // be missing if elided).
+ if len(child.body) == 0 && child.typ == ast.TableTypeNormal {
+ for _, gchild := range child.children {
+ gchild.name = child.name + "." + gchild.name
+ b.addChild(gchild)
+ }
+ return
+ }
+ b.children = append(b.children, child)
+}
+
+func (b *tableBuf) structFields(cfg *Config, rv reflect.Value) error {
+ rt := rv.Type()
+ for i := 0; i < rv.NumField(); i++ {
+ ft := rt.Field(i)
+ if ft.PkgPath != "" && !ft.Anonymous { // not exported
+ continue
+ }
+ name, rest := extractTag(ft.Tag.Get(fieldTagName))
+ if name == tagSkip {
+ continue
+ }
+ fv := rv.Field(i)
+ if rest == tagOmitempty && isEmptyValue(fv) {
+ continue
+ }
+ if name == "" {
+ name = cfg.FieldToKey(rt, ft.Name)
+ }
+ if err := b.field(cfg, name, fv); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type mapKeyList []struct {
+ key string
+ value reflect.Value
+}
+
+func (l mapKeyList) Len() int { return len(l) }
+func (l mapKeyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+func (l mapKeyList) Less(i, j int) bool { return l[i].key < l[j].key }
+
+func (b *tableBuf) mapFields(cfg *Config, rv reflect.Value) error {
+ keys := rv.MapKeys()
+ keylist := make(mapKeyList, len(keys))
+ for i, key := range keys {
+ var err error
+ keylist[i].key, err = encodeMapKey(key)
+ if err != nil {
+ return err
+ }
+ keylist[i].value = rv.MapIndex(key)
+ }
+ sort.Sort(keylist)
+
+ for _, kv := range keylist {
+ if err := b.field(cfg, kv.key, kv.value); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (b *tableBuf) field(cfg *Config, name string, rv reflect.Value) error {
+ off := len(b.body)
+ b.body = append(b.body, quoteName(name)...)
+ b.body = append(b.body, " = "...)
+ isTable, err := b.value(cfg, rv, name)
+ if isTable {
+ b.body = b.body[:off] // rub out "key ="
+ } else {
+ b.body = append(b.body, '\n')
+ }
+ return err
+}
+
+func (b *tableBuf) value(cfg *Config, rv reflect.Value, name string) (bool, error) {
+ isMarshaler, isTable, err := b.marshaler(cfg, rv, name)
+ if isMarshaler {
+ return isTable, err
+ }
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ b.body = strconv.AppendInt(b.body, rv.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ b.body = strconv.AppendUint(b.body, rv.Uint(), 10)
+ case reflect.Float32, reflect.Float64:
+ b.body = strconv.AppendFloat(b.body, rv.Float(), 'e', -1, 64)
+ case reflect.Bool:
+ b.body = strconv.AppendBool(b.body, rv.Bool())
+ case reflect.String:
+ b.body = strconv.AppendQuote(b.body, rv.String())
+ case reflect.Ptr, reflect.Interface:
+ if rv.IsNil() {
+ return false, &marshalNilError{rv.Type()}
+ }
+ return b.value(cfg, rv.Elem(), name)
+ case reflect.Slice, reflect.Array:
+ rvlen := rv.Len()
+ if rvlen == 0 {
+ b.body = append(b.body, '[', ']')
+ return false, nil
+ }
+
+ b.arrayDepth++
+ wroteElem := false
+ b.body = append(b.body, '[')
+ for i := 0; i < rvlen; i++ {
+ isTable, err := b.value(cfg, rv.Index(i), name)
+ if err != nil {
+ return isTable, err
+ }
+ wroteElem = wroteElem || !isTable
+ if wroteElem {
+ if i < rvlen-1 {
+ b.body = append(b.body, ',', ' ')
+ } else {
+ b.body = append(b.body, ']')
+ }
+ }
+ }
+ if !wroteElem {
+ b.body = b.body[:len(b.body)-1] // rub out '['
+ }
+ b.arrayDepth--
+ return !wroteElem, nil
+ case reflect.Struct:
+ child := b.newChild(name)
+ err := child.structFields(cfg, rv)
+ b.addChild(child)
+ return true, err
+ case reflect.Map:
+ child := b.newChild(name)
+ err := child.mapFields(cfg, rv)
+ b.addChild(child)
+ return true, err
+ default:
+ return false, fmt.Errorf("toml: marshal: unsupported type %v", rv.Kind())
+ }
+ return false, nil
+}
+
+func (b *tableBuf) marshaler(cfg *Config, rv reflect.Value, name string) (handled, isTable bool, err error) {
+ switch t := rv.Interface().(type) {
+ case encoding.TextMarshaler:
+ enc, err := t.MarshalText()
+ if err != nil {
+ return true, false, err
+ }
+ b.body = encodeTextMarshaler(b.body, string(enc))
+ return true, false, nil
+ case MarshalerRec:
+ newval, err := t.MarshalTOML()
+ if err != nil {
+ return true, false, err
+ }
+ isTable, err = b.value(cfg, reflect.ValueOf(newval), name)
+ return true, isTable, err
+ case Marshaler:
+ enc, err := t.MarshalTOML()
+ if err != nil {
+ return true, false, err
+ }
+ b.body = append(b.body, enc...)
+ return true, false, nil
+ }
+ return false, false, nil
+}
+
+func encodeTextMarshaler(buf []byte, v string) []byte {
+ // Emit the value without quotes if possible.
+ if v == "true" || v == "false" {
+ return append(buf, v...)
+ } else if _, err := time.Parse(time.RFC3339Nano, v); err == nil {
+ return append(buf, v...)
+ } else if _, err := strconv.ParseInt(v, 10, 64); err == nil {
+ return append(buf, v...)
+ } else if _, err := strconv.ParseUint(v, 10, 64); err == nil {
+ return append(buf, v...)
+ } else if _, err := strconv.ParseFloat(v, 64); err == nil {
+ return append(buf, v...)
+ }
+ return strconv.AppendQuote(buf, v)
+}
+
+func encodeMapKey(rv reflect.Value) (string, error) {
+ if rv.Kind() == reflect.String {
+ return rv.String(), nil
+ }
+ if tm, ok := rv.Interface().(encoding.TextMarshaler); ok {
+ b, err := tm.MarshalText()
+ return string(b), err
+ }
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return strconv.FormatInt(rv.Int(), 10), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return strconv.FormatUint(rv.Uint(), 10), nil
+ }
+ return "", fmt.Errorf("toml: invalid map key type %v", rv.Type())
+}
+
+func isEmptyValue(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Array:
+ // encoding/json treats all arrays with non-zero length as non-empty. We check the
+ // array content here because zero-length arrays are almost never used.
+ len := v.Len()
+ for i := 0; i < len; i++ {
+ if !isEmptyValue(v.Index(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
+
+func quoteName(s string) string {
+ if len(s) == 0 {
+ return strconv.Quote(s)
+ }
+ for _, r := range s {
+ if r >= '0' && r <= '9' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '-' || r == '_' {
+ continue
+ }
+ return strconv.Quote(s)
+ }
+ return s
+}
diff --git a/vendor/github.com/naoina/toml/error.go b/vendor/github.com/naoina/toml/error.go
new file mode 100644
index 000000000..cb73b5e0a
--- /dev/null
+++ b/vendor/github.com/naoina/toml/error.go
@@ -0,0 +1,107 @@
+package toml
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+var (
+ errArrayMultiType = errors.New("array can't contain multiple types")
+)
+
+// LineError is returned by Unmarshal, UnmarshalTable and Parse
+// if the error is local to a line.
+type LineError struct {
+ Line int
+ StructField string
+ Err error
+}
+
+func (err *LineError) Error() string {
+ field := ""
+ if err.StructField != "" {
+ field = "(" + err.StructField + ") "
+ }
+ return fmt.Sprintf("line %d: %s%v", err.Line, field, err.Err)
+}
+
+func lineError(line int, err error) error {
+ if err == nil {
+ return nil
+ }
+ if _, ok := err.(*LineError); ok {
+ return err
+ }
+ return &LineError{Line: line, Err: err}
+}
+
+func lineErrorField(line int, field string, err error) error {
+ if lerr, ok := err.(*LineError); ok {
+ return lerr
+ } else if err != nil {
+ err = &LineError{Line: line, StructField: field, Err: err}
+ }
+ return err
+}
+
+type overflowError struct {
+ kind reflect.Kind
+ v string
+}
+
+func (err *overflowError) Error() string {
+ return fmt.Sprintf("value %s is out of range for %v", err.v, err.kind)
+}
+
+func convertNumError(kind reflect.Kind, err error) error {
+ if numerr, ok := err.(*strconv.NumError); ok && numerr.Err == strconv.ErrRange {
+ return &overflowError{kind, numerr.Num}
+ }
+ return err
+}
+
+type invalidUnmarshalError struct {
+ typ reflect.Type
+}
+
+func (err *invalidUnmarshalError) Error() string {
+ if err.typ == nil {
+ return "toml: Unmarshal(nil)"
+ }
+ if err.typ.Kind() != reflect.Ptr {
+ return "toml: Unmarshal(non-pointer " + err.typ.String() + ")"
+ }
+ return "toml: Unmarshal(nil " + err.typ.String() + ")"
+}
+
+type unmarshalTypeError struct {
+ what string
+ want string
+ typ reflect.Type
+}
+
+func (err *unmarshalTypeError) Error() string {
+ msg := fmt.Sprintf("cannot unmarshal TOML %s into %s", err.what, err.typ)
+ if err.want != "" {
+ msg += " (need " + err.want + ")"
+ }
+ return msg
+}
+
+type marshalNilError struct {
+ typ reflect.Type
+}
+
+func (err *marshalNilError) Error() string {
+ return fmt.Sprintf("toml: cannot marshal nil %s", err.typ)
+}
+
+type marshalTableError struct {
+ typ reflect.Type
+}
+
+func (err *marshalTableError) Error() string {
+ return fmt.Sprintf("toml: cannot marshal %s as table, want struct or map type", err.typ)
+}
diff --git a/vendor/github.com/naoina/toml/parse.go b/vendor/github.com/naoina/toml/parse.go
new file mode 100644
index 000000000..e6f95001e
--- /dev/null
+++ b/vendor/github.com/naoina/toml/parse.go
@@ -0,0 +1,376 @@
+package toml
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/naoina/toml/ast"
+)
+
+// The parser is generated by github.com/pointlander/peg. To regenerate it, do:
+//
+// go get -u github.com/pointlander/peg
+// go generate .
+
+//go:generate peg -switch -inline parse.peg
+
+var errParse = errors.New("invalid TOML syntax")
+
+// Parse returns an AST representation of TOML.
+// The toplevel is represented by a table.
+func Parse(data []byte) (*ast.Table, error) {
+ d := &parseState{p: &tomlParser{Buffer: string(data)}}
+ d.init()
+
+ if err := d.parse(); err != nil {
+ return nil, err
+ }
+
+ return d.p.toml.table, nil
+}
+
+type parseState struct {
+ p *tomlParser
+}
+
+func (d *parseState) init() {
+ d.p.Init()
+ d.p.toml.init(d.p.buffer)
+}
+
+func (d *parseState) parse() error {
+ if err := d.p.Parse(); err != nil {
+ if err, ok := err.(*parseError); ok {
+ return lineError(err.Line(), errParse)
+ }
+ return err
+ }
+ return d.execute()
+}
+
+func (d *parseState) execute() (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ lerr, ok := e.(*LineError)
+ if !ok {
+ panic(e)
+ }
+ err = lerr
+ }
+ }()
+ d.p.Execute()
+ return nil
+}
+
+func (e *parseError) Line() int {
+ tokens := []token32{e.max}
+ positions, p := make([]int, 2*len(tokens)), 0
+ for _, token := range tokens {
+ positions[p], p = int(token.begin), p+1
+ positions[p], p = int(token.end), p+1
+ }
+ for _, t := range translatePositions(e.p.buffer, positions) {
+ if e.p.line < t.line {
+ e.p.line = t.line
+ }
+ }
+ return e.p.line
+}
+
+type stack struct {
+ key string
+ table *ast.Table
+}
+
+type array struct {
+ parent *array
+ child *array
+ current *ast.Array
+ line int
+}
+
+type toml struct {
+ table *ast.Table
+ line int
+ currentTable *ast.Table
+ s string
+ key string
+ val ast.Value
+ arr *array
+ stack []*stack
+ skip bool
+}
+
+func (p *toml) init(data []rune) {
+ p.line = 1
+ p.table = p.newTable(ast.TableTypeNormal, "")
+ p.table.Position.End = len(data) - 1
+ p.table.Data = data[:len(data)-1] // truncate the end_symbol added by PEG parse generator.
+ p.currentTable = p.table
+}
+
+func (p *toml) Error(err error) {
+ panic(lineError(p.line, err))
+}
+
+func (p *tomlParser) SetTime(begin, end int) {
+ p.val = &ast.Datetime{
+ Position: ast.Position{Begin: begin, End: end},
+ Data: p.buffer[begin:end],
+ Value: string(p.buffer[begin:end]),
+ }
+}
+
+func (p *tomlParser) SetFloat64(begin, end int) {
+ p.val = &ast.Float{
+ Position: ast.Position{Begin: begin, End: end},
+ Data: p.buffer[begin:end],
+ Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
+ }
+}
+
+func (p *tomlParser) SetInt64(begin, end int) {
+ p.val = &ast.Integer{
+ Position: ast.Position{Begin: begin, End: end},
+ Data: p.buffer[begin:end],
+ Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
+ }
+}
+
+func (p *tomlParser) SetString(begin, end int) {
+ p.val = &ast.String{
+ Position: ast.Position{Begin: begin, End: end},
+ Data: p.buffer[begin:end],
+ Value: p.s,
+ }
+ p.s = ""
+}
+
+func (p *tomlParser) SetBool(begin, end int) {
+ p.val = &ast.Boolean{
+ Position: ast.Position{Begin: begin, End: end},
+ Data: p.buffer[begin:end],
+ Value: string(p.buffer[begin:end]),
+ }
+}
+
+func (p *tomlParser) StartArray() {
+ if p.arr == nil {
+ p.arr = &array{line: p.line, current: &ast.Array{}}
+ return
+ }
+ p.arr.child = &array{parent: p.arr, line: p.line, current: &ast.Array{}}
+ p.arr = p.arr.child
+}
+
+func (p *tomlParser) AddArrayVal() {
+ if p.arr.current == nil {
+ p.arr.current = &ast.Array{}
+ }
+ p.arr.current.Value = append(p.arr.current.Value, p.val)
+}
+
+func (p *tomlParser) SetArray(begin, end int) {
+ p.arr.current.Position = ast.Position{Begin: begin, End: end}
+ p.arr.current.Data = p.buffer[begin:end]
+ p.val = p.arr.current
+ p.arr = p.arr.parent
+}
+
+func (p *toml) SetTable(buf []rune, begin, end int) {
+ p.setTable(p.table, buf, begin, end)
+}
+
+func (p *toml) setTable(parent *ast.Table, buf []rune, begin, end int) {
+ name := string(buf[begin:end])
+ names := splitTableKey(name)
+ parent, err := p.lookupTable(parent, names[:len(names)-1])
+ if err != nil {
+ p.Error(err)
+ }
+ last := names[len(names)-1]
+ tbl := p.newTable(ast.TableTypeNormal, last)
+ switch v := parent.Fields[last].(type) {
+ case nil:
+ parent.Fields[last] = tbl
+ case []*ast.Table:
+ p.Error(fmt.Errorf("table `%s' is in conflict with array table in line %d", name, v[0].Line))
+ case *ast.Table:
+ if (v.Position == ast.Position{}) {
+ // This table was created as an implicit parent.
+ // Replace it with the real defined table.
+ tbl.Fields = v.Fields
+ parent.Fields[last] = tbl
+ } else {
+ p.Error(fmt.Errorf("table `%s' is in conflict with table in line %d", name, v.Line))
+ }
+ case *ast.KeyValue:
+ p.Error(fmt.Errorf("table `%s' is in conflict with line %d", name, v.Line))
+ default:
+ p.Error(fmt.Errorf("BUG: table `%s' is in conflict but it's unknown type `%T'", last, v))
+ }
+ p.currentTable = tbl
+}
+
+func (p *toml) newTable(typ ast.TableType, name string) *ast.Table {
+ return &ast.Table{
+ Line: p.line,
+ Name: name,
+ Type: typ,
+ Fields: make(map[string]interface{}),
+ }
+}
+
+func (p *tomlParser) SetTableString(begin, end int) {
+ p.currentTable.Data = p.buffer[begin:end]
+ p.currentTable.Position.Begin = begin
+ p.currentTable.Position.End = end
+}
+
+func (p *toml) SetArrayTable(buf []rune, begin, end int) {
+ p.setArrayTable(p.table, buf, begin, end)
+}
+
+func (p *toml) setArrayTable(parent *ast.Table, buf []rune, begin, end int) {
+ name := string(buf[begin:end])
+ names := splitTableKey(name)
+ parent, err := p.lookupTable(parent, names[:len(names)-1])
+ if err != nil {
+ p.Error(err)
+ }
+ last := names[len(names)-1]
+ tbl := p.newTable(ast.TableTypeArray, last)
+ switch v := parent.Fields[last].(type) {
+ case nil:
+ parent.Fields[last] = []*ast.Table{tbl}
+ case []*ast.Table:
+ parent.Fields[last] = append(v, tbl)
+ case *ast.Table:
+ p.Error(fmt.Errorf("array table `%s' is in conflict with table in line %d", name, v.Line))
+ case *ast.KeyValue:
+ p.Error(fmt.Errorf("array table `%s' is in conflict with line %d", name, v.Line))
+ default:
+ p.Error(fmt.Errorf("BUG: array table `%s' is in conflict but it's unknown type `%T'", name, v))
+ }
+ p.currentTable = tbl
+}
+
+func (p *toml) StartInlineTable() {
+ p.skip = false
+ p.stack = append(p.stack, &stack{p.key, p.currentTable})
+ buf := []rune(p.key)
+ if p.arr == nil {
+ p.setTable(p.currentTable, buf, 0, len(buf))
+ } else {
+ p.setArrayTable(p.currentTable, buf, 0, len(buf))
+ }
+}
+
+func (p *toml) EndInlineTable() {
+ st := p.stack[len(p.stack)-1]
+ p.key, p.currentTable = st.key, st.table
+ p.stack[len(p.stack)-1] = nil
+ p.stack = p.stack[:len(p.stack)-1]
+ p.skip = true
+}
+
+func (p *toml) AddLineCount(i int) {
+ p.line += i
+}
+
+func (p *toml) SetKey(buf []rune, begin, end int) {
+ p.key = string(buf[begin:end])
+}
+
+func (p *toml) AddKeyValue() {
+ if p.skip {
+ p.skip = false
+ return
+ }
+ if val, exists := p.currentTable.Fields[p.key]; exists {
+ switch v := val.(type) {
+ case *ast.Table:
+ p.Error(fmt.Errorf("key `%s' is in conflict with table in line %d", p.key, v.Line))
+ case *ast.KeyValue:
+ p.Error(fmt.Errorf("key `%s' is in conflict with line %xd", p.key, v.Line))
+ default:
+ p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", p.key, v))
+ }
+ }
+ p.currentTable.Fields[p.key] = &ast.KeyValue{Key: p.key, Value: p.val, Line: p.line}
+}
+
+func (p *toml) SetBasicString(buf []rune, begin, end int) {
+ p.s = p.unquote(string(buf[begin:end]))
+}
+
+func (p *toml) SetMultilineString() {
+ p.s = p.unquote(`"` + escapeReplacer.Replace(strings.TrimLeft(p.s, "\r\n")) + `"`)
+}
+
+func (p *toml) AddMultilineBasicBody(buf []rune, begin, end int) {
+ p.s += string(buf[begin:end])
+}
+
+func (p *toml) SetLiteralString(buf []rune, begin, end int) {
+ p.s = string(buf[begin:end])
+}
+
+func (p *toml) SetMultilineLiteralString(buf []rune, begin, end int) {
+ p.s = strings.TrimLeft(string(buf[begin:end]), "\r\n")
+}
+
+func (p *toml) unquote(s string) string {
+ s, err := strconv.Unquote(s)
+ if err != nil {
+ p.Error(err)
+ }
+ return s
+}
+
+func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) {
+ for _, s := range keys {
+ val, exists := t.Fields[s]
+ if !exists {
+ tbl := p.newTable(ast.TableTypeNormal, s)
+ t.Fields[s] = tbl
+ t = tbl
+ continue
+ }
+ switch v := val.(type) {
+ case *ast.Table:
+ t = v
+ case []*ast.Table:
+ t = v[len(v)-1]
+ case *ast.KeyValue:
+ return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line)
+ default:
+ return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v)
+ }
+ }
+ return t, nil
+}
+
+func splitTableKey(tk string) []string {
+ key := make([]byte, 0, 1)
+ keys := make([]string, 0, 1)
+ inQuote := false
+ for i := 0; i < len(tk); i++ {
+ k := tk[i]
+ switch {
+ case k == tableSeparator && !inQuote:
+ keys = append(keys, string(key))
+ key = key[:0] // reuse buffer.
+ case k == '"':
+ inQuote = !inQuote
+ case (k == ' ' || k == '\t') && !inQuote:
+ // skip.
+ default:
+ key = append(key, k)
+ }
+ }
+ keys = append(keys, string(key))
+ return keys
+}
diff --git a/vendor/github.com/naoina/toml/parse.peg b/vendor/github.com/naoina/toml/parse.peg
new file mode 100644
index 000000000..da31dae30
--- /dev/null
+++ b/vendor/github.com/naoina/toml/parse.peg
@@ -0,0 +1,145 @@
+package toml
+
+type tomlParser Peg {
+ toml
+}
+
+TOML <- Expression (newline Expression)* newline? !. { _ = buffer }
+
+Expression <- (
+ <ws table ws comment? (wsnl keyval ws comment?)*> { p.SetTableString(begin, end) }
+ / ws keyval ws comment?
+ / ws comment?
+ / ws
+)
+
+newline <- <[\r\n]+> { p.AddLineCount(end - begin) }
+
+ws <- [ \t]*
+wsnl <- (
+ [ \t]
+ / <[\r\n]> { p.AddLineCount(end - begin) }
+)*
+
+comment <- '#' <[\t -\0x10FFFF]*>
+
+keyval <- key ws '=' ws val { p.AddKeyValue() }
+
+key <- bareKey / quotedKey
+
+bareKey <- <[0-9A-Za-z\-_]+> { p.SetKey(p.buffer, begin, end) }
+
+quotedKey <- '"' <basicChar+> '"' { p.SetKey(p.buffer, begin-1, end+1) }
+
+val <- (
+ <datetime> { p.SetTime(begin, end) }
+ / <float> { p.SetFloat64(begin, end) }
+ / <integer> { p.SetInt64(begin, end) }
+ / <string> { p.SetString(begin, end) }
+ / <boolean> { p.SetBool(begin, end) }
+ / <array> { p.SetArray(begin, end) }
+ / inlineTable
+)
+
+table <- stdTable / arrayTable
+
+stdTable <- '[' ws <tableKey> ws ']' { p.SetTable(p.buffer, begin, end) }
+
+arrayTable <- '[[' ws <tableKey> ws ']]' { p.SetArrayTable(p.buffer, begin, end) }
+
+inlineTable <- (
+ '{' { p.StartInlineTable() }
+ ws inlineTableKeyValues ws
+ '}' { p.EndInlineTable() }
+)
+
+inlineTableKeyValues <- (keyval inlineTableValSep?)*
+
+tableKey <- key (tableKeySep key)*
+
+tableKeySep <- ws '.' ws
+
+inlineTableValSep <- ws ',' ws
+
+integer <- [\-+]? int
+int <- [1-9] (digit / '_' digit)+ / digit
+
+float <- integer (frac exp? / frac? exp)
+frac <- '.' digit (digit / '_' digit)*
+exp <- [eE] [\-+]? digit (digit / '_' digit)*
+
+string <- (
+ mlLiteralString
+ / literalString
+ / mlBasicString
+ / basicString
+)
+
+basicString <- <'"' basicChar* '"'> { p.SetBasicString(p.buffer, begin, end) }
+
+basicChar <- basicUnescaped / escaped
+escaped <- escape ([btnfr"/\\] / 'u' hexQuad / 'U' hexQuad hexQuad)
+
+basicUnescaped <- [ -!#-\[\]-\0x10FFFF]
+
+escape <- '\\'
+
+mlBasicString <- '"""' mlBasicBody '"""' { p.SetMultilineString() }
+
+mlBasicBody <- (
+ <basicChar / newline> { p.AddMultilineBasicBody(p.buffer, begin, end) }
+ / escape newline wsnl
+)*
+
+literalString <- "'" <literalChar*> "'" { p.SetLiteralString(p.buffer, begin, end) }
+
+literalChar <- [\t -&(-\0x10FFFF]
+
+mlLiteralString <- "'''" <mlLiteralBody> "'''" { p.SetMultilineLiteralString(p.buffer, begin, end) }
+
+mlLiteralBody <- (!"'''" (mlLiteralChar / newline))*
+
+mlLiteralChar <- [\t -\0x10FFFF]
+
+hexdigit <- [0-9A-Fa-f]
+hexQuad <- hexdigit hexdigit hexdigit hexdigit
+
+boolean <- 'true' / 'false'
+
+dateFullYear <- digitQuad
+dateMonth <- digitDual
+dateMDay <- digitDual
+timeHour <- digitDual
+timeMinute <- digitDual
+timeSecond <- digitDual
+timeSecfrac <- '.' digit+
+timeNumoffset <- [\-+] timeHour ':' timeMinute
+timeOffset <- 'Z' / timeNumoffset
+partialTime <- timeHour ':' timeMinute ':' timeSecond timeSecfrac?
+fullDate <- dateFullYear '-' dateMonth '-' dateMDay
+fullTime <- partialTime timeOffset
+datetime <- (fullDate ('T' fullTime)?) / partialTime
+
+digit <- [0-9]
+digitDual <- digit digit
+digitQuad <- digitDual digitDual
+
+array <- (
+ '[' { p.StartArray() }
+ wsnl arrayValues? wsnl
+ ']'
+)
+
+arrayValues <- (
+ val { p.AddArrayVal() }
+ (
+ wsnl comment?
+ wsnl arraySep
+ wsnl comment?
+ wsnl val { p.AddArrayVal() }
+ )*
+ wsnl arraySep?
+ wsnl comment?
+)
+
+arraySep <- ','
diff --git a/vendor/github.com/naoina/toml/parse.peg.go b/vendor/github.com/naoina/toml/parse.peg.go
new file mode 100644
index 000000000..d7de73b19
--- /dev/null
+++ b/vendor/github.com/naoina/toml/parse.peg.go
@@ -0,0 +1,2556 @@
+package toml
+
+import (
+ "fmt"
+ "math"
+ "sort"
+ "strconv"
+)
+
+const endSymbol rune = 1114112
+
+/* The rule types inferred from the grammar are below. */
+type pegRule uint8
+
+const (
+ ruleUnknown pegRule = iota
+ ruleTOML
+ ruleExpression
+ rulenewline
+ rulews
+ rulewsnl
+ rulecomment
+ rulekeyval
+ rulekey
+ rulebareKey
+ rulequotedKey
+ ruleval
+ ruletable
+ rulestdTable
+ rulearrayTable
+ ruleinlineTable
+ ruleinlineTableKeyValues
+ ruletableKey
+ ruletableKeySep
+ ruleinlineTableValSep
+ ruleinteger
+ ruleint
+ rulefloat
+ rulefrac
+ ruleexp
+ rulestring
+ rulebasicString
+ rulebasicChar
+ ruleescaped
+ rulebasicUnescaped
+ ruleescape
+ rulemlBasicString
+ rulemlBasicBody
+ ruleliteralString
+ ruleliteralChar
+ rulemlLiteralString
+ rulemlLiteralBody
+ rulemlLiteralChar
+ rulehexdigit
+ rulehexQuad
+ ruleboolean
+ ruledateFullYear
+ ruledateMonth
+ ruledateMDay
+ ruletimeHour
+ ruletimeMinute
+ ruletimeSecond
+ ruletimeSecfrac
+ ruletimeNumoffset
+ ruletimeOffset
+ rulepartialTime
+ rulefullDate
+ rulefullTime
+ ruledatetime
+ ruledigit
+ ruledigitDual
+ ruledigitQuad
+ rulearray
+ rulearrayValues
+ rulearraySep
+ ruleAction0
+ rulePegText
+ ruleAction1
+ ruleAction2
+ ruleAction3
+ ruleAction4
+ ruleAction5
+ ruleAction6
+ ruleAction7
+ ruleAction8
+ ruleAction9
+ ruleAction10
+ ruleAction11
+ ruleAction12
+ ruleAction13
+ ruleAction14
+ ruleAction15
+ ruleAction16
+ ruleAction17
+ ruleAction18
+ ruleAction19
+ ruleAction20
+ ruleAction21
+ ruleAction22
+ ruleAction23
+ ruleAction24
+)
+
+var rul3s = [...]string{
+ "Unknown",
+ "TOML",
+ "Expression",
+ "newline",
+ "ws",
+ "wsnl",
+ "comment",
+ "keyval",
+ "key",
+ "bareKey",
+ "quotedKey",
+ "val",
+ "table",
+ "stdTable",
+ "arrayTable",
+ "inlineTable",
+ "inlineTableKeyValues",
+ "tableKey",
+ "tableKeySep",
+ "inlineTableValSep",
+ "integer",
+ "int",
+ "float",
+ "frac",
+ "exp",
+ "string",
+ "basicString",
+ "basicChar",
+ "escaped",
+ "basicUnescaped",
+ "escape",
+ "mlBasicString",
+ "mlBasicBody",
+ "literalString",
+ "literalChar",
+ "mlLiteralString",
+ "mlLiteralBody",
+ "mlLiteralChar",
+ "hexdigit",
+ "hexQuad",
+ "boolean",
+ "dateFullYear",
+ "dateMonth",
+ "dateMDay",
+ "timeHour",
+ "timeMinute",
+ "timeSecond",
+ "timeSecfrac",
+ "timeNumoffset",
+ "timeOffset",
+ "partialTime",
+ "fullDate",
+ "fullTime",
+ "datetime",
+ "digit",
+ "digitDual",
+ "digitQuad",
+ "array",
+ "arrayValues",
+ "arraySep",
+ "Action0",
+ "PegText",
+ "Action1",
+ "Action2",
+ "Action3",
+ "Action4",
+ "Action5",
+ "Action6",
+ "Action7",
+ "Action8",
+ "Action9",
+ "Action10",
+ "Action11",
+ "Action12",
+ "Action13",
+ "Action14",
+ "Action15",
+ "Action16",
+ "Action17",
+ "Action18",
+ "Action19",
+ "Action20",
+ "Action21",
+ "Action22",
+ "Action23",
+ "Action24",
+}
+
+type token32 struct {
+ pegRule
+ begin, end uint32
+}
+
+func (t *token32) String() string {
+ return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end)
+}
+
+type node32 struct {
+ token32
+ up, next *node32
+}
+
+func (node *node32) print(pretty bool, buffer string) {
+ var print func(node *node32, depth int)
+ print = func(node *node32, depth int) {
+ for node != nil {
+ for c := 0; c < depth; c++ {
+ fmt.Printf(" ")
+ }
+ rule := rul3s[node.pegRule]
+ quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end])))
+ if !pretty {
+ fmt.Printf("%v %v\n", rule, quote)
+ } else {
+ fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote)
+ }
+ if node.up != nil {
+ print(node.up, depth+1)
+ }
+ node = node.next
+ }
+ }
+ print(node, 0)
+}
+
+func (node *node32) Print(buffer string) {
+ node.print(false, buffer)
+}
+
+func (node *node32) PrettyPrint(buffer string) {
+ node.print(true, buffer)
+}
+
+type tokens32 struct {
+ tree []token32
+}
+
+func (t *tokens32) Trim(length uint32) {
+ t.tree = t.tree[:length]
+}
+
+func (t *tokens32) Print() {
+ for _, token := range t.tree {
+ fmt.Println(token.String())
+ }
+}
+
+func (t *tokens32) AST() *node32 {
+ type element struct {
+ node *node32
+ down *element
+ }
+ tokens := t.Tokens()
+ var stack *element
+ for _, token := range tokens {
+ if token.begin == token.end {
+ continue
+ }
+ node := &node32{token32: token}
+ for stack != nil && stack.node.begin >= token.begin && stack.node.end <= token.end {
+ stack.node.next = node.up
+ node.up = stack.node
+ stack = stack.down
+ }
+ stack = &element{node: node, down: stack}
+ }
+ if stack != nil {
+ return stack.node
+ }
+ return nil
+}
+
+func (t *tokens32) PrintSyntaxTree(buffer string) {
+ t.AST().Print(buffer)
+}
+
+func (t *tokens32) PrettyPrintSyntaxTree(buffer string) {
+ t.AST().PrettyPrint(buffer)
+}
+
+func (t *tokens32) Add(rule pegRule, begin, end, index uint32) {
+ if tree := t.tree; int(index) >= len(tree) {
+ expanded := make([]token32, 2*len(tree))
+ copy(expanded, tree)
+ t.tree = expanded
+ }
+ t.tree[index] = token32{
+ pegRule: rule,
+ begin: begin,
+ end: end,
+ }
+}
+
+func (t *tokens32) Tokens() []token32 {
+ return t.tree
+}
+
+type tomlParser struct {
+ toml
+
+ Buffer string
+ buffer []rune
+ rules [86]func() bool
+ parse func(rule ...int) error
+ reset func()
+ Pretty bool
+ tokens32
+}
+
+func (p *tomlParser) Parse(rule ...int) error {
+ return p.parse(rule...)
+}
+
+func (p *tomlParser) Reset() {
+ p.reset()
+}
+
+type textPosition struct {
+ line, symbol int
+}
+
+type textPositionMap map[int]textPosition
+
+func translatePositions(buffer []rune, positions []int) textPositionMap {
+ length, translations, j, line, symbol := len(positions), make(textPositionMap, len(positions)), 0, 1, 0
+ sort.Ints(positions)
+
+search:
+ for i, c := range buffer {
+ if c == '\n' {
+ line, symbol = line+1, 0
+ } else {
+ symbol++
+ }
+ if i == positions[j] {
+ translations[positions[j]] = textPosition{line, symbol}
+ for j++; j < length; j++ {
+ if i != positions[j] {
+ continue search
+ }
+ }
+ break search
+ }
+ }
+
+ return translations
+}
+
+type parseError struct {
+ p *tomlParser
+ max token32
+}
+
+func (e *parseError) Error() string {
+ tokens, error := []token32{e.max}, "\n"
+ positions, p := make([]int, 2*len(tokens)), 0
+ for _, token := range tokens {
+ positions[p], p = int(token.begin), p+1
+ positions[p], p = int(token.end), p+1
+ }
+ translations := translatePositions(e.p.buffer, positions)
+ format := "parse error near %v (line %v symbol %v - line %v symbol %v):\n%v\n"
+ if e.p.Pretty {
+ format = "parse error near \x1B[34m%v\x1B[m (line %v symbol %v - line %v symbol %v):\n%v\n"
+ }
+ for _, token := range tokens {
+ begin, end := int(token.begin), int(token.end)
+ error += fmt.Sprintf(format,
+ rul3s[token.pegRule],
+ translations[begin].line, translations[begin].symbol,
+ translations[end].line, translations[end].symbol,
+ strconv.Quote(string(e.p.buffer[begin:end])))
+ }
+
+ return error
+}
+
+func (p *tomlParser) PrintSyntaxTree() {
+ if p.Pretty {
+ p.tokens32.PrettyPrintSyntaxTree(p.Buffer)
+ } else {
+ p.tokens32.PrintSyntaxTree(p.Buffer)
+ }
+}
+
+func (p *tomlParser) Execute() {
+ buffer, _buffer, text, begin, end := p.Buffer, p.buffer, "", 0, 0
+ for _, token := range p.Tokens() {
+ switch token.pegRule {
+
+ case rulePegText:
+ begin, end = int(token.begin), int(token.end)
+ text = string(_buffer[begin:end])
+
+ case ruleAction0:
+ _ = buffer
+ case ruleAction1:
+ p.SetTableString(begin, end)
+ case ruleAction2:
+ p.AddLineCount(end - begin)
+ case ruleAction3:
+ p.AddLineCount(end - begin)
+ case ruleAction4:
+ p.AddKeyValue()
+ case ruleAction5:
+ p.SetKey(p.buffer, begin, end)
+ case ruleAction6:
+ p.SetKey(p.buffer, begin-1, end+1)
+ case ruleAction7:
+ p.SetTime(begin, end)
+ case ruleAction8:
+ p.SetFloat64(begin, end)
+ case ruleAction9:
+ p.SetInt64(begin, end)
+ case ruleAction10:
+ p.SetString(begin, end)
+ case ruleAction11:
+ p.SetBool(begin, end)
+ case ruleAction12:
+ p.SetArray(begin, end)
+ case ruleAction13:
+ p.SetTable(p.buffer, begin, end)
+ case ruleAction14:
+ p.SetArrayTable(p.buffer, begin, end)
+ case ruleAction15:
+ p.StartInlineTable()
+ case ruleAction16:
+ p.EndInlineTable()
+ case ruleAction17:
+ p.SetBasicString(p.buffer, begin, end)
+ case ruleAction18:
+ p.SetMultilineString()
+ case ruleAction19:
+ p.AddMultilineBasicBody(p.buffer, begin, end)
+ case ruleAction20:
+ p.SetLiteralString(p.buffer, begin, end)
+ case ruleAction21:
+ p.SetMultilineLiteralString(p.buffer, begin, end)
+ case ruleAction22:
+ p.StartArray()
+ case ruleAction23:
+ p.AddArrayVal()
+ case ruleAction24:
+ p.AddArrayVal()
+
+ }
+ }
+ _, _, _, _, _ = buffer, _buffer, text, begin, end
+}
+
+func (p *tomlParser) Init() {
+ var (
+ max token32
+ position, tokenIndex uint32
+ buffer []rune
+ )
+ p.reset = func() {
+ max = token32{}
+ position, tokenIndex = 0, 0
+
+ p.buffer = []rune(p.Buffer)
+ if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol {
+ p.buffer = append(p.buffer, endSymbol)
+ }
+ buffer = p.buffer
+ }
+ p.reset()
+
+ _rules := p.rules
+ tree := tokens32{tree: make([]token32, math.MaxInt16)}
+ p.parse = func(rule ...int) error {
+ r := 1
+ if len(rule) > 0 {
+ r = rule[0]
+ }
+ matches := p.rules[r]()
+ p.tokens32 = tree
+ if matches {
+ p.Trim(tokenIndex)
+ return nil
+ }
+ return &parseError{p, max}
+ }
+
+ add := func(rule pegRule, begin uint32) {
+ tree.Add(rule, begin, position, tokenIndex)
+ tokenIndex++
+ if begin != position && position > max.end {
+ max = token32{rule, begin, position}
+ }
+ }
+
+ matchDot := func() bool {
+ if buffer[position] != endSymbol {
+ position++
+ return true
+ }
+ return false
+ }
+
+ /*matchChar := func(c byte) bool {
+ if buffer[position] == c {
+ position++
+ return true
+ }
+ return false
+ }*/
+
+ /*matchRange := func(lower byte, upper byte) bool {
+ if c := buffer[position]; c >= lower && c <= upper {
+ position++
+ return true
+ }
+ return false
+ }*/
+
+ _rules = [...]func() bool{
+ nil,
+ /* 0 TOML <- <(Expression (newline Expression)* newline? !. Action0)> */
+ func() bool {
+ position0, tokenIndex0 := position, tokenIndex
+ {
+ position1 := position
+ if !_rules[ruleExpression]() {
+ goto l0
+ }
+ l2:
+ {
+ position3, tokenIndex3 := position, tokenIndex
+ if !_rules[rulenewline]() {
+ goto l3
+ }
+ if !_rules[ruleExpression]() {
+ goto l3
+ }
+ goto l2
+ l3:
+ position, tokenIndex = position3, tokenIndex3
+ }
+ {
+ position4, tokenIndex4 := position, tokenIndex
+ if !_rules[rulenewline]() {
+ goto l4
+ }
+ goto l5
+ l4:
+ position, tokenIndex = position4, tokenIndex4
+ }
+ l5:
+ {
+ position6, tokenIndex6 := position, tokenIndex
+ if !matchDot() {
+ goto l6
+ }
+ goto l0
+ l6:
+ position, tokenIndex = position6, tokenIndex6
+ }
+ {
+ add(ruleAction0, position)
+ }
+ add(ruleTOML, position1)
+ }
+ return true
+ l0:
+ position, tokenIndex = position0, tokenIndex0
+ return false
+ },
+ /* 1 Expression <- <((<(ws table ws comment? (wsnl keyval ws comment?)*)> Action1) / (ws keyval ws comment?) / (ws comment?) / ws)> */
+ func() bool {
+ position8, tokenIndex8 := position, tokenIndex
+ {
+ position9 := position
+ {
+ position10, tokenIndex10 := position, tokenIndex
+ {
+ position12 := position
+ if !_rules[rulews]() {
+ goto l11
+ }
+ {
+ position13 := position
+ {
+ position14, tokenIndex14 := position, tokenIndex
+ {
+ position16 := position
+ if buffer[position] != rune('[') {
+ goto l15
+ }
+ position++
+ if !_rules[rulews]() {
+ goto l15
+ }
+ {
+ position17 := position
+ if !_rules[ruletableKey]() {
+ goto l15
+ }
+ add(rulePegText, position17)
+ }
+ if !_rules[rulews]() {
+ goto l15
+ }
+ if buffer[position] != rune(']') {
+ goto l15
+ }
+ position++
+ {
+ add(ruleAction13, position)
+ }
+ add(rulestdTable, position16)
+ }
+ goto l14
+ l15:
+ position, tokenIndex = position14, tokenIndex14
+ {
+ position19 := position
+ if buffer[position] != rune('[') {
+ goto l11
+ }
+ position++
+ if buffer[position] != rune('[') {
+ goto l11
+ }
+ position++
+ if !_rules[rulews]() {
+ goto l11
+ }
+ {
+ position20 := position
+ if !_rules[ruletableKey]() {
+ goto l11
+ }
+ add(rulePegText, position20)
+ }
+ if !_rules[rulews]() {
+ goto l11
+ }
+ if buffer[position] != rune(']') {
+ goto l11
+ }
+ position++
+ if buffer[position] != rune(']') {
+ goto l11
+ }
+ position++
+ {
+ add(ruleAction14, position)
+ }
+ add(rulearrayTable, position19)
+ }
+ }
+ l14:
+ add(ruletable, position13)
+ }
+ if !_rules[rulews]() {
+ goto l11
+ }
+ {
+ position22, tokenIndex22 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l22
+ }
+ goto l23
+ l22:
+ position, tokenIndex = position22, tokenIndex22
+ }
+ l23:
+ l24:
+ {
+ position25, tokenIndex25 := position, tokenIndex
+ if !_rules[rulewsnl]() {
+ goto l25
+ }
+ if !_rules[rulekeyval]() {
+ goto l25
+ }
+ if !_rules[rulews]() {
+ goto l25
+ }
+ {
+ position26, tokenIndex26 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l26
+ }
+ goto l27
+ l26:
+ position, tokenIndex = position26, tokenIndex26
+ }
+ l27:
+ goto l24
+ l25:
+ position, tokenIndex = position25, tokenIndex25
+ }
+ add(rulePegText, position12)
+ }
+ {
+ add(ruleAction1, position)
+ }
+ goto l10
+ l11:
+ position, tokenIndex = position10, tokenIndex10
+ if !_rules[rulews]() {
+ goto l29
+ }
+ if !_rules[rulekeyval]() {
+ goto l29
+ }
+ if !_rules[rulews]() {
+ goto l29
+ }
+ {
+ position30, tokenIndex30 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l30
+ }
+ goto l31
+ l30:
+ position, tokenIndex = position30, tokenIndex30
+ }
+ l31:
+ goto l10
+ l29:
+ position, tokenIndex = position10, tokenIndex10
+ if !_rules[rulews]() {
+ goto l32
+ }
+ {
+ position33, tokenIndex33 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l33
+ }
+ goto l34
+ l33:
+ position, tokenIndex = position33, tokenIndex33
+ }
+ l34:
+ goto l10
+ l32:
+ position, tokenIndex = position10, tokenIndex10
+ if !_rules[rulews]() {
+ goto l8
+ }
+ }
+ l10:
+ add(ruleExpression, position9)
+ }
+ return true
+ l8:
+ position, tokenIndex = position8, tokenIndex8
+ return false
+ },
+ /* 2 newline <- <(<('\r' / '\n')+> Action2)> */
+ func() bool {
+ position35, tokenIndex35 := position, tokenIndex
+ {
+ position36 := position
+ {
+ position37 := position
+ {
+ position40, tokenIndex40 := position, tokenIndex
+ if buffer[position] != rune('\r') {
+ goto l41
+ }
+ position++
+ goto l40
+ l41:
+ position, tokenIndex = position40, tokenIndex40
+ if buffer[position] != rune('\n') {
+ goto l35
+ }
+ position++
+ }
+ l40:
+ l38:
+ {
+ position39, tokenIndex39 := position, tokenIndex
+ {
+ position42, tokenIndex42 := position, tokenIndex
+ if buffer[position] != rune('\r') {
+ goto l43
+ }
+ position++
+ goto l42
+ l43:
+ position, tokenIndex = position42, tokenIndex42
+ if buffer[position] != rune('\n') {
+ goto l39
+ }
+ position++
+ }
+ l42:
+ goto l38
+ l39:
+ position, tokenIndex = position39, tokenIndex39
+ }
+ add(rulePegText, position37)
+ }
+ {
+ add(ruleAction2, position)
+ }
+ add(rulenewline, position36)
+ }
+ return true
+ l35:
+ position, tokenIndex = position35, tokenIndex35
+ return false
+ },
+ /* 3 ws <- <(' ' / '\t')*> */
+ func() bool {
+ {
+ position46 := position
+ l47:
+ {
+ position48, tokenIndex48 := position, tokenIndex
+ {
+ position49, tokenIndex49 := position, tokenIndex
+ if buffer[position] != rune(' ') {
+ goto l50
+ }
+ position++
+ goto l49
+ l50:
+ position, tokenIndex = position49, tokenIndex49
+ if buffer[position] != rune('\t') {
+ goto l48
+ }
+ position++
+ }
+ l49:
+ goto l47
+ l48:
+ position, tokenIndex = position48, tokenIndex48
+ }
+ add(rulews, position46)
+ }
+ return true
+ },
+ /* 4 wsnl <- <((&('\t') '\t') | (&(' ') ' ') | (&('\n' | '\r') (<('\r' / '\n')> Action3)))*> */
+ func() bool {
+ {
+ position52 := position
+ l53:
+ {
+ position54, tokenIndex54 := position, tokenIndex
+ {
+ switch buffer[position] {
+ case '\t':
+ if buffer[position] != rune('\t') {
+ goto l54
+ }
+ position++
+ break
+ case ' ':
+ if buffer[position] != rune(' ') {
+ goto l54
+ }
+ position++
+ break
+ default:
+ {
+ position56 := position
+ {
+ position57, tokenIndex57 := position, tokenIndex
+ if buffer[position] != rune('\r') {
+ goto l58
+ }
+ position++
+ goto l57
+ l58:
+ position, tokenIndex = position57, tokenIndex57
+ if buffer[position] != rune('\n') {
+ goto l54
+ }
+ position++
+ }
+ l57:
+ add(rulePegText, position56)
+ }
+ {
+ add(ruleAction3, position)
+ }
+ break
+ }
+ }
+
+ goto l53
+ l54:
+ position, tokenIndex = position54, tokenIndex54
+ }
+ add(rulewsnl, position52)
+ }
+ return true
+ },
+ /* 5 comment <- <('#' <('\t' / [ -\U0010ffff])*>)> */
+ func() bool {
+ position60, tokenIndex60 := position, tokenIndex
+ {
+ position61 := position
+ if buffer[position] != rune('#') {
+ goto l60
+ }
+ position++
+ {
+ position62 := position
+ l63:
+ {
+ position64, tokenIndex64 := position, tokenIndex
+ {
+ position65, tokenIndex65 := position, tokenIndex
+ if buffer[position] != rune('\t') {
+ goto l66
+ }
+ position++
+ goto l65
+ l66:
+ position, tokenIndex = position65, tokenIndex65
+ if c := buffer[position]; c < rune(' ') || c > rune('\U0010ffff') {
+ goto l64
+ }
+ position++
+ }
+ l65:
+ goto l63
+ l64:
+ position, tokenIndex = position64, tokenIndex64
+ }
+ add(rulePegText, position62)
+ }
+ add(rulecomment, position61)
+ }
+ return true
+ l60:
+ position, tokenIndex = position60, tokenIndex60
+ return false
+ },
+ /* 6 keyval <- <(key ws '=' ws val Action4)> */
+ func() bool {
+ position67, tokenIndex67 := position, tokenIndex
+ {
+ position68 := position
+ if !_rules[rulekey]() {
+ goto l67
+ }
+ if !_rules[rulews]() {
+ goto l67
+ }
+ if buffer[position] != rune('=') {
+ goto l67
+ }
+ position++
+ if !_rules[rulews]() {
+ goto l67
+ }
+ if !_rules[ruleval]() {
+ goto l67
+ }
+ {
+ add(ruleAction4, position)
+ }
+ add(rulekeyval, position68)
+ }
+ return true
+ l67:
+ position, tokenIndex = position67, tokenIndex67
+ return false
+ },
+ /* 7 key <- <(bareKey / quotedKey)> */
+ func() bool {
+ position70, tokenIndex70 := position, tokenIndex
+ {
+ position71 := position
+ {
+ position72, tokenIndex72 := position, tokenIndex
+ {
+ position74 := position
+ {
+ position75 := position
+ {
+ switch buffer[position] {
+ case '_':
+ if buffer[position] != rune('_') {
+ goto l73
+ }
+ position++
+ break
+ case '-':
+ if buffer[position] != rune('-') {
+ goto l73
+ }
+ position++
+ break
+ case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z':
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l73
+ }
+ position++
+ break
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l73
+ }
+ position++
+ break
+ default:
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l73
+ }
+ position++
+ break
+ }
+ }
+
+ l76:
+ {
+ position77, tokenIndex77 := position, tokenIndex
+ {
+ switch buffer[position] {
+ case '_':
+ if buffer[position] != rune('_') {
+ goto l77
+ }
+ position++
+ break
+ case '-':
+ if buffer[position] != rune('-') {
+ goto l77
+ }
+ position++
+ break
+ case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z':
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l77
+ }
+ position++
+ break
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l77
+ }
+ position++
+ break
+ default:
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l77
+ }
+ position++
+ break
+ }
+ }
+
+ goto l76
+ l77:
+ position, tokenIndex = position77, tokenIndex77
+ }
+ add(rulePegText, position75)
+ }
+ {
+ add(ruleAction5, position)
+ }
+ add(rulebareKey, position74)
+ }
+ goto l72
+ l73:
+ position, tokenIndex = position72, tokenIndex72
+ {
+ position81 := position
+ if buffer[position] != rune('"') {
+ goto l70
+ }
+ position++
+ {
+ position82 := position
+ if !_rules[rulebasicChar]() {
+ goto l70
+ }
+ l83:
+ {
+ position84, tokenIndex84 := position, tokenIndex
+ if !_rules[rulebasicChar]() {
+ goto l84
+ }
+ goto l83
+ l84:
+ position, tokenIndex = position84, tokenIndex84
+ }
+ add(rulePegText, position82)
+ }
+ if buffer[position] != rune('"') {
+ goto l70
+ }
+ position++
+ {
+ add(ruleAction6, position)
+ }
+ add(rulequotedKey, position81)
+ }
+ }
+ l72:
+ add(rulekey, position71)
+ }
+ return true
+ l70:
+ position, tokenIndex = position70, tokenIndex70
+ return false
+ },
+ /* 8 bareKey <- <(<((&('_') '_') | (&('-') '-') | (&('a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z') [a-z]) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z') [A-Z]))+> Action5)> */
+ nil,
+ /* 9 quotedKey <- <('"' <basicChar+> '"' Action6)> */
+ nil,
+ /* 10 val <- <((<datetime> Action7) / (<float> Action8) / ((&('{') inlineTable) | (&('[') (<array> Action12)) | (&('f' | 't') (<boolean> Action11)) | (&('"' | '\'') (<string> Action10)) | (&('+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') (<integer> Action9))))> */
+ func() bool {
+ position88, tokenIndex88 := position, tokenIndex
+ {
+ position89 := position
+ {
+ position90, tokenIndex90 := position, tokenIndex
+ {
+ position92 := position
+ {
+ position93 := position
+ {
+ position94, tokenIndex94 := position, tokenIndex
+ {
+ position96 := position
+ {
+ position97 := position
+ {
+ position98 := position
+ if !_rules[ruledigitDual]() {
+ goto l95
+ }
+ if !_rules[ruledigitDual]() {
+ goto l95
+ }
+ add(ruledigitQuad, position98)
+ }
+ add(ruledateFullYear, position97)
+ }
+ if buffer[position] != rune('-') {
+ goto l95
+ }
+ position++
+ {
+ position99 := position
+ if !_rules[ruledigitDual]() {
+ goto l95
+ }
+ add(ruledateMonth, position99)
+ }
+ if buffer[position] != rune('-') {
+ goto l95
+ }
+ position++
+ {
+ position100 := position
+ if !_rules[ruledigitDual]() {
+ goto l95
+ }
+ add(ruledateMDay, position100)
+ }
+ add(rulefullDate, position96)
+ }
+ {
+ position101, tokenIndex101 := position, tokenIndex
+ if buffer[position] != rune('T') {
+ goto l101
+ }
+ position++
+ {
+ position103 := position
+ if !_rules[rulepartialTime]() {
+ goto l101
+ }
+ {
+ position104 := position
+ {
+ position105, tokenIndex105 := position, tokenIndex
+ if buffer[position] != rune('Z') {
+ goto l106
+ }
+ position++
+ goto l105
+ l106:
+ position, tokenIndex = position105, tokenIndex105
+ {
+ position107 := position
+ {
+ position108, tokenIndex108 := position, tokenIndex
+ if buffer[position] != rune('-') {
+ goto l109
+ }
+ position++
+ goto l108
+ l109:
+ position, tokenIndex = position108, tokenIndex108
+ if buffer[position] != rune('+') {
+ goto l101
+ }
+ position++
+ }
+ l108:
+ if !_rules[ruletimeHour]() {
+ goto l101
+ }
+ if buffer[position] != rune(':') {
+ goto l101
+ }
+ position++
+ if !_rules[ruletimeMinute]() {
+ goto l101
+ }
+ add(ruletimeNumoffset, position107)
+ }
+ }
+ l105:
+ add(ruletimeOffset, position104)
+ }
+ add(rulefullTime, position103)
+ }
+ goto l102
+ l101:
+ position, tokenIndex = position101, tokenIndex101
+ }
+ l102:
+ goto l94
+ l95:
+ position, tokenIndex = position94, tokenIndex94
+ if !_rules[rulepartialTime]() {
+ goto l91
+ }
+ }
+ l94:
+ add(ruledatetime, position93)
+ }
+ add(rulePegText, position92)
+ }
+ {
+ add(ruleAction7, position)
+ }
+ goto l90
+ l91:
+ position, tokenIndex = position90, tokenIndex90
+ {
+ position112 := position
+ {
+ position113 := position
+ if !_rules[ruleinteger]() {
+ goto l111
+ }
+ {
+ position114, tokenIndex114 := position, tokenIndex
+ if !_rules[rulefrac]() {
+ goto l115
+ }
+ {
+ position116, tokenIndex116 := position, tokenIndex
+ if !_rules[ruleexp]() {
+ goto l116
+ }
+ goto l117
+ l116:
+ position, tokenIndex = position116, tokenIndex116
+ }
+ l117:
+ goto l114
+ l115:
+ position, tokenIndex = position114, tokenIndex114
+ {
+ position118, tokenIndex118 := position, tokenIndex
+ if !_rules[rulefrac]() {
+ goto l118
+ }
+ goto l119
+ l118:
+ position, tokenIndex = position118, tokenIndex118
+ }
+ l119:
+ if !_rules[ruleexp]() {
+ goto l111
+ }
+ }
+ l114:
+ add(rulefloat, position113)
+ }
+ add(rulePegText, position112)
+ }
+ {
+ add(ruleAction8, position)
+ }
+ goto l90
+ l111:
+ position, tokenIndex = position90, tokenIndex90
+ {
+ switch buffer[position] {
+ case '{':
+ {
+ position122 := position
+ if buffer[position] != rune('{') {
+ goto l88
+ }
+ position++
+ {
+ add(ruleAction15, position)
+ }
+ if !_rules[rulews]() {
+ goto l88
+ }
+ {
+ position124 := position
+ l125:
+ {
+ position126, tokenIndex126 := position, tokenIndex
+ if !_rules[rulekeyval]() {
+ goto l126
+ }
+ {
+ position127, tokenIndex127 := position, tokenIndex
+ {
+ position129 := position
+ if !_rules[rulews]() {
+ goto l127
+ }
+ if buffer[position] != rune(',') {
+ goto l127
+ }
+ position++
+ if !_rules[rulews]() {
+ goto l127
+ }
+ add(ruleinlineTableValSep, position129)
+ }
+ goto l128
+ l127:
+ position, tokenIndex = position127, tokenIndex127
+ }
+ l128:
+ goto l125
+ l126:
+ position, tokenIndex = position126, tokenIndex126
+ }
+ add(ruleinlineTableKeyValues, position124)
+ }
+ if !_rules[rulews]() {
+ goto l88
+ }
+ if buffer[position] != rune('}') {
+ goto l88
+ }
+ position++
+ {
+ add(ruleAction16, position)
+ }
+ add(ruleinlineTable, position122)
+ }
+ break
+ case '[':
+ {
+ position131 := position
+ {
+ position132 := position
+ if buffer[position] != rune('[') {
+ goto l88
+ }
+ position++
+ {
+ add(ruleAction22, position)
+ }
+ if !_rules[rulewsnl]() {
+ goto l88
+ }
+ {
+ position134, tokenIndex134 := position, tokenIndex
+ {
+ position136 := position
+ if !_rules[ruleval]() {
+ goto l134
+ }
+ {
+ add(ruleAction23, position)
+ }
+ l138:
+ {
+ position139, tokenIndex139 := position, tokenIndex
+ if !_rules[rulewsnl]() {
+ goto l139
+ }
+ {
+ position140, tokenIndex140 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l140
+ }
+ goto l141
+ l140:
+ position, tokenIndex = position140, tokenIndex140
+ }
+ l141:
+ if !_rules[rulewsnl]() {
+ goto l139
+ }
+ if !_rules[rulearraySep]() {
+ goto l139
+ }
+ if !_rules[rulewsnl]() {
+ goto l139
+ }
+ {
+ position142, tokenIndex142 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l142
+ }
+ goto l143
+ l142:
+ position, tokenIndex = position142, tokenIndex142
+ }
+ l143:
+ if !_rules[rulewsnl]() {
+ goto l139
+ }
+ if !_rules[ruleval]() {
+ goto l139
+ }
+ {
+ add(ruleAction24, position)
+ }
+ goto l138
+ l139:
+ position, tokenIndex = position139, tokenIndex139
+ }
+ if !_rules[rulewsnl]() {
+ goto l134
+ }
+ {
+ position145, tokenIndex145 := position, tokenIndex
+ if !_rules[rulearraySep]() {
+ goto l145
+ }
+ goto l146
+ l145:
+ position, tokenIndex = position145, tokenIndex145
+ }
+ l146:
+ if !_rules[rulewsnl]() {
+ goto l134
+ }
+ {
+ position147, tokenIndex147 := position, tokenIndex
+ if !_rules[rulecomment]() {
+ goto l147
+ }
+ goto l148
+ l147:
+ position, tokenIndex = position147, tokenIndex147
+ }
+ l148:
+ add(rulearrayValues, position136)
+ }
+ goto l135
+ l134:
+ position, tokenIndex = position134, tokenIndex134
+ }
+ l135:
+ if !_rules[rulewsnl]() {
+ goto l88
+ }
+ if buffer[position] != rune(']') {
+ goto l88
+ }
+ position++
+ add(rulearray, position132)
+ }
+ add(rulePegText, position131)
+ }
+ {
+ add(ruleAction12, position)
+ }
+ break
+ case 'f', 't':
+ {
+ position150 := position
+ {
+ position151 := position
+ {
+ position152, tokenIndex152 := position, tokenIndex
+ if buffer[position] != rune('t') {
+ goto l153
+ }
+ position++
+ if buffer[position] != rune('r') {
+ goto l153
+ }
+ position++
+ if buffer[position] != rune('u') {
+ goto l153
+ }
+ position++
+ if buffer[position] != rune('e') {
+ goto l153
+ }
+ position++
+ goto l152
+ l153:
+ position, tokenIndex = position152, tokenIndex152
+ if buffer[position] != rune('f') {
+ goto l88
+ }
+ position++
+ if buffer[position] != rune('a') {
+ goto l88
+ }
+ position++
+ if buffer[position] != rune('l') {
+ goto l88
+ }
+ position++
+ if buffer[position] != rune('s') {
+ goto l88
+ }
+ position++
+ if buffer[position] != rune('e') {
+ goto l88
+ }
+ position++
+ }
+ l152:
+ add(ruleboolean, position151)
+ }
+ add(rulePegText, position150)
+ }
+ {
+ add(ruleAction11, position)
+ }
+ break
+ case '"', '\'':
+ {
+ position155 := position
+ {
+ position156 := position
+ {
+ position157, tokenIndex157 := position, tokenIndex
+ {
+ position159 := position
+ if buffer[position] != rune('\'') {
+ goto l158
+ }
+ position++
+ if buffer[position] != rune('\'') {
+ goto l158
+ }
+ position++
+ if buffer[position] != rune('\'') {
+ goto l158
+ }
+ position++
+ {
+ position160 := position
+ {
+ position161 := position
+ l162:
+ {
+ position163, tokenIndex163 := position, tokenIndex
+ {
+ position164, tokenIndex164 := position, tokenIndex
+ if buffer[position] != rune('\'') {
+ goto l164
+ }
+ position++
+ if buffer[position] != rune('\'') {
+ goto l164
+ }
+ position++
+ if buffer[position] != rune('\'') {
+ goto l164
+ }
+ position++
+ goto l163
+ l164:
+ position, tokenIndex = position164, tokenIndex164
+ }
+ {
+ position165, tokenIndex165 := position, tokenIndex
+ {
+ position167 := position
+ {
+ position168, tokenIndex168 := position, tokenIndex
+ if buffer[position] != rune('\t') {
+ goto l169
+ }
+ position++
+ goto l168
+ l169:
+ position, tokenIndex = position168, tokenIndex168
+ if c := buffer[position]; c < rune(' ') || c > rune('\U0010ffff') {
+ goto l166
+ }
+ position++
+ }
+ l168:
+ add(rulemlLiteralChar, position167)
+ }
+ goto l165
+ l166:
+ position, tokenIndex = position165, tokenIndex165
+ if !_rules[rulenewline]() {
+ goto l163
+ }
+ }
+ l165:
+ goto l162
+ l163:
+ position, tokenIndex = position163, tokenIndex163
+ }
+ add(rulemlLiteralBody, position161)
+ }
+ add(rulePegText, position160)
+ }
+ if buffer[position] != rune('\'') {
+ goto l158
+ }
+ position++
+ if buffer[position] != rune('\'') {
+ goto l158
+ }
+ position++
+ if buffer[position] != rune('\'') {
+ goto l158
+ }
+ position++
+ {
+ add(ruleAction21, position)
+ }
+ add(rulemlLiteralString, position159)
+ }
+ goto l157
+ l158:
+ position, tokenIndex = position157, tokenIndex157
+ {
+ position172 := position
+ if buffer[position] != rune('\'') {
+ goto l171
+ }
+ position++
+ {
+ position173 := position
+ l174:
+ {
+ position175, tokenIndex175 := position, tokenIndex
+ {
+ position176 := position
+ {
+ switch buffer[position] {
+ case '\t':
+ if buffer[position] != rune('\t') {
+ goto l175
+ }
+ position++
+ break
+ case ' ', '!', '"', '#', '$', '%', '&':
+ if c := buffer[position]; c < rune(' ') || c > rune('&') {
+ goto l175
+ }
+ position++
+ break
+ default:
+ if c := buffer[position]; c < rune('(') || c > rune('\U0010ffff') {
+ goto l175
+ }
+ position++
+ break
+ }
+ }
+
+ add(ruleliteralChar, position176)
+ }
+ goto l174
+ l175:
+ position, tokenIndex = position175, tokenIndex175
+ }
+ add(rulePegText, position173)
+ }
+ if buffer[position] != rune('\'') {
+ goto l171
+ }
+ position++
+ {
+ add(ruleAction20, position)
+ }
+ add(ruleliteralString, position172)
+ }
+ goto l157
+ l171:
+ position, tokenIndex = position157, tokenIndex157
+ {
+ position180 := position
+ if buffer[position] != rune('"') {
+ goto l179
+ }
+ position++
+ if buffer[position] != rune('"') {
+ goto l179
+ }
+ position++
+ if buffer[position] != rune('"') {
+ goto l179
+ }
+ position++
+ {
+ position181 := position
+ l182:
+ {
+ position183, tokenIndex183 := position, tokenIndex
+ {
+ position184, tokenIndex184 := position, tokenIndex
+ {
+ position186 := position
+ {
+ position187, tokenIndex187 := position, tokenIndex
+ if !_rules[rulebasicChar]() {
+ goto l188
+ }
+ goto l187
+ l188:
+ position, tokenIndex = position187, tokenIndex187
+ if !_rules[rulenewline]() {
+ goto l185
+ }
+ }
+ l187:
+ add(rulePegText, position186)
+ }
+ {
+ add(ruleAction19, position)
+ }
+ goto l184
+ l185:
+ position, tokenIndex = position184, tokenIndex184
+ if !_rules[ruleescape]() {
+ goto l183
+ }
+ if !_rules[rulenewline]() {
+ goto l183
+ }
+ if !_rules[rulewsnl]() {
+ goto l183
+ }
+ }
+ l184:
+ goto l182
+ l183:
+ position, tokenIndex = position183, tokenIndex183
+ }
+ add(rulemlBasicBody, position181)
+ }
+ if buffer[position] != rune('"') {
+ goto l179
+ }
+ position++
+ if buffer[position] != rune('"') {
+ goto l179
+ }
+ position++
+ if buffer[position] != rune('"') {
+ goto l179
+ }
+ position++
+ {
+ add(ruleAction18, position)
+ }
+ add(rulemlBasicString, position180)
+ }
+ goto l157
+ l179:
+ position, tokenIndex = position157, tokenIndex157
+ {
+ position191 := position
+ {
+ position192 := position
+ if buffer[position] != rune('"') {
+ goto l88
+ }
+ position++
+ l193:
+ {
+ position194, tokenIndex194 := position, tokenIndex
+ if !_rules[rulebasicChar]() {
+ goto l194
+ }
+ goto l193
+ l194:
+ position, tokenIndex = position194, tokenIndex194
+ }
+ if buffer[position] != rune('"') {
+ goto l88
+ }
+ position++
+ add(rulePegText, position192)
+ }
+ {
+ add(ruleAction17, position)
+ }
+ add(rulebasicString, position191)
+ }
+ }
+ l157:
+ add(rulestring, position156)
+ }
+ add(rulePegText, position155)
+ }
+ {
+ add(ruleAction10, position)
+ }
+ break
+ default:
+ {
+ position197 := position
+ if !_rules[ruleinteger]() {
+ goto l88
+ }
+ add(rulePegText, position197)
+ }
+ {
+ add(ruleAction9, position)
+ }
+ break
+ }
+ }
+
+ }
+ l90:
+ add(ruleval, position89)
+ }
+ return true
+ l88:
+ position, tokenIndex = position88, tokenIndex88
+ return false
+ },
+ /* 11 table <- <(stdTable / arrayTable)> */
+ nil,
+ /* 12 stdTable <- <('[' ws <tableKey> ws ']' Action13)> */
+ nil,
+ /* 13 arrayTable <- <('[' '[' ws <tableKey> ws (']' ']') Action14)> */
+ nil,
+ /* 14 inlineTable <- <('{' Action15 ws inlineTableKeyValues ws '}' Action16)> */
+ nil,
+ /* 15 inlineTableKeyValues <- <(keyval inlineTableValSep?)*> */
+ nil,
+ /* 16 tableKey <- <(key (tableKeySep key)*)> */
+ func() bool {
+ position204, tokenIndex204 := position, tokenIndex
+ {
+ position205 := position
+ if !_rules[rulekey]() {
+ goto l204
+ }
+ l206:
+ {
+ position207, tokenIndex207 := position, tokenIndex
+ {
+ position208 := position
+ if !_rules[rulews]() {
+ goto l207
+ }
+ if buffer[position] != rune('.') {
+ goto l207
+ }
+ position++
+ if !_rules[rulews]() {
+ goto l207
+ }
+ add(ruletableKeySep, position208)
+ }
+ if !_rules[rulekey]() {
+ goto l207
+ }
+ goto l206
+ l207:
+ position, tokenIndex = position207, tokenIndex207
+ }
+ add(ruletableKey, position205)
+ }
+ return true
+ l204:
+ position, tokenIndex = position204, tokenIndex204
+ return false
+ },
+ /* 17 tableKeySep <- <(ws '.' ws)> */
+ nil,
+ /* 18 inlineTableValSep <- <(ws ',' ws)> */
+ nil,
+ /* 19 integer <- <(('-' / '+')? int)> */
+ func() bool {
+ position211, tokenIndex211 := position, tokenIndex
+ {
+ position212 := position
+ {
+ position213, tokenIndex213 := position, tokenIndex
+ {
+ position215, tokenIndex215 := position, tokenIndex
+ if buffer[position] != rune('-') {
+ goto l216
+ }
+ position++
+ goto l215
+ l216:
+ position, tokenIndex = position215, tokenIndex215
+ if buffer[position] != rune('+') {
+ goto l213
+ }
+ position++
+ }
+ l215:
+ goto l214
+ l213:
+ position, tokenIndex = position213, tokenIndex213
+ }
+ l214:
+ {
+ position217 := position
+ {
+ position218, tokenIndex218 := position, tokenIndex
+ if c := buffer[position]; c < rune('1') || c > rune('9') {
+ goto l219
+ }
+ position++
+ {
+ position222, tokenIndex222 := position, tokenIndex
+ if !_rules[ruledigit]() {
+ goto l223
+ }
+ goto l222
+ l223:
+ position, tokenIndex = position222, tokenIndex222
+ if buffer[position] != rune('_') {
+ goto l219
+ }
+ position++
+ if !_rules[ruledigit]() {
+ goto l219
+ }
+ }
+ l222:
+ l220:
+ {
+ position221, tokenIndex221 := position, tokenIndex
+ {
+ position224, tokenIndex224 := position, tokenIndex
+ if !_rules[ruledigit]() {
+ goto l225
+ }
+ goto l224
+ l225:
+ position, tokenIndex = position224, tokenIndex224
+ if buffer[position] != rune('_') {
+ goto l221
+ }
+ position++
+ if !_rules[ruledigit]() {
+ goto l221
+ }
+ }
+ l224:
+ goto l220
+ l221:
+ position, tokenIndex = position221, tokenIndex221
+ }
+ goto l218
+ l219:
+ position, tokenIndex = position218, tokenIndex218
+ if !_rules[ruledigit]() {
+ goto l211
+ }
+ }
+ l218:
+ add(ruleint, position217)
+ }
+ add(ruleinteger, position212)
+ }
+ return true
+ l211:
+ position, tokenIndex = position211, tokenIndex211
+ return false
+ },
+ /* 20 int <- <(([1-9] (digit / ('_' digit))+) / digit)> */
+ nil,
+ /* 21 float <- <(integer ((frac exp?) / (frac? exp)))> */
+ nil,
+ /* 22 frac <- <('.' digit (digit / ('_' digit))*)> */
+ func() bool {
+ position228, tokenIndex228 := position, tokenIndex
+ {
+ position229 := position
+ if buffer[position] != rune('.') {
+ goto l228
+ }
+ position++
+ if !_rules[ruledigit]() {
+ goto l228
+ }
+ l230:
+ {
+ position231, tokenIndex231 := position, tokenIndex
+ {
+ position232, tokenIndex232 := position, tokenIndex
+ if !_rules[ruledigit]() {
+ goto l233
+ }
+ goto l232
+ l233:
+ position, tokenIndex = position232, tokenIndex232
+ if buffer[position] != rune('_') {
+ goto l231
+ }
+ position++
+ if !_rules[ruledigit]() {
+ goto l231
+ }
+ }
+ l232:
+ goto l230
+ l231:
+ position, tokenIndex = position231, tokenIndex231
+ }
+ add(rulefrac, position229)
+ }
+ return true
+ l228:
+ position, tokenIndex = position228, tokenIndex228
+ return false
+ },
+ /* 23 exp <- <(('e' / 'E') ('-' / '+')? digit (digit / ('_' digit))*)> */
+ func() bool {
+ position234, tokenIndex234 := position, tokenIndex
+ {
+ position235 := position
+ {
+ position236, tokenIndex236 := position, tokenIndex
+ if buffer[position] != rune('e') {
+ goto l237
+ }
+ position++
+ goto l236
+ l237:
+ position, tokenIndex = position236, tokenIndex236
+ if buffer[position] != rune('E') {
+ goto l234
+ }
+ position++
+ }
+ l236:
+ {
+ position238, tokenIndex238 := position, tokenIndex
+ {
+ position240, tokenIndex240 := position, tokenIndex
+ if buffer[position] != rune('-') {
+ goto l241
+ }
+ position++
+ goto l240
+ l241:
+ position, tokenIndex = position240, tokenIndex240
+ if buffer[position] != rune('+') {
+ goto l238
+ }
+ position++
+ }
+ l240:
+ goto l239
+ l238:
+ position, tokenIndex = position238, tokenIndex238
+ }
+ l239:
+ if !_rules[ruledigit]() {
+ goto l234
+ }
+ l242:
+ {
+ position243, tokenIndex243 := position, tokenIndex
+ {
+ position244, tokenIndex244 := position, tokenIndex
+ if !_rules[ruledigit]() {
+ goto l245
+ }
+ goto l244
+ l245:
+ position, tokenIndex = position244, tokenIndex244
+ if buffer[position] != rune('_') {
+ goto l243
+ }
+ position++
+ if !_rules[ruledigit]() {
+ goto l243
+ }
+ }
+ l244:
+ goto l242
+ l243:
+ position, tokenIndex = position243, tokenIndex243
+ }
+ add(ruleexp, position235)
+ }
+ return true
+ l234:
+ position, tokenIndex = position234, tokenIndex234
+ return false
+ },
+ /* 24 string <- <(mlLiteralString / literalString / mlBasicString / basicString)> */
+ nil,
+ /* 25 basicString <- <(<('"' basicChar* '"')> Action17)> */
+ nil,
+ /* 26 basicChar <- <(basicUnescaped / escaped)> */
+ func() bool {
+ position248, tokenIndex248 := position, tokenIndex
+ {
+ position249 := position
+ {
+ position250, tokenIndex250 := position, tokenIndex
+ {
+ position252 := position
+ {
+ switch buffer[position] {
+ case ' ', '!':
+ if c := buffer[position]; c < rune(' ') || c > rune('!') {
+ goto l251
+ }
+ position++
+ break
+ case '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[':
+ if c := buffer[position]; c < rune('#') || c > rune('[') {
+ goto l251
+ }
+ position++
+ break
+ default:
+ if c := buffer[position]; c < rune(']') || c > rune('\U0010ffff') {
+ goto l251
+ }
+ position++
+ break
+ }
+ }
+
+ add(rulebasicUnescaped, position252)
+ }
+ goto l250
+ l251:
+ position, tokenIndex = position250, tokenIndex250
+ {
+ position254 := position
+ if !_rules[ruleescape]() {
+ goto l248
+ }
+ {
+ switch buffer[position] {
+ case 'U':
+ if buffer[position] != rune('U') {
+ goto l248
+ }
+ position++
+ if !_rules[rulehexQuad]() {
+ goto l248
+ }
+ if !_rules[rulehexQuad]() {
+ goto l248
+ }
+ break
+ case 'u':
+ if buffer[position] != rune('u') {
+ goto l248
+ }
+ position++
+ if !_rules[rulehexQuad]() {
+ goto l248
+ }
+ break
+ case '\\':
+ if buffer[position] != rune('\\') {
+ goto l248
+ }
+ position++
+ break
+ case '/':
+ if buffer[position] != rune('/') {
+ goto l248
+ }
+ position++
+ break
+ case '"':
+ if buffer[position] != rune('"') {
+ goto l248
+ }
+ position++
+ break
+ case 'r':
+ if buffer[position] != rune('r') {
+ goto l248
+ }
+ position++
+ break
+ case 'f':
+ if buffer[position] != rune('f') {
+ goto l248
+ }
+ position++
+ break
+ case 'n':
+ if buffer[position] != rune('n') {
+ goto l248
+ }
+ position++
+ break
+ case 't':
+ if buffer[position] != rune('t') {
+ goto l248
+ }
+ position++
+ break
+ default:
+ if buffer[position] != rune('b') {
+ goto l248
+ }
+ position++
+ break
+ }
+ }
+
+ add(ruleescaped, position254)
+ }
+ }
+ l250:
+ add(rulebasicChar, position249)
+ }
+ return true
+ l248:
+ position, tokenIndex = position248, tokenIndex248
+ return false
+ },
+ /* 27 escaped <- <(escape ((&('U') ('U' hexQuad hexQuad)) | (&('u') ('u' hexQuad)) | (&('\\') '\\') | (&('/') '/') | (&('"') '"') | (&('r') 'r') | (&('f') 'f') | (&('n') 'n') | (&('t') 't') | (&('b') 'b')))> */
+ nil,
+ /* 28 basicUnescaped <- <((&(' ' | '!') [ -!]) | (&('#' | '$' | '%' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '[') [#-[]) | (&(']' | '^' | '_' | '`' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '{' | '|' | '}' | '~' | '\u007f' | '\u0080' | '\u0081' | '\u0082' | '\u0083' | '\u0084' | '\u0085' | '\u0086' | '\u0087' | '\u0088' | '\u0089' | '\u008a' | '\u008b' | '\u008c' | '\u008d' | '\u008e' | '\u008f' | '\u0090' | '\u0091' | '\u0092' | '\u0093' | '\u0094' | '\u0095' | '\u0096' | '\u0097' | '\u0098' | '\u0099' | '\u009a' | '\u009b' | '\u009c' | '\u009d' | '\u009e' | '\u009f' | '\u00a0' | '¡' | '¢' | '£' | '¤' | '¥' | '¦' | '§' | '¨' | '©' | 'ª' | '«' | '¬' | '\u00ad' | '®' | '¯' | '°' | '±' | '²' | '³' | '´' | 'µ' | '¶' | '·' | '¸' | '¹' | 'º' | '»' | '¼' | '½' | '¾' | '¿' | 'À' | 'Á' | 'Â' | 'Ã' | 'Ä' | 'Å' | 'Æ' | 'Ç' | 'È' | 'É' | 'Ê' | 'Ë' | 'Ì' | 'Í' | 'Î' | 'Ï' | 'Ð' | 'Ñ' | 'Ò' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | '×' | 'Ø' | 'Ù' | 'Ú' | 'Û' | 'Ü' | 'Ý' | 'Þ' | 'ß' | 'à' | 'á' | 'â' | 'ã' | 'ä' | 'å' | 'æ' | 'ç' | 'è' | 'é' | 'ê' | 'ë' | 'ì' | 'í' | 'î' | 'ï' | 'ð' | 'ñ' | 'ò' | 'ó' | 'ô' | 'õ' | 'ö' | '÷' | 'ø' | 'ù' | 'ú' | 'û' | 'ü' | 'ý' | 'þ' | 'ÿ') []-\U0010ffff]))> */
+ nil,
+ /* 29 escape <- <'\\'> */
+ func() bool {
+ position258, tokenIndex258 := position, tokenIndex
+ {
+ position259 := position
+ if buffer[position] != rune('\\') {
+ goto l258
+ }
+ position++
+ add(ruleescape, position259)
+ }
+ return true
+ l258:
+ position, tokenIndex = position258, tokenIndex258
+ return false
+ },
+ /* 30 mlBasicString <- <('"' '"' '"' mlBasicBody ('"' '"' '"') Action18)> */
+ nil,
+ /* 31 mlBasicBody <- <((<(basicChar / newline)> Action19) / (escape newline wsnl))*> */
+ nil,
+ /* 32 literalString <- <('\'' <literalChar*> '\'' Action20)> */
+ nil,
+ /* 33 literalChar <- <((&('\t') '\t') | (&(' ' | '!' | '"' | '#' | '$' | '%' | '&') [ -&]) | (&('(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '[' | '\\' | ']' | '^' | '_' | '`' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '{' | '|' | '}' | '~' | '\u007f' | '\u0080' | '\u0081' | '\u0082' | '\u0083' | '\u0084' | '\u0085' | '\u0086' | '\u0087' | '\u0088' | '\u0089' | '\u008a' | '\u008b' | '\u008c' | '\u008d' | '\u008e' | '\u008f' | '\u0090' | '\u0091' | '\u0092' | '\u0093' | '\u0094' | '\u0095' | '\u0096' | '\u0097' | '\u0098' | '\u0099' | '\u009a' | '\u009b' | '\u009c' | '\u009d' | '\u009e' | '\u009f' | '\u00a0' | '¡' | '¢' | '£' | '¤' | '¥' | '¦' | '§' | '¨' | '©' | 'ª' | '«' | '¬' | '\u00ad' | '®' | '¯' | '°' | '±' | '²' | '³' | '´' | 'µ' | '¶' | '·' | '¸' | '¹' | 'º' | '»' | '¼' | '½' | '¾' | '¿' | 'À' | 'Á' | 'Â' | 'Ã' | 'Ä' | 'Å' | 'Æ' | 'Ç' | 'È' | 'É' | 'Ê' | 'Ë' | 'Ì' | 'Í' | 'Î' | 'Ï' | 'Ð' | 'Ñ' | 'Ò' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | '×' | 'Ø' | 'Ù' | 'Ú' | 'Û' | 'Ü' | 'Ý' | 'Þ' | 'ß' | 'à' | 'á' | 'â' | 'ã' | 'ä' | 'å' | 'æ' | 'ç' | 'è' | 'é' | 'ê' | 'ë' | 'ì' | 'í' | 'î' | 'ï' | 'ð' | 'ñ' | 'ò' | 'ó' | 'ô' | 'õ' | 'ö' | '÷' | 'ø' | 'ù' | 'ú' | 'û' | 'ü' | 'ý' | 'þ' | 'ÿ') [(-\U0010ffff]))> */
+ nil,
+ /* 34 mlLiteralString <- <('\'' '\'' '\'' <mlLiteralBody> ('\'' '\'' '\'') Action21)> */
+ nil,
+ /* 35 mlLiteralBody <- <(!('\'' '\'' '\'') (mlLiteralChar / newline))*> */
+ nil,
+ /* 36 mlLiteralChar <- <('\t' / [ -\U0010ffff])> */
+ nil,
+ /* 37 hexdigit <- <((&('a' | 'b' | 'c' | 'd' | 'e' | 'f') [a-f]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F') [A-F]) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]))> */
+ func() bool {
+ position267, tokenIndex267 := position, tokenIndex
+ {
+ position268 := position
+ {
+ switch buffer[position] {
+ case 'a', 'b', 'c', 'd', 'e', 'f':
+ if c := buffer[position]; c < rune('a') || c > rune('f') {
+ goto l267
+ }
+ position++
+ break
+ case 'A', 'B', 'C', 'D', 'E', 'F':
+ if c := buffer[position]; c < rune('A') || c > rune('F') {
+ goto l267
+ }
+ position++
+ break
+ default:
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l267
+ }
+ position++
+ break
+ }
+ }
+
+ add(rulehexdigit, position268)
+ }
+ return true
+ l267:
+ position, tokenIndex = position267, tokenIndex267
+ return false
+ },
+ /* 38 hexQuad <- <(hexdigit hexdigit hexdigit hexdigit)> */
+ func() bool {
+ position270, tokenIndex270 := position, tokenIndex
+ {
+ position271 := position
+ if !_rules[rulehexdigit]() {
+ goto l270
+ }
+ if !_rules[rulehexdigit]() {
+ goto l270
+ }
+ if !_rules[rulehexdigit]() {
+ goto l270
+ }
+ if !_rules[rulehexdigit]() {
+ goto l270
+ }
+ add(rulehexQuad, position271)
+ }
+ return true
+ l270:
+ position, tokenIndex = position270, tokenIndex270
+ return false
+ },
+ /* 39 boolean <- <(('t' 'r' 'u' 'e') / ('f' 'a' 'l' 's' 'e'))> */
+ nil,
+ /* 40 dateFullYear <- <digitQuad> */
+ nil,
+ /* 41 dateMonth <- <digitDual> */
+ nil,
+ /* 42 dateMDay <- <digitDual> */
+ nil,
+ /* 43 timeHour <- <digitDual> */
+ func() bool {
+ position276, tokenIndex276 := position, tokenIndex
+ {
+ position277 := position
+ if !_rules[ruledigitDual]() {
+ goto l276
+ }
+ add(ruletimeHour, position277)
+ }
+ return true
+ l276:
+ position, tokenIndex = position276, tokenIndex276
+ return false
+ },
+ /* 44 timeMinute <- <digitDual> */
+ func() bool {
+ position278, tokenIndex278 := position, tokenIndex
+ {
+ position279 := position
+ if !_rules[ruledigitDual]() {
+ goto l278
+ }
+ add(ruletimeMinute, position279)
+ }
+ return true
+ l278:
+ position, tokenIndex = position278, tokenIndex278
+ return false
+ },
+ /* 45 timeSecond <- <digitDual> */
+ nil,
+ /* 46 timeSecfrac <- <('.' digit+)> */
+ nil,
+ /* 47 timeNumoffset <- <(('-' / '+') timeHour ':' timeMinute)> */
+ nil,
+ /* 48 timeOffset <- <('Z' / timeNumoffset)> */
+ nil,
+ /* 49 partialTime <- <(timeHour ':' timeMinute ':' timeSecond timeSecfrac?)> */
+ func() bool {
+ position284, tokenIndex284 := position, tokenIndex
+ {
+ position285 := position
+ if !_rules[ruletimeHour]() {
+ goto l284
+ }
+ if buffer[position] != rune(':') {
+ goto l284
+ }
+ position++
+ if !_rules[ruletimeMinute]() {
+ goto l284
+ }
+ if buffer[position] != rune(':') {
+ goto l284
+ }
+ position++
+ {
+ position286 := position
+ if !_rules[ruledigitDual]() {
+ goto l284
+ }
+ add(ruletimeSecond, position286)
+ }
+ {
+ position287, tokenIndex287 := position, tokenIndex
+ {
+ position289 := position
+ if buffer[position] != rune('.') {
+ goto l287
+ }
+ position++
+ if !_rules[ruledigit]() {
+ goto l287
+ }
+ l290:
+ {
+ position291, tokenIndex291 := position, tokenIndex
+ if !_rules[ruledigit]() {
+ goto l291
+ }
+ goto l290
+ l291:
+ position, tokenIndex = position291, tokenIndex291
+ }
+ add(ruletimeSecfrac, position289)
+ }
+ goto l288
+ l287:
+ position, tokenIndex = position287, tokenIndex287
+ }
+ l288:
+ add(rulepartialTime, position285)
+ }
+ return true
+ l284:
+ position, tokenIndex = position284, tokenIndex284
+ return false
+ },
+ /* 50 fullDate <- <(dateFullYear '-' dateMonth '-' dateMDay)> */
+ nil,
+ /* 51 fullTime <- <(partialTime timeOffset)> */
+ nil,
+ /* 52 datetime <- <((fullDate ('T' fullTime)?) / partialTime)> */
+ nil,
+ /* 53 digit <- <[0-9]> */
+ func() bool {
+ position295, tokenIndex295 := position, tokenIndex
+ {
+ position296 := position
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l295
+ }
+ position++
+ add(ruledigit, position296)
+ }
+ return true
+ l295:
+ position, tokenIndex = position295, tokenIndex295
+ return false
+ },
+ /* 54 digitDual <- <(digit digit)> */
+ func() bool {
+ position297, tokenIndex297 := position, tokenIndex
+ {
+ position298 := position
+ if !_rules[ruledigit]() {
+ goto l297
+ }
+ if !_rules[ruledigit]() {
+ goto l297
+ }
+ add(ruledigitDual, position298)
+ }
+ return true
+ l297:
+ position, tokenIndex = position297, tokenIndex297
+ return false
+ },
+ /* 55 digitQuad <- <(digitDual digitDual)> */
+ nil,
+ /* 56 array <- <('[' Action22 wsnl arrayValues? wsnl ']')> */
+ nil,
+ /* 57 arrayValues <- <(val Action23 (wsnl comment? wsnl arraySep wsnl comment? wsnl val Action24)* wsnl arraySep? wsnl comment?)> */
+ nil,
+ /* 58 arraySep <- <','> */
+ func() bool {
+ position302, tokenIndex302 := position, tokenIndex
+ {
+ position303 := position
+ if buffer[position] != rune(',') {
+ goto l302
+ }
+ position++
+ add(rulearraySep, position303)
+ }
+ return true
+ l302:
+ position, tokenIndex = position302, tokenIndex302
+ return false
+ },
+ /* 60 Action0 <- <{ _ = buffer }> */
+ nil,
+ nil,
+ /* 62 Action1 <- <{ p.SetTableString(begin, end) }> */
+ nil,
+ /* 63 Action2 <- <{ p.AddLineCount(end - begin) }> */
+ nil,
+ /* 64 Action3 <- <{ p.AddLineCount(end - begin) }> */
+ nil,
+ /* 65 Action4 <- <{ p.AddKeyValue() }> */
+ nil,
+ /* 66 Action5 <- <{ p.SetKey(p.buffer, begin, end) }> */
+ nil,
+ /* 67 Action6 <- <{ p.SetKey(p.buffer, begin-1, end+1) }> */
+ nil,
+ /* 68 Action7 <- <{ p.SetTime(begin, end) }> */
+ nil,
+ /* 69 Action8 <- <{ p.SetFloat64(begin, end) }> */
+ nil,
+ /* 70 Action9 <- <{ p.SetInt64(begin, end) }> */
+ nil,
+ /* 71 Action10 <- <{ p.SetString(begin, end) }> */
+ nil,
+ /* 72 Action11 <- <{ p.SetBool(begin, end) }> */
+ nil,
+ /* 73 Action12 <- <{ p.SetArray(begin, end) }> */
+ nil,
+ /* 74 Action13 <- <{ p.SetTable(p.buffer, begin, end) }> */
+ nil,
+ /* 75 Action14 <- <{ p.SetArrayTable(p.buffer, begin, end) }> */
+ nil,
+ /* 76 Action15 <- <{ p.StartInlineTable() }> */
+ nil,
+ /* 77 Action16 <- <{ p.EndInlineTable() }> */
+ nil,
+ /* 78 Action17 <- <{ p.SetBasicString(p.buffer, begin, end) }> */
+ nil,
+ /* 79 Action18 <- <{ p.SetMultilineString() }> */
+ nil,
+ /* 80 Action19 <- <{ p.AddMultilineBasicBody(p.buffer, begin, end) }> */
+ nil,
+ /* 81 Action20 <- <{ p.SetLiteralString(p.buffer, begin, end) }> */
+ nil,
+ /* 82 Action21 <- <{ p.SetMultilineLiteralString(p.buffer, begin, end) }> */
+ nil,
+ /* 83 Action22 <- <{ p.StartArray() }> */
+ nil,
+ /* 84 Action23 <- <{ p.AddArrayVal() }> */
+ nil,
+ /* 85 Action24 <- <{ p.AddArrayVal() }> */
+ nil,
+ }
+ p.rules = _rules
+}
diff --git a/vendor/github.com/naoina/toml/util.go b/vendor/github.com/naoina/toml/util.go
new file mode 100644
index 000000000..f882f4e5f
--- /dev/null
+++ b/vendor/github.com/naoina/toml/util.go
@@ -0,0 +1,65 @@
+package toml
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+const fieldTagName = "toml"
+
+// fieldCache maps normalized field names to their position in a struct.
+type fieldCache struct {
+ named map[string]fieldInfo // fields with an explicit name in tag
+ auto map[string]fieldInfo // fields with auto-assigned normalized names
+}
+
+type fieldInfo struct {
+ index []int
+ name string
+ ignored bool
+}
+
+func makeFieldCache(cfg *Config, rt reflect.Type) fieldCache {
+ named, auto := make(map[string]fieldInfo), make(map[string]fieldInfo)
+ for i := 0; i < rt.NumField(); i++ {
+ ft := rt.Field(i)
+ // skip unexported fields
+ if ft.PkgPath != "" && !ft.Anonymous {
+ continue
+ }
+ col, _ := extractTag(ft.Tag.Get(fieldTagName))
+ info := fieldInfo{index: ft.Index, name: ft.Name, ignored: col == "-"}
+ if col == "" || col == "-" {
+ auto[cfg.NormFieldName(rt, ft.Name)] = info
+ } else {
+ named[col] = info
+ }
+ }
+ return fieldCache{named, auto}
+}
+
+func (fc fieldCache) findField(cfg *Config, rv reflect.Value, name string) (reflect.Value, string, error) {
+ info, found := fc.named[name]
+ if !found {
+ info, found = fc.auto[cfg.NormFieldName(rv.Type(), name)]
+ }
+ if !found {
+ if cfg.MissingField == nil {
+ return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type())
+ } else {
+ return reflect.Value{}, "", cfg.MissingField(rv.Type(), name)
+ }
+ } else if info.ignored {
+ return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' in %v cannot be set through TOML", name, rv.Type())
+ }
+ return rv.FieldByIndex(info.index), info.name, nil
+}
+
+func extractTag(tag string) (col, rest string) {
+ tags := strings.SplitN(tag, ",", 2)
+ if len(tags) == 2 {
+ return strings.TrimSpace(tags[0]), strings.TrimSpace(tags[1])
+ }
+ return strings.TrimSpace(tags[0]), ""
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/LICENCE.md b/vendor/github.com/olekukonko/tablewriter/LICENCE.md
new file mode 100644
index 000000000..1fd848425
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/LICENCE.md
@@ -0,0 +1,19 @@
+Copyright (C) 2014 by Oleku Konko
+
+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. \ No newline at end of file
diff --git a/vendor/github.com/olekukonko/tablewriter/README.md b/vendor/github.com/olekukonko/tablewriter/README.md
new file mode 100644
index 000000000..805330adc
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/README.md
@@ -0,0 +1,204 @@
+ASCII Table Writer
+=========
+
+[![Build Status](https://travis-ci.org/olekukonko/tablewriter.png?branch=master)](https://travis-ci.org/olekukonko/tablewriter) [![Total views](https://sourcegraph.com/api/repos/github.com/olekukonko/tablewriter/counters/views.png)](https://sourcegraph.com/github.com/olekukonko/tablewriter)
+
+Generate ASCII table on the fly ... Installation is simple as
+
+ go get github.com/olekukonko/tablewriter
+
+
+#### Features
+- Automatic Padding
+- Support Multiple Lines
+- Supports Alignment
+- Support Custom Separators
+- Automatic Alignment of numbers & percentage
+- Write directly to http , file etc via `io.Writer`
+- Read directly from CSV file
+- Optional row line via `SetRowLine`
+- Normalise table header
+- Make CSV Headers optional
+- Enable or disable table border
+- Set custom footer support
+- Optional identical cells merging
+
+
+#### Example 1 - Basic
+```go
+data := [][]string{
+ []string{"A", "The Good", "500"},
+ []string{"B", "The Very very Bad Man", "288"},
+ []string{"C", "The Ugly", "120"},
+ []string{"D", "The Gopher", "800"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Name", "Sign", "Rating"})
+
+for _, v := range data {
+ table.Append(v)
+}
+table.Render() // Send output
+```
+
+##### Output 1
+```
++------+-----------------------+--------+
+| NAME | SIGN | RATING |
++------+-----------------------+--------+
+| A | The Good | 500 |
+| B | The Very very Bad Man | 288 |
+| C | The Ugly | 120 |
+| D | The Gopher | 800 |
++------+-----------------------+--------+
+```
+
+#### Example 2 - Without Border / Footer / Bulk Append
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "2233", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2233", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "2233", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
+table.SetBorder(false) // Set Border to false
+table.AppendBulk(data) // Add Bulk Data
+table.Render()
+```
+
+##### Output 2
+```
+
+ DATE | DESCRIPTION | CV2 | AMOUNT
++----------+--------------------------+-------+---------+
+ 1/1/2014 | Domain name | 2233 | $10.98
+ 1/1/2014 | January Hosting | 2233 | $54.95
+ 1/4/2014 | February Hosting | 2233 | $51.00
+ 1/4/2014 | February Extra Bandwidth | 2233 | $30.00
++----------+--------------------------+-------+---------+
+ TOTAL | $146 93
+ +-------+---------+
+
+```
+
+
+#### Example 3 - CSV
+```go
+table, _ := tablewriter.NewCSV(os.Stdout, "test_info.csv", true)
+table.SetAlignment(tablewriter.ALIGN_LEFT) // Set Alignment
+table.Render()
+```
+
+##### Output 3
+```
++----------+--------------+------+-----+---------+----------------+
+| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA |
++----------+--------------+------+-----+---------+----------------+
+| user_id | smallint(5) | NO | PRI | NULL | auto_increment |
+| username | varchar(10) | NO | | NULL | |
+| password | varchar(100) | NO | | NULL | |
++----------+--------------+------+-----+---------+----------------+
+```
+
+#### Example 4 - Custom Separator
+```go
+table, _ := tablewriter.NewCSV(os.Stdout, "test.csv", true)
+table.SetRowLine(true) // Enable row line
+
+// Change table lines
+table.SetCenterSeparator("*")
+table.SetColumnSeparator("‡")
+table.SetRowSeparator("-")
+
+table.SetAlignment(tablewriter.ALIGN_LEFT)
+table.Render()
+```
+
+##### Output 4
+```
+*------------*-----------*---------*
+╪ FIRST NAME ╪ LAST NAME ╪ SSN ╪
+*------------*-----------*---------*
+╪ John ╪ Barry ╪ 123456 ╪
+*------------*-----------*---------*
+╪ Kathy ╪ Smith ╪ 687987 ╪
+*------------*-----------*---------*
+╪ Bob ╪ McCornick ╪ 3979870 ╪
+*------------*-----------*---------*
+```
+
+##### Example 5 - Markdown Format
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "2233", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2233", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "2233", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
+table.SetCenterSeparator("|")
+table.AppendBulk(data) // Add Bulk Data
+table.Render()
+```
+
+##### Output 5
+```
+| DATE | DESCRIPTION | CV2 | AMOUNT |
+|----------|--------------------------|------|--------|
+| 1/1/2014 | Domain name | 2233 | $10.98 |
+| 1/1/2014 | January Hosting | 2233 | $54.95 |
+| 1/4/2014 | February Hosting | 2233 | $51.00 |
+| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |
+```
+
+#### Example 6 - Identical cells merging
+```go
+data := [][]string{
+ []string{"1/1/2014", "Domain name", "1234", "$10.98"},
+ []string{"1/1/2014", "January Hosting", "2345", "$54.95"},
+ []string{"1/4/2014", "February Hosting", "3456", "$51.00"},
+ []string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
+}
+
+table := tablewriter.NewWriter(os.Stdout)
+table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
+table.SetFooter([]string{"", "", "Total", "$146.93"})
+table.SetAutoMergeCells(true)
+table.SetRowLine(true)
+table.AppendBulk(data)
+table.Render()
+```
+
+##### Output 6
+```
++----------+--------------------------+-------+---------+
+| DATE | DESCRIPTION | CV2 | AMOUNT |
++----------+--------------------------+-------+---------+
+| 1/1/2014 | Domain name | 1234 | $10.98 |
++ +--------------------------+-------+---------+
+| | January Hosting | 2345 | $54.95 |
++----------+--------------------------+-------+---------+
+| 1/4/2014 | February Hosting | 3456 | $51.00 |
++ +--------------------------+-------+---------+
+| | February Extra Bandwidth | 4567 | $30.00 |
++----------+--------------------------+-------+---------+
+| TOTAL | $146 93 |
++----------+--------------------------+-------+---------+
+```
+
+#### TODO
+- ~~Import Directly from CSV~~ - `done`
+- ~~Support for `SetFooter`~~ - `done`
+- ~~Support for `SetBorder`~~ - `done`
+- ~~Support table with uneven rows~~ - `done`
+- Support custom alignment
+- General Improvement & Optimisation
+- `NewHTML` Parse table from HTML
diff --git a/vendor/github.com/olekukonko/tablewriter/csv.go b/vendor/github.com/olekukonko/tablewriter/csv.go
new file mode 100644
index 000000000..98878303b
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/csv.go
@@ -0,0 +1,52 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+package tablewriter
+
+import (
+ "encoding/csv"
+ "io"
+ "os"
+)
+
+// Start A new table by importing from a CSV file
+// Takes io.Writer and csv File name
+func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) {
+ file, err := os.Open(fileName)
+ if err != nil {
+ return &Table{}, err
+ }
+ defer file.Close()
+ csvReader := csv.NewReader(file)
+ t, err := NewCSVReader(writer, csvReader, hasHeader)
+ return t, err
+}
+
+// Start a New Table Writer with csv.Reader
+// This enables customisation such as reader.Comma = ';'
+// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
+func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) {
+ t := NewWriter(writer)
+ if hasHeader {
+ // Read the first row
+ headers, err := csvReader.Read()
+ if err != nil {
+ return &Table{}, err
+ }
+ t.SetHeader(headers)
+ }
+ for {
+ record, err := csvReader.Read()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return &Table{}, err
+ }
+ t.Append(record)
+ }
+ return t, nil
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/table.go b/vendor/github.com/olekukonko/tablewriter/table.go
new file mode 100644
index 000000000..3314bfba5
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/table.go
@@ -0,0 +1,662 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+// Create & Generate text based table
+package tablewriter
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "regexp"
+ "strings"
+)
+
+const (
+ MAX_ROW_WIDTH = 30
+)
+
+const (
+ CENTER = "+"
+ ROW = "-"
+ COLUMN = "|"
+ SPACE = " "
+ NEWLINE = "\n"
+)
+
+const (
+ ALIGN_DEFAULT = iota
+ ALIGN_CENTER
+ ALIGN_RIGHT
+ ALIGN_LEFT
+)
+
+var (
+ decimal = regexp.MustCompile(`^-*\d*\.?\d*$`)
+ percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`)
+)
+
+type Border struct {
+ Left bool
+ Right bool
+ Top bool
+ Bottom bool
+}
+
+type Table struct {
+ out io.Writer
+ rows [][]string
+ lines [][][]string
+ cs map[int]int
+ rs map[int]int
+ headers []string
+ footers []string
+ autoFmt bool
+ autoWrap bool
+ mW int
+ pCenter string
+ pRow string
+ pColumn string
+ tColumn int
+ tRow int
+ hAlign int
+ fAlign int
+ align int
+ newLine string
+ rowLine bool
+ autoMergeCells bool
+ hdrLine bool
+ borders Border
+ colSize int
+}
+
+// Start New Table
+// Take io.Writer Directly
+func NewWriter(writer io.Writer) *Table {
+ t := &Table{
+ out: writer,
+ rows: [][]string{},
+ lines: [][][]string{},
+ cs: make(map[int]int),
+ rs: make(map[int]int),
+ headers: []string{},
+ footers: []string{},
+ autoFmt: true,
+ autoWrap: true,
+ mW: MAX_ROW_WIDTH,
+ pCenter: CENTER,
+ pRow: ROW,
+ pColumn: COLUMN,
+ tColumn: -1,
+ tRow: -1,
+ hAlign: ALIGN_DEFAULT,
+ fAlign: ALIGN_DEFAULT,
+ align: ALIGN_DEFAULT,
+ newLine: NEWLINE,
+ rowLine: false,
+ hdrLine: true,
+ borders: Border{Left: true, Right: true, Bottom: true, Top: true},
+ colSize: -1}
+ return t
+}
+
+// Render table output
+func (t Table) Render() {
+ if t.borders.Top {
+ t.printLine(true)
+ }
+ t.printHeading()
+ if t.autoMergeCells {
+ t.printRowsMergeCells()
+ } else {
+ t.printRows()
+ }
+
+ if !t.rowLine && t.borders.Bottom {
+ t.printLine(true)
+ }
+ t.printFooter()
+
+}
+
+// Set table header
+func (t *Table) SetHeader(keys []string) {
+ t.colSize = len(keys)
+ for i, v := range keys {
+ t.parseDimension(v, i, -1)
+ t.headers = append(t.headers, v)
+ }
+}
+
+// Set table Footer
+func (t *Table) SetFooter(keys []string) {
+ //t.colSize = len(keys)
+ for i, v := range keys {
+ t.parseDimension(v, i, -1)
+ t.footers = append(t.footers, v)
+ }
+}
+
+// Turn header autoformatting on/off. Default is on (true).
+func (t *Table) SetAutoFormatHeaders(auto bool) {
+ t.autoFmt = auto
+}
+
+// Turn automatic multiline text adjustment on/off. Default is on (true).
+func (t *Table) SetAutoWrapText(auto bool) {
+ t.autoWrap = auto
+}
+
+// Set the Default column width
+func (t *Table) SetColWidth(width int) {
+ t.mW = width
+}
+
+// Set the Column Separator
+func (t *Table) SetColumnSeparator(sep string) {
+ t.pColumn = sep
+}
+
+// Set the Row Separator
+func (t *Table) SetRowSeparator(sep string) {
+ t.pRow = sep
+}
+
+// Set the center Separator
+func (t *Table) SetCenterSeparator(sep string) {
+ t.pCenter = sep
+}
+
+// Set Header Alignment
+func (t *Table) SetHeaderAlignment(hAlign int) {
+ t.hAlign = hAlign
+}
+
+// Set Footer Alignment
+func (t *Table) SetFooterAlignment(fAlign int) {
+ t.fAlign = fAlign
+}
+
+// Set Table Alignment
+func (t *Table) SetAlignment(align int) {
+ t.align = align
+}
+
+// Set New Line
+func (t *Table) SetNewLine(nl string) {
+ t.newLine = nl
+}
+
+// Set Header Line
+// This would enable / disable a line after the header
+func (t *Table) SetHeaderLine(line bool) {
+ t.hdrLine = line
+}
+
+// Set Row Line
+// This would enable / disable a line on each row of the table
+func (t *Table) SetRowLine(line bool) {
+ t.rowLine = line
+}
+
+// Set Auto Merge Cells
+// This would enable / disable the merge of cells with identical values
+func (t *Table) SetAutoMergeCells(auto bool) {
+ t.autoMergeCells = auto
+}
+
+// Set Table Border
+// This would enable / disable line around the table
+func (t *Table) SetBorder(border bool) {
+ t.SetBorders(Border{border, border, border, border})
+}
+
+func (t *Table) SetBorders(border Border) {
+ t.borders = border
+}
+
+// Append row to table
+func (t *Table) Append(row []string) {
+ rowSize := len(t.headers)
+ if rowSize > t.colSize {
+ t.colSize = rowSize
+ }
+
+ n := len(t.lines)
+ line := [][]string{}
+ for i, v := range row {
+
+ // Detect string width
+ // Detect String height
+ // Break strings into words
+ out := t.parseDimension(v, i, n)
+
+ // Append broken words
+ line = append(line, out)
+ }
+ t.lines = append(t.lines, line)
+}
+
+// Allow Support for Bulk Append
+// Eliminates repeated for loops
+func (t *Table) AppendBulk(rows [][]string) {
+ for _, row := range rows {
+ t.Append(row)
+ }
+}
+
+// Print line based on row width
+func (t Table) printLine(nl bool) {
+ fmt.Fprint(t.out, t.pCenter)
+ for i := 0; i < len(t.cs); i++ {
+ v := t.cs[i]
+ fmt.Fprintf(t.out, "%s%s%s%s",
+ t.pRow,
+ strings.Repeat(string(t.pRow), v),
+ t.pRow,
+ t.pCenter)
+ }
+ if nl {
+ fmt.Fprint(t.out, t.newLine)
+ }
+}
+
+// Print line based on row width with our without cell separator
+func (t Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
+ fmt.Fprint(t.out, t.pCenter)
+ for i := 0; i < len(t.cs); i++ {
+ v := t.cs[i]
+ if i > len(displayCellSeparator) || displayCellSeparator[i] {
+ // Display the cell separator
+ fmt.Fprintf(t.out, "%s%s%s%s",
+ t.pRow,
+ strings.Repeat(string(t.pRow), v),
+ t.pRow,
+ t.pCenter)
+ } else {
+ // Don't display the cell separator for this cell
+ fmt.Fprintf(t.out, "%s%s",
+ strings.Repeat(" ", v+2),
+ t.pCenter)
+ }
+ }
+ if nl {
+ fmt.Fprint(t.out, t.newLine)
+ }
+}
+
+// Return the PadRight function if align is left, PadLeft if align is right,
+// and Pad by default
+func pad(align int) func(string, string, int) string {
+ padFunc := Pad
+ switch align {
+ case ALIGN_LEFT:
+ padFunc = PadRight
+ case ALIGN_RIGHT:
+ padFunc = PadLeft
+ }
+ return padFunc
+}
+
+// Print heading information
+func (t Table) printHeading() {
+ // Check if headers is available
+ if len(t.headers) < 1 {
+ return
+ }
+
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
+
+ // Identify last column
+ end := len(t.cs) - 1
+
+ // Get pad function
+ padFunc := pad(t.hAlign)
+
+ // Print Heading column
+ for i := 0; i <= end; i++ {
+ v := t.cs[i]
+ h := t.headers[i]
+ if t.autoFmt {
+ h = Title(h)
+ }
+ pad := ConditionString((i == end && !t.borders.Left), SPACE, t.pColumn)
+ fmt.Fprintf(t.out, " %s %s",
+ padFunc(h, SPACE, v),
+ pad)
+ }
+ // Next line
+ fmt.Fprint(t.out, t.newLine)
+ if t.hdrLine {
+ t.printLine(true)
+ }
+}
+
+// Print heading information
+func (t Table) printFooter() {
+ // Check if headers is available
+ if len(t.footers) < 1 {
+ return
+ }
+
+ // Only print line if border is not set
+ if !t.borders.Bottom {
+ t.printLine(true)
+ }
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
+
+ // Identify last column
+ end := len(t.cs) - 1
+
+ // Get pad function
+ padFunc := pad(t.fAlign)
+
+ // Print Heading column
+ for i := 0; i <= end; i++ {
+ v := t.cs[i]
+ f := t.footers[i]
+ if t.autoFmt {
+ f = Title(f)
+ }
+ pad := ConditionString((i == end && !t.borders.Top), SPACE, t.pColumn)
+
+ if len(t.footers[i]) == 0 {
+ pad = SPACE
+ }
+ fmt.Fprintf(t.out, " %s %s",
+ padFunc(f, SPACE, v),
+ pad)
+ }
+ // Next line
+ fmt.Fprint(t.out, t.newLine)
+ //t.printLine(true)
+
+ hasPrinted := false
+
+ for i := 0; i <= end; i++ {
+ v := t.cs[i]
+ pad := t.pRow
+ center := t.pCenter
+ length := len(t.footers[i])
+
+ if length > 0 {
+ hasPrinted = true
+ }
+
+ // Set center to be space if length is 0
+ if length == 0 && !t.borders.Right {
+ center = SPACE
+ }
+
+ // Print first junction
+ if i == 0 {
+ fmt.Fprint(t.out, center)
+ }
+
+ // Pad With space of length is 0
+ if length == 0 {
+ pad = SPACE
+ }
+ // Ignore left space of it has printed before
+ if hasPrinted || t.borders.Left {
+ pad = t.pRow
+ center = t.pCenter
+ }
+
+ // Change Center start position
+ if center == SPACE {
+ if i < end && len(t.footers[i+1]) != 0 {
+ center = t.pCenter
+ }
+ }
+
+ // Print the footer
+ fmt.Fprintf(t.out, "%s%s%s%s",
+ pad,
+ strings.Repeat(string(pad), v),
+ pad,
+ center)
+
+ }
+
+ fmt.Fprint(t.out, t.newLine)
+
+}
+
+func (t Table) printRows() {
+ for i, lines := range t.lines {
+ t.printRow(lines, i)
+ }
+
+}
+
+// Print Row Information
+// Adjust column alignment based on type
+
+func (t Table) printRow(columns [][]string, colKey int) {
+ // Get Maximum Height
+ max := t.rs[colKey]
+ total := len(columns)
+
+ // TODO Fix uneven col size
+ // if total < t.colSize {
+ // for n := t.colSize - total; n < t.colSize ; n++ {
+ // columns = append(columns, []string{SPACE})
+ // t.cs[n] = t.mW
+ // }
+ //}
+
+ // Pad Each Height
+ // pads := []int{}
+ pads := []int{}
+
+ for i, line := range columns {
+ length := len(line)
+ pad := max - length
+ pads = append(pads, pad)
+ for n := 0; n < pad; n++ {
+ columns[i] = append(columns[i], " ")
+ }
+ }
+ //fmt.Println(max, "\n")
+ for x := 0; x < max; x++ {
+ for y := 0; y < total; y++ {
+
+ // Check if border is set
+ fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
+
+ fmt.Fprintf(t.out, SPACE)
+ str := columns[y][x]
+
+ // This would print alignment
+ // Default alignment would use multiple configuration
+ switch t.align {
+ case ALIGN_CENTER: //
+ fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
+ case ALIGN_RIGHT:
+ fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
+ case ALIGN_LEFT:
+ fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
+ default:
+ if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
+ fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
+ } else {
+ fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
+
+ // TODO Custom alignment per column
+ //if max == 1 || pads[y] > 0 {
+ // fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
+ //} else {
+ // fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
+ //}
+
+ }
+ }
+ fmt.Fprintf(t.out, SPACE)
+ }
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
+ fmt.Fprint(t.out, t.newLine)
+ }
+
+ if t.rowLine {
+ t.printLine(true)
+ }
+}
+
+// Print the rows of the table and merge the cells that are identical
+func (t Table) printRowsMergeCells() {
+ var previousLine []string
+ var displayCellBorder []bool
+ var tmpWriter bytes.Buffer
+ for i, lines := range t.lines {
+ // We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
+ previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
+ if i > 0 { //We don't need to print borders above first line
+ if t.rowLine {
+ t.printLineOptionalCellSeparators(true, displayCellBorder)
+ }
+ }
+ tmpWriter.WriteTo(t.out)
+ }
+ //Print the end of the table
+ if t.rowLine {
+ t.printLine(true)
+ }
+}
+
+// Print Row Information to a writer and merge identical cells.
+// Adjust column alignment based on type
+
+func (t Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey int, previousLine []string) ([]string, []bool) {
+ // Get Maximum Height
+ max := t.rs[colKey]
+ total := len(columns)
+
+ // Pad Each Height
+ pads := []int{}
+
+ for i, line := range columns {
+ length := len(line)
+ pad := max - length
+ pads = append(pads, pad)
+ for n := 0; n < pad; n++ {
+ columns[i] = append(columns[i], " ")
+ }
+ }
+
+ var displayCellBorder []bool
+ for x := 0; x < max; x++ {
+ for y := 0; y < total; y++ {
+
+ // Check if border is set
+ fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
+
+ fmt.Fprintf(writer, SPACE)
+
+ str := columns[y][x]
+
+ if t.autoMergeCells {
+ //Store the full line to merge mutli-lines cells
+ fullLine := strings.Join(columns[y], " ")
+ if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
+ // If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
+ displayCellBorder = append(displayCellBorder, false)
+ str = ""
+ } else {
+ // First line or different content, keep the content and print the cell border
+ displayCellBorder = append(displayCellBorder, true)
+ }
+ }
+
+ // This would print alignment
+ // Default alignment would use multiple configuration
+ switch t.align {
+ case ALIGN_CENTER: //
+ fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
+ case ALIGN_RIGHT:
+ fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
+ case ALIGN_LEFT:
+ fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
+ default:
+ if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
+ fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
+ } else {
+ fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
+ }
+ }
+ fmt.Fprintf(writer, SPACE)
+ }
+ // Check if border is set
+ // Replace with space if not set
+ fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
+ fmt.Fprint(writer, t.newLine)
+ }
+
+ //The new previous line is the current one
+ previousLine = make([]string, total)
+ for y := 0; y < total; y++ {
+ previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells
+ }
+ //Returns the newly added line and wether or not a border should be displayed above.
+ return previousLine, displayCellBorder
+}
+
+func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
+ var (
+ raw []string
+ max int
+ )
+ w := DisplayWidth(str)
+ // Calculate Width
+ // Check if with is grater than maximum width
+ if w > t.mW {
+ w = t.mW
+ }
+
+ // Check if width exists
+ v, ok := t.cs[colKey]
+ if !ok || v < w || v == 0 {
+ t.cs[colKey] = w
+ }
+
+ if rowKey == -1 {
+ return raw
+ }
+ // Calculate Height
+ if t.autoWrap {
+ raw, _ = WrapString(str, t.cs[colKey])
+ } else {
+ raw = getLines(str)
+ }
+
+ for _, line := range raw {
+ if w := DisplayWidth(line); w > max {
+ max = w
+ }
+ }
+
+ // Make sure the with is the same length as maximum word
+ // Important for cases where the width is smaller than maxu word
+ if max > t.cs[colKey] {
+ t.cs[colKey] = max
+ }
+
+ h := len(raw)
+ v, ok = t.rs[rowKey]
+
+ if !ok || v < h || v == 0 {
+ t.rs[rowKey] = h
+ }
+ //fmt.Printf("Raw %+v %d\n", raw, len(raw))
+ return raw
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/test.csv b/vendor/github.com/olekukonko/tablewriter/test.csv
new file mode 100644
index 000000000..1609327e9
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/test.csv
@@ -0,0 +1,4 @@
+first_name,last_name,ssn
+John,Barry,123456
+Kathy,Smith,687987
+Bob,McCornick,3979870 \ No newline at end of file
diff --git a/vendor/github.com/olekukonko/tablewriter/test_info.csv b/vendor/github.com/olekukonko/tablewriter/test_info.csv
new file mode 100644
index 000000000..e4c40e983
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/test_info.csv
@@ -0,0 +1,4 @@
+Field,Type,Null,Key,Default,Extra
+user_id,smallint(5),NO,PRI,NULL,auto_increment
+username,varchar(10),NO,,NULL,
+password,varchar(100),NO,,NULL, \ No newline at end of file
diff --git a/vendor/github.com/olekukonko/tablewriter/util.go b/vendor/github.com/olekukonko/tablewriter/util.go
new file mode 100644
index 000000000..2deefbc52
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/util.go
@@ -0,0 +1,72 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+package tablewriter
+
+import (
+ "math"
+ "regexp"
+ "strings"
+
+ "github.com/mattn/go-runewidth"
+)
+
+var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]")
+
+func DisplayWidth(str string) int {
+ return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, ""))
+}
+
+// Simple Condition for string
+// Returns value based on condition
+func ConditionString(cond bool, valid, inValid string) string {
+ if cond {
+ return valid
+ }
+ return inValid
+}
+
+// Format Table Header
+// Replace _ , . and spaces
+func Title(name string) string {
+ name = strings.Replace(name, "_", " ", -1)
+ name = strings.Replace(name, ".", " ", -1)
+ name = strings.TrimSpace(name)
+ return strings.ToUpper(name)
+}
+
+// Pad String
+// Attempts to play string in the center
+func Pad(s, pad string, width int) string {
+ gap := width - DisplayWidth(s)
+ if gap > 0 {
+ gapLeft := int(math.Ceil(float64(gap / 2)))
+ gapRight := gap - gapLeft
+ return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight)
+ }
+ return s
+}
+
+// Pad String Right position
+// This would pace string at the left side fo the screen
+func PadRight(s, pad string, width int) string {
+ gap := width - DisplayWidth(s)
+ if gap > 0 {
+ return s + strings.Repeat(string(pad), gap)
+ }
+ return s
+}
+
+// Pad String Left position
+// This would pace string at the right side fo the screen
+func PadLeft(s, pad string, width int) string {
+ gap := width - DisplayWidth(s)
+ if gap > 0 {
+ return strings.Repeat(string(pad), gap) + s
+ }
+ return s
+}
diff --git a/vendor/github.com/olekukonko/tablewriter/wrap.go b/vendor/github.com/olekukonko/tablewriter/wrap.go
new file mode 100644
index 000000000..5290fb65a
--- /dev/null
+++ b/vendor/github.com/olekukonko/tablewriter/wrap.go
@@ -0,0 +1,103 @@
+// Copyright 2014 Oleku Konko All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// This module is a Table Writer API for the Go Programming Language.
+// The protocols were written in pure Go and works on windows and unix systems
+
+package tablewriter
+
+import (
+ "math"
+ "strings"
+ "unicode/utf8"
+)
+
+var (
+ nl = "\n"
+ sp = " "
+)
+
+const defaultPenalty = 1e5
+
+// Wrap wraps s into a paragraph of lines of length lim, with minimal
+// raggedness.
+func WrapString(s string, lim int) ([]string, int) {
+ words := strings.Split(strings.Replace(s, nl, sp, -1), sp)
+ var lines []string
+ max := 0
+ for _, v := range words {
+ max = len(v)
+ if max > lim {
+ lim = max
+ }
+ }
+ for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
+ lines = append(lines, strings.Join(line, sp))
+ }
+ return lines, lim
+}
+
+// WrapWords is the low-level line-breaking algorithm, useful if you need more
+// control over the details of the text wrapping process. For most uses,
+// WrapString will be sufficient and more convenient.
+//
+// WrapWords splits a list of words into lines with minimal "raggedness",
+// treating each rune as one unit, accounting for spc units between adjacent
+// words on each line, and attempting to limit lines to lim units. Raggedness
+// is the total error over all lines, where error is the square of the
+// difference of the length of the line and lim. Too-long lines (which only
+// happen when a single word is longer than lim units) have pen penalty units
+// added to the error.
+func WrapWords(words []string, spc, lim, pen int) [][]string {
+ n := len(words)
+
+ length := make([][]int, n)
+ for i := 0; i < n; i++ {
+ length[i] = make([]int, n)
+ length[i][i] = utf8.RuneCountInString(words[i])
+ for j := i + 1; j < n; j++ {
+ length[i][j] = length[i][j-1] + spc + utf8.RuneCountInString(words[j])
+ }
+ }
+ nbrk := make([]int, n)
+ cost := make([]int, n)
+ for i := range cost {
+ cost[i] = math.MaxInt32
+ }
+ for i := n - 1; i >= 0; i-- {
+ if length[i][n-1] <= lim {
+ cost[i] = 0
+ nbrk[i] = n
+ } else {
+ for j := i + 1; j < n; j++ {
+ d := lim - length[i][j-1]
+ c := d*d + cost[j]
+ if length[i][j-1] > lim {
+ c += pen // too-long lines get a worse penalty
+ }
+ if c < cost[i] {
+ cost[i] = c
+ nbrk[i] = j
+ }
+ }
+ }
+ }
+ var lines [][]string
+ i := 0
+ for i < n {
+ lines = append(lines, words[i:nbrk[i]])
+ i = nbrk[i]
+ }
+ return lines
+}
+
+// getLines decomposes a multiline string into a slice of strings.
+func getLines(s string) []string {
+ var lines []string
+
+ for _, line := range strings.Split(s, nl) {
+ lines = append(lines, line)
+ }
+ return lines
+}
diff --git a/vendor/golang.org/x/crypto/curve25519/const_amd64.h b/vendor/golang.org/x/crypto/curve25519/const_amd64.h
new file mode 100644
index 000000000..80ad2220f
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/const_amd64.h
@@ -0,0 +1,8 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+#define REDMASK51 0x0007FFFFFFFFFFFF
diff --git a/vendor/golang.org/x/crypto/curve25519/const_amd64.s b/vendor/golang.org/x/crypto/curve25519/const_amd64.s
new file mode 100644
index 000000000..0ad539885
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/const_amd64.s
@@ -0,0 +1,20 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// These constants cannot be encoded in non-MOVQ immediates.
+// We access them directly from memory instead.
+
+DATA ·_121666_213(SB)/8, $996687872
+GLOBL ·_121666_213(SB), 8, $8
+
+DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA
+GLOBL ·_2P0(SB), 8, $8
+
+DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE
+GLOBL ·_2P1234(SB), 8, $8
diff --git a/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s b/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
new file mode 100644
index 000000000..45484d1b5
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
@@ -0,0 +1,88 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func cswap(inout *[5]uint64, v uint64)
+TEXT ·cswap(SB),7,$0
+ MOVQ inout+0(FP),DI
+ MOVQ v+8(FP),SI
+
+ CMPQ SI,$1
+ MOVQ 0(DI),SI
+ MOVQ 80(DI),DX
+ MOVQ 8(DI),CX
+ MOVQ 88(DI),R8
+ MOVQ SI,R9
+ CMOVQEQ DX,SI
+ CMOVQEQ R9,DX
+ MOVQ CX,R9
+ CMOVQEQ R8,CX
+ CMOVQEQ R9,R8
+ MOVQ SI,0(DI)
+ MOVQ DX,80(DI)
+ MOVQ CX,8(DI)
+ MOVQ R8,88(DI)
+ MOVQ 16(DI),SI
+ MOVQ 96(DI),DX
+ MOVQ 24(DI),CX
+ MOVQ 104(DI),R8
+ MOVQ SI,R9
+ CMOVQEQ DX,SI
+ CMOVQEQ R9,DX
+ MOVQ CX,R9
+ CMOVQEQ R8,CX
+ CMOVQEQ R9,R8
+ MOVQ SI,16(DI)
+ MOVQ DX,96(DI)
+ MOVQ CX,24(DI)
+ MOVQ R8,104(DI)
+ MOVQ 32(DI),SI
+ MOVQ 112(DI),DX
+ MOVQ 40(DI),CX
+ MOVQ 120(DI),R8
+ MOVQ SI,R9
+ CMOVQEQ DX,SI
+ CMOVQEQ R9,DX
+ MOVQ CX,R9
+ CMOVQEQ R8,CX
+ CMOVQEQ R9,R8
+ MOVQ SI,32(DI)
+ MOVQ DX,112(DI)
+ MOVQ CX,40(DI)
+ MOVQ R8,120(DI)
+ MOVQ 48(DI),SI
+ MOVQ 128(DI),DX
+ MOVQ 56(DI),CX
+ MOVQ 136(DI),R8
+ MOVQ SI,R9
+ CMOVQEQ DX,SI
+ CMOVQEQ R9,DX
+ MOVQ CX,R9
+ CMOVQEQ R8,CX
+ CMOVQEQ R9,R8
+ MOVQ SI,48(DI)
+ MOVQ DX,128(DI)
+ MOVQ CX,56(DI)
+ MOVQ R8,136(DI)
+ MOVQ 64(DI),SI
+ MOVQ 144(DI),DX
+ MOVQ 72(DI),CX
+ MOVQ 152(DI),R8
+ MOVQ SI,R9
+ CMOVQEQ DX,SI
+ CMOVQEQ R9,DX
+ MOVQ CX,R9
+ CMOVQEQ R8,CX
+ CMOVQEQ R9,R8
+ MOVQ SI,64(DI)
+ MOVQ DX,144(DI)
+ MOVQ CX,72(DI)
+ MOVQ R8,152(DI)
+ MOVQ DI,AX
+ MOVQ SI,DX
+ RET
diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go
new file mode 100644
index 000000000..6918c47fc
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go
@@ -0,0 +1,841 @@
+// Copyright 2013 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.
+
+// We have a implementation in amd64 assembly so this code is only run on
+// non-amd64 platforms. The amd64 assembly does not support gccgo.
+// +build !amd64 gccgo appengine
+
+package curve25519
+
+// This code is a port of the public domain, "ref10" implementation of
+// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
+
+// fieldElement represents an element of the field GF(2^255 - 19). An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
+// context.
+type fieldElement [10]int32
+
+func feZero(fe *fieldElement) {
+ for i := range fe {
+ fe[i] = 0
+ }
+}
+
+func feOne(fe *fieldElement) {
+ feZero(fe)
+ fe[0] = 1
+}
+
+func feAdd(dst, a, b *fieldElement) {
+ for i := range dst {
+ dst[i] = a[i] + b[i]
+ }
+}
+
+func feSub(dst, a, b *fieldElement) {
+ for i := range dst {
+ dst[i] = a[i] - b[i]
+ }
+}
+
+func feCopy(dst, src *fieldElement) {
+ for i := range dst {
+ dst[i] = src[i]
+ }
+}
+
+// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func feCSwap(f, g *fieldElement, b int32) {
+ var x fieldElement
+ b = -b
+ for i := range x {
+ x[i] = b & (f[i] ^ g[i])
+ }
+
+ for i := range f {
+ f[i] ^= x[i]
+ }
+ for i := range g {
+ g[i] ^= x[i]
+ }
+}
+
+// load3 reads a 24-bit, little-endian value from in.
+func load3(in []byte) int64 {
+ var r int64
+ r = int64(in[0])
+ r |= int64(in[1]) << 8
+ r |= int64(in[2]) << 16
+ return r
+}
+
+// load4 reads a 32-bit, little-endian value from in.
+func load4(in []byte) int64 {
+ var r int64
+ r = int64(in[0])
+ r |= int64(in[1]) << 8
+ r |= int64(in[2]) << 16
+ r |= int64(in[3]) << 24
+ return r
+}
+
+func feFromBytes(dst *fieldElement, src *[32]byte) {
+ h0 := load4(src[:])
+ h1 := load3(src[4:]) << 6
+ h2 := load3(src[7:]) << 5
+ h3 := load3(src[10:]) << 3
+ h4 := load3(src[13:]) << 2
+ h5 := load4(src[16:])
+ h6 := load3(src[20:]) << 7
+ h7 := load3(src[23:]) << 5
+ h8 := load3(src[26:]) << 4
+ h9 := load3(src[29:]) << 2
+
+ var carry [10]int64
+ carry[9] = (h9 + 1<<24) >> 25
+ h0 += carry[9] * 19
+ h9 -= carry[9] << 25
+ carry[1] = (h1 + 1<<24) >> 25
+ h2 += carry[1]
+ h1 -= carry[1] << 25
+ carry[3] = (h3 + 1<<24) >> 25
+ h4 += carry[3]
+ h3 -= carry[3] << 25
+ carry[5] = (h5 + 1<<24) >> 25
+ h6 += carry[5]
+ h5 -= carry[5] << 25
+ carry[7] = (h7 + 1<<24) >> 25
+ h8 += carry[7]
+ h7 -= carry[7] << 25
+
+ carry[0] = (h0 + 1<<25) >> 26
+ h1 += carry[0]
+ h0 -= carry[0] << 26
+ carry[2] = (h2 + 1<<25) >> 26
+ h3 += carry[2]
+ h2 -= carry[2] << 26
+ carry[4] = (h4 + 1<<25) >> 26
+ h5 += carry[4]
+ h4 -= carry[4] << 26
+ carry[6] = (h6 + 1<<25) >> 26
+ h7 += carry[6]
+ h6 -= carry[6] << 26
+ carry[8] = (h8 + 1<<25) >> 26
+ h9 += carry[8]
+ h8 -= carry[8] << 26
+
+ dst[0] = int32(h0)
+ dst[1] = int32(h1)
+ dst[2] = int32(h2)
+ dst[3] = int32(h3)
+ dst[4] = int32(h4)
+ dst[5] = int32(h5)
+ dst[6] = int32(h6)
+ dst[7] = int32(h7)
+ dst[8] = int32(h8)
+ dst[9] = int32(h9)
+}
+
+// feToBytes marshals h to s.
+// Preconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+// Then 0<y<1.
+//
+// Write r=h-pq.
+// Have 0<=r<=p-1=2^255-20.
+// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+// Write x=r+19(2^-255)r+y.
+// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func feToBytes(s *[32]byte, h *fieldElement) {
+ var carry [10]int32
+
+ q := (19*h[9] + (1 << 24)) >> 25
+ q = (h[0] + q) >> 26
+ q = (h[1] + q) >> 25
+ q = (h[2] + q) >> 26
+ q = (h[3] + q) >> 25
+ q = (h[4] + q) >> 26
+ q = (h[5] + q) >> 25
+ q = (h[6] + q) >> 26
+ q = (h[7] + q) >> 25
+ q = (h[8] + q) >> 26
+ q = (h[9] + q) >> 25
+
+ // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+ h[0] += 19 * q
+ // Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+ carry[0] = h[0] >> 26
+ h[1] += carry[0]
+ h[0] -= carry[0] << 26
+ carry[1] = h[1] >> 25
+ h[2] += carry[1]
+ h[1] -= carry[1] << 25
+ carry[2] = h[2] >> 26
+ h[3] += carry[2]
+ h[2] -= carry[2] << 26
+ carry[3] = h[3] >> 25
+ h[4] += carry[3]
+ h[3] -= carry[3] << 25
+ carry[4] = h[4] >> 26
+ h[5] += carry[4]
+ h[4] -= carry[4] << 26
+ carry[5] = h[5] >> 25
+ h[6] += carry[5]
+ h[5] -= carry[5] << 25
+ carry[6] = h[6] >> 26
+ h[7] += carry[6]
+ h[6] -= carry[6] << 26
+ carry[7] = h[7] >> 25
+ h[8] += carry[7]
+ h[7] -= carry[7] << 25
+ carry[8] = h[8] >> 26
+ h[9] += carry[8]
+ h[8] -= carry[8] << 26
+ carry[9] = h[9] >> 25
+ h[9] -= carry[9] << 25
+ // h10 = carry9
+
+ // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+ // Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+ // evidently 2^255 h10-2^255 q = 0.
+ // Goal: Output h[0]+...+2^230 h[9].
+
+ s[0] = byte(h[0] >> 0)
+ s[1] = byte(h[0] >> 8)
+ s[2] = byte(h[0] >> 16)
+ s[3] = byte((h[0] >> 24) | (h[1] << 2))
+ s[4] = byte(h[1] >> 6)
+ s[5] = byte(h[1] >> 14)
+ s[6] = byte((h[1] >> 22) | (h[2] << 3))
+ s[7] = byte(h[2] >> 5)
+ s[8] = byte(h[2] >> 13)
+ s[9] = byte((h[2] >> 21) | (h[3] << 5))
+ s[10] = byte(h[3] >> 3)
+ s[11] = byte(h[3] >> 11)
+ s[12] = byte((h[3] >> 19) | (h[4] << 6))
+ s[13] = byte(h[4] >> 2)
+ s[14] = byte(h[4] >> 10)
+ s[15] = byte(h[4] >> 18)
+ s[16] = byte(h[5] >> 0)
+ s[17] = byte(h[5] >> 8)
+ s[18] = byte(h[5] >> 16)
+ s[19] = byte((h[5] >> 24) | (h[6] << 1))
+ s[20] = byte(h[6] >> 7)
+ s[21] = byte(h[6] >> 15)
+ s[22] = byte((h[6] >> 23) | (h[7] << 3))
+ s[23] = byte(h[7] >> 5)
+ s[24] = byte(h[7] >> 13)
+ s[25] = byte((h[7] >> 21) | (h[8] << 4))
+ s[26] = byte(h[8] >> 4)
+ s[27] = byte(h[8] >> 12)
+ s[28] = byte((h[8] >> 20) | (h[9] << 6))
+ s[29] = byte(h[9] >> 2)
+ s[30] = byte(h[9] >> 10)
+ s[31] = byte(h[9] >> 18)
+}
+
+// feMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs can squeeze carries into int32.
+func feMul(h, f, g *fieldElement) {
+ f0 := f[0]
+ f1 := f[1]
+ f2 := f[2]
+ f3 := f[3]
+ f4 := f[4]
+ f5 := f[5]
+ f6 := f[6]
+ f7 := f[7]
+ f8 := f[8]
+ f9 := f[9]
+ g0 := g[0]
+ g1 := g[1]
+ g2 := g[2]
+ g3 := g[3]
+ g4 := g[4]
+ g5 := g[5]
+ g6 := g[6]
+ g7 := g[7]
+ g8 := g[8]
+ g9 := g[9]
+ g1_19 := 19 * g1 // 1.4*2^29
+ g2_19 := 19 * g2 // 1.4*2^30; still ok
+ g3_19 := 19 * g3
+ g4_19 := 19 * g4
+ g5_19 := 19 * g5
+ g6_19 := 19 * g6
+ g7_19 := 19 * g7
+ g8_19 := 19 * g8
+ g9_19 := 19 * g9
+ f1_2 := 2 * f1
+ f3_2 := 2 * f3
+ f5_2 := 2 * f5
+ f7_2 := 2 * f7
+ f9_2 := 2 * f9
+ f0g0 := int64(f0) * int64(g0)
+ f0g1 := int64(f0) * int64(g1)
+ f0g2 := int64(f0) * int64(g2)
+ f0g3 := int64(f0) * int64(g3)
+ f0g4 := int64(f0) * int64(g4)
+ f0g5 := int64(f0) * int64(g5)
+ f0g6 := int64(f0) * int64(g6)
+ f0g7 := int64(f0) * int64(g7)
+ f0g8 := int64(f0) * int64(g8)
+ f0g9 := int64(f0) * int64(g9)
+ f1g0 := int64(f1) * int64(g0)
+ f1g1_2 := int64(f1_2) * int64(g1)
+ f1g2 := int64(f1) * int64(g2)
+ f1g3_2 := int64(f1_2) * int64(g3)
+ f1g4 := int64(f1) * int64(g4)
+ f1g5_2 := int64(f1_2) * int64(g5)
+ f1g6 := int64(f1) * int64(g6)
+ f1g7_2 := int64(f1_2) * int64(g7)
+ f1g8 := int64(f1) * int64(g8)
+ f1g9_38 := int64(f1_2) * int64(g9_19)
+ f2g0 := int64(f2) * int64(g0)
+ f2g1 := int64(f2) * int64(g1)
+ f2g2 := int64(f2) * int64(g2)
+ f2g3 := int64(f2) * int64(g3)
+ f2g4 := int64(f2) * int64(g4)
+ f2g5 := int64(f2) * int64(g5)
+ f2g6 := int64(f2) * int64(g6)
+ f2g7 := int64(f2) * int64(g7)
+ f2g8_19 := int64(f2) * int64(g8_19)
+ f2g9_19 := int64(f2) * int64(g9_19)
+ f3g0 := int64(f3) * int64(g0)
+ f3g1_2 := int64(f3_2) * int64(g1)
+ f3g2 := int64(f3) * int64(g2)
+ f3g3_2 := int64(f3_2) * int64(g3)
+ f3g4 := int64(f3) * int64(g4)
+ f3g5_2 := int64(f3_2) * int64(g5)
+ f3g6 := int64(f3) * int64(g6)
+ f3g7_38 := int64(f3_2) * int64(g7_19)
+ f3g8_19 := int64(f3) * int64(g8_19)
+ f3g9_38 := int64(f3_2) * int64(g9_19)
+ f4g0 := int64(f4) * int64(g0)
+ f4g1 := int64(f4) * int64(g1)
+ f4g2 := int64(f4) * int64(g2)
+ f4g3 := int64(f4) * int64(g3)
+ f4g4 := int64(f4) * int64(g4)
+ f4g5 := int64(f4) * int64(g5)
+ f4g6_19 := int64(f4) * int64(g6_19)
+ f4g7_19 := int64(f4) * int64(g7_19)
+ f4g8_19 := int64(f4) * int64(g8_19)
+ f4g9_19 := int64(f4) * int64(g9_19)
+ f5g0 := int64(f5) * int64(g0)
+ f5g1_2 := int64(f5_2) * int64(g1)
+ f5g2 := int64(f5) * int64(g2)
+ f5g3_2 := int64(f5_2) * int64(g3)
+ f5g4 := int64(f5) * int64(g4)
+ f5g5_38 := int64(f5_2) * int64(g5_19)
+ f5g6_19 := int64(f5) * int64(g6_19)
+ f5g7_38 := int64(f5_2) * int64(g7_19)
+ f5g8_19 := int64(f5) * int64(g8_19)
+ f5g9_38 := int64(f5_2) * int64(g9_19)
+ f6g0 := int64(f6) * int64(g0)
+ f6g1 := int64(f6) * int64(g1)
+ f6g2 := int64(f6) * int64(g2)
+ f6g3 := int64(f6) * int64(g3)
+ f6g4_19 := int64(f6) * int64(g4_19)
+ f6g5_19 := int64(f6) * int64(g5_19)
+ f6g6_19 := int64(f6) * int64(g6_19)
+ f6g7_19 := int64(f6) * int64(g7_19)
+ f6g8_19 := int64(f6) * int64(g8_19)
+ f6g9_19 := int64(f6) * int64(g9_19)
+ f7g0 := int64(f7) * int64(g0)
+ f7g1_2 := int64(f7_2) * int64(g1)
+ f7g2 := int64(f7) * int64(g2)
+ f7g3_38 := int64(f7_2) * int64(g3_19)
+ f7g4_19 := int64(f7) * int64(g4_19)
+ f7g5_38 := int64(f7_2) * int64(g5_19)
+ f7g6_19 := int64(f7) * int64(g6_19)
+ f7g7_38 := int64(f7_2) * int64(g7_19)
+ f7g8_19 := int64(f7) * int64(g8_19)
+ f7g9_38 := int64(f7_2) * int64(g9_19)
+ f8g0 := int64(f8) * int64(g0)
+ f8g1 := int64(f8) * int64(g1)
+ f8g2_19 := int64(f8) * int64(g2_19)
+ f8g3_19 := int64(f8) * int64(g3_19)
+ f8g4_19 := int64(f8) * int64(g4_19)
+ f8g5_19 := int64(f8) * int64(g5_19)
+ f8g6_19 := int64(f8) * int64(g6_19)
+ f8g7_19 := int64(f8) * int64(g7_19)
+ f8g8_19 := int64(f8) * int64(g8_19)
+ f8g9_19 := int64(f8) * int64(g9_19)
+ f9g0 := int64(f9) * int64(g0)
+ f9g1_38 := int64(f9_2) * int64(g1_19)
+ f9g2_19 := int64(f9) * int64(g2_19)
+ f9g3_38 := int64(f9_2) * int64(g3_19)
+ f9g4_19 := int64(f9) * int64(g4_19)
+ f9g5_38 := int64(f9_2) * int64(g5_19)
+ f9g6_19 := int64(f9) * int64(g6_19)
+ f9g7_38 := int64(f9_2) * int64(g7_19)
+ f9g8_19 := int64(f9) * int64(g8_19)
+ f9g9_38 := int64(f9_2) * int64(g9_19)
+ h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
+ h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
+ h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
+ h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
+ h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
+ h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
+ h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
+ h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
+ h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
+ h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
+ var carry [10]int64
+
+ // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+ // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+ // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+ // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+
+ carry[0] = (h0 + (1 << 25)) >> 26
+ h1 += carry[0]
+ h0 -= carry[0] << 26
+ carry[4] = (h4 + (1 << 25)) >> 26
+ h5 += carry[4]
+ h4 -= carry[4] << 26
+ // |h0| <= 2^25
+ // |h4| <= 2^25
+ // |h1| <= 1.51*2^58
+ // |h5| <= 1.51*2^58
+
+ carry[1] = (h1 + (1 << 24)) >> 25
+ h2 += carry[1]
+ h1 -= carry[1] << 25
+ carry[5] = (h5 + (1 << 24)) >> 25
+ h6 += carry[5]
+ h5 -= carry[5] << 25
+ // |h1| <= 2^24; from now on fits into int32
+ // |h5| <= 2^24; from now on fits into int32
+ // |h2| <= 1.21*2^59
+ // |h6| <= 1.21*2^59
+
+ carry[2] = (h2 + (1 << 25)) >> 26
+ h3 += carry[2]
+ h2 -= carry[2] << 26
+ carry[6] = (h6 + (1 << 25)) >> 26
+ h7 += carry[6]
+ h6 -= carry[6] << 26
+ // |h2| <= 2^25; from now on fits into int32 unchanged
+ // |h6| <= 2^25; from now on fits into int32 unchanged
+ // |h3| <= 1.51*2^58
+ // |h7| <= 1.51*2^58
+
+ carry[3] = (h3 + (1 << 24)) >> 25
+ h4 += carry[3]
+ h3 -= carry[3] << 25
+ carry[7] = (h7 + (1 << 24)) >> 25
+ h8 += carry[7]
+ h7 -= carry[7] << 25
+ // |h3| <= 2^24; from now on fits into int32 unchanged
+ // |h7| <= 2^24; from now on fits into int32 unchanged
+ // |h4| <= 1.52*2^33
+ // |h8| <= 1.52*2^33
+
+ carry[4] = (h4 + (1 << 25)) >> 26
+ h5 += carry[4]
+ h4 -= carry[4] << 26
+ carry[8] = (h8 + (1 << 25)) >> 26
+ h9 += carry[8]
+ h8 -= carry[8] << 26
+ // |h4| <= 2^25; from now on fits into int32 unchanged
+ // |h8| <= 2^25; from now on fits into int32 unchanged
+ // |h5| <= 1.01*2^24
+ // |h9| <= 1.51*2^58
+
+ carry[9] = (h9 + (1 << 24)) >> 25
+ h0 += carry[9] * 19
+ h9 -= carry[9] << 25
+ // |h9| <= 2^24; from now on fits into int32 unchanged
+ // |h0| <= 1.8*2^37
+
+ carry[0] = (h0 + (1 << 25)) >> 26
+ h1 += carry[0]
+ h0 -= carry[0] << 26
+ // |h0| <= 2^25; from now on fits into int32 unchanged
+ // |h1| <= 1.01*2^24
+
+ h[0] = int32(h0)
+ h[1] = int32(h1)
+ h[2] = int32(h2)
+ h[3] = int32(h3)
+ h[4] = int32(h4)
+ h[5] = int32(h5)
+ h[6] = int32(h6)
+ h[7] = int32(h7)
+ h[8] = int32(h8)
+ h[9] = int32(h9)
+}
+
+// feSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func feSquare(h, f *fieldElement) {
+ f0 := f[0]
+ f1 := f[1]
+ f2 := f[2]
+ f3 := f[3]
+ f4 := f[4]
+ f5 := f[5]
+ f6 := f[6]
+ f7 := f[7]
+ f8 := f[8]
+ f9 := f[9]
+ f0_2 := 2 * f0
+ f1_2 := 2 * f1
+ f2_2 := 2 * f2
+ f3_2 := 2 * f3
+ f4_2 := 2 * f4
+ f5_2 := 2 * f5
+ f6_2 := 2 * f6
+ f7_2 := 2 * f7
+ f5_38 := 38 * f5 // 1.31*2^30
+ f6_19 := 19 * f6 // 1.31*2^30
+ f7_38 := 38 * f7 // 1.31*2^30
+ f8_19 := 19 * f8 // 1.31*2^30
+ f9_38 := 38 * f9 // 1.31*2^30
+ f0f0 := int64(f0) * int64(f0)
+ f0f1_2 := int64(f0_2) * int64(f1)
+ f0f2_2 := int64(f0_2) * int64(f2)
+ f0f3_2 := int64(f0_2) * int64(f3)
+ f0f4_2 := int64(f0_2) * int64(f4)
+ f0f5_2 := int64(f0_2) * int64(f5)
+ f0f6_2 := int64(f0_2) * int64(f6)
+ f0f7_2 := int64(f0_2) * int64(f7)
+ f0f8_2 := int64(f0_2) * int64(f8)
+ f0f9_2 := int64(f0_2) * int64(f9)
+ f1f1_2 := int64(f1_2) * int64(f1)
+ f1f2_2 := int64(f1_2) * int64(f2)
+ f1f3_4 := int64(f1_2) * int64(f3_2)
+ f1f4_2 := int64(f1_2) * int64(f4)
+ f1f5_4 := int64(f1_2) * int64(f5_2)
+ f1f6_2 := int64(f1_2) * int64(f6)
+ f1f7_4 := int64(f1_2) * int64(f7_2)
+ f1f8_2 := int64(f1_2) * int64(f8)
+ f1f9_76 := int64(f1_2) * int64(f9_38)
+ f2f2 := int64(f2) * int64(f2)
+ f2f3_2 := int64(f2_2) * int64(f3)
+ f2f4_2 := int64(f2_2) * int64(f4)
+ f2f5_2 := int64(f2_2) * int64(f5)
+ f2f6_2 := int64(f2_2) * int64(f6)
+ f2f7_2 := int64(f2_2) * int64(f7)
+ f2f8_38 := int64(f2_2) * int64(f8_19)
+ f2f9_38 := int64(f2) * int64(f9_38)
+ f3f3_2 := int64(f3_2) * int64(f3)
+ f3f4_2 := int64(f3_2) * int64(f4)
+ f3f5_4 := int64(f3_2) * int64(f5_2)
+ f3f6_2 := int64(f3_2) * int64(f6)
+ f3f7_76 := int64(f3_2) * int64(f7_38)
+ f3f8_38 := int64(f3_2) * int64(f8_19)
+ f3f9_76 := int64(f3_2) * int64(f9_38)
+ f4f4 := int64(f4) * int64(f4)
+ f4f5_2 := int64(f4_2) * int64(f5)
+ f4f6_38 := int64(f4_2) * int64(f6_19)
+ f4f7_38 := int64(f4) * int64(f7_38)
+ f4f8_38 := int64(f4_2) * int64(f8_19)
+ f4f9_38 := int64(f4) * int64(f9_38)
+ f5f5_38 := int64(f5) * int64(f5_38)
+ f5f6_38 := int64(f5_2) * int64(f6_19)
+ f5f7_76 := int64(f5_2) * int64(f7_38)
+ f5f8_38 := int64(f5_2) * int64(f8_19)
+ f5f9_76 := int64(f5_2) * int64(f9_38)
+ f6f6_19 := int64(f6) * int64(f6_19)
+ f6f7_38 := int64(f6) * int64(f7_38)
+ f6f8_38 := int64(f6_2) * int64(f8_19)
+ f6f9_38 := int64(f6) * int64(f9_38)
+ f7f7_38 := int64(f7) * int64(f7_38)
+ f7f8_38 := int64(f7_2) * int64(f8_19)
+ f7f9_76 := int64(f7_2) * int64(f9_38)
+ f8f8_19 := int64(f8) * int64(f8_19)
+ f8f9_38 := int64(f8) * int64(f9_38)
+ f9f9_38 := int64(f9) * int64(f9_38)
+ h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
+ h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
+ h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
+ h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
+ h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
+ h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
+ h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
+ h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
+ h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
+ h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
+ var carry [10]int64
+
+ carry[0] = (h0 + (1 << 25)) >> 26
+ h1 += carry[0]
+ h0 -= carry[0] << 26
+ carry[4] = (h4 + (1 << 25)) >> 26
+ h5 += carry[4]
+ h4 -= carry[4] << 26
+
+ carry[1] = (h1 + (1 << 24)) >> 25
+ h2 += carry[1]
+ h1 -= carry[1] << 25
+ carry[5] = (h5 + (1 << 24)) >> 25
+ h6 += carry[5]
+ h5 -= carry[5] << 25
+
+ carry[2] = (h2 + (1 << 25)) >> 26
+ h3 += carry[2]
+ h2 -= carry[2] << 26
+ carry[6] = (h6 + (1 << 25)) >> 26
+ h7 += carry[6]
+ h6 -= carry[6] << 26
+
+ carry[3] = (h3 + (1 << 24)) >> 25
+ h4 += carry[3]
+ h3 -= carry[3] << 25
+ carry[7] = (h7 + (1 << 24)) >> 25
+ h8 += carry[7]
+ h7 -= carry[7] << 25
+
+ carry[4] = (h4 + (1 << 25)) >> 26
+ h5 += carry[4]
+ h4 -= carry[4] << 26
+ carry[8] = (h8 + (1 << 25)) >> 26
+ h9 += carry[8]
+ h8 -= carry[8] << 26
+
+ carry[9] = (h9 + (1 << 24)) >> 25
+ h0 += carry[9] * 19
+ h9 -= carry[9] << 25
+
+ carry[0] = (h0 + (1 << 25)) >> 26
+ h1 += carry[0]
+ h0 -= carry[0] << 26
+
+ h[0] = int32(h0)
+ h[1] = int32(h1)
+ h[2] = int32(h2)
+ h[3] = int32(h3)
+ h[4] = int32(h4)
+ h[5] = int32(h5)
+ h[6] = int32(h6)
+ h[7] = int32(h7)
+ h[8] = int32(h8)
+ h[9] = int32(h9)
+}
+
+// feMul121666 calculates h = f * 121666. Can overlap h with f.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func feMul121666(h, f *fieldElement) {
+ h0 := int64(f[0]) * 121666
+ h1 := int64(f[1]) * 121666
+ h2 := int64(f[2]) * 121666
+ h3 := int64(f[3]) * 121666
+ h4 := int64(f[4]) * 121666
+ h5 := int64(f[5]) * 121666
+ h6 := int64(f[6]) * 121666
+ h7 := int64(f[7]) * 121666
+ h8 := int64(f[8]) * 121666
+ h9 := int64(f[9]) * 121666
+ var carry [10]int64
+
+ carry[9] = (h9 + (1 << 24)) >> 25
+ h0 += carry[9] * 19
+ h9 -= carry[9] << 25
+ carry[1] = (h1 + (1 << 24)) >> 25
+ h2 += carry[1]
+ h1 -= carry[1] << 25
+ carry[3] = (h3 + (1 << 24)) >> 25
+ h4 += carry[3]
+ h3 -= carry[3] << 25
+ carry[5] = (h5 + (1 << 24)) >> 25
+ h6 += carry[5]
+ h5 -= carry[5] << 25
+ carry[7] = (h7 + (1 << 24)) >> 25
+ h8 += carry[7]
+ h7 -= carry[7] << 25
+
+ carry[0] = (h0 + (1 << 25)) >> 26
+ h1 += carry[0]
+ h0 -= carry[0] << 26
+ carry[2] = (h2 + (1 << 25)) >> 26
+ h3 += carry[2]
+ h2 -= carry[2] << 26
+ carry[4] = (h4 + (1 << 25)) >> 26
+ h5 += carry[4]
+ h4 -= carry[4] << 26
+ carry[6] = (h6 + (1 << 25)) >> 26
+ h7 += carry[6]
+ h6 -= carry[6] << 26
+ carry[8] = (h8 + (1 << 25)) >> 26
+ h9 += carry[8]
+ h8 -= carry[8] << 26
+
+ h[0] = int32(h0)
+ h[1] = int32(h1)
+ h[2] = int32(h2)
+ h[3] = int32(h3)
+ h[4] = int32(h4)
+ h[5] = int32(h5)
+ h[6] = int32(h6)
+ h[7] = int32(h7)
+ h[8] = int32(h8)
+ h[9] = int32(h9)
+}
+
+// feInvert sets out = z^-1.
+func feInvert(out, z *fieldElement) {
+ var t0, t1, t2, t3 fieldElement
+ var i int
+
+ feSquare(&t0, z)
+ for i = 1; i < 1; i++ {
+ feSquare(&t0, &t0)
+ }
+ feSquare(&t1, &t0)
+ for i = 1; i < 2; i++ {
+ feSquare(&t1, &t1)
+ }
+ feMul(&t1, z, &t1)
+ feMul(&t0, &t0, &t1)
+ feSquare(&t2, &t0)
+ for i = 1; i < 1; i++ {
+ feSquare(&t2, &t2)
+ }
+ feMul(&t1, &t1, &t2)
+ feSquare(&t2, &t1)
+ for i = 1; i < 5; i++ {
+ feSquare(&t2, &t2)
+ }
+ feMul(&t1, &t2, &t1)
+ feSquare(&t2, &t1)
+ for i = 1; i < 10; i++ {
+ feSquare(&t2, &t2)
+ }
+ feMul(&t2, &t2, &t1)
+ feSquare(&t3, &t2)
+ for i = 1; i < 20; i++ {
+ feSquare(&t3, &t3)
+ }
+ feMul(&t2, &t3, &t2)
+ feSquare(&t2, &t2)
+ for i = 1; i < 10; i++ {
+ feSquare(&t2, &t2)
+ }
+ feMul(&t1, &t2, &t1)
+ feSquare(&t2, &t1)
+ for i = 1; i < 50; i++ {
+ feSquare(&t2, &t2)
+ }
+ feMul(&t2, &t2, &t1)
+ feSquare(&t3, &t2)
+ for i = 1; i < 100; i++ {
+ feSquare(&t3, &t3)
+ }
+ feMul(&t2, &t3, &t2)
+ feSquare(&t2, &t2)
+ for i = 1; i < 50; i++ {
+ feSquare(&t2, &t2)
+ }
+ feMul(&t1, &t2, &t1)
+ feSquare(&t1, &t1)
+ for i = 1; i < 5; i++ {
+ feSquare(&t1, &t1)
+ }
+ feMul(out, &t1, &t0)
+}
+
+func scalarMult(out, in, base *[32]byte) {
+ var e [32]byte
+
+ copy(e[:], in[:])
+ e[0] &= 248
+ e[31] &= 127
+ e[31] |= 64
+
+ var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
+ feFromBytes(&x1, base)
+ feOne(&x2)
+ feCopy(&x3, &x1)
+ feOne(&z3)
+
+ swap := int32(0)
+ for pos := 254; pos >= 0; pos-- {
+ b := e[pos/8] >> uint(pos&7)
+ b &= 1
+ swap ^= int32(b)
+ feCSwap(&x2, &x3, swap)
+ feCSwap(&z2, &z3, swap)
+ swap = int32(b)
+
+ feSub(&tmp0, &x3, &z3)
+ feSub(&tmp1, &x2, &z2)
+ feAdd(&x2, &x2, &z2)
+ feAdd(&z2, &x3, &z3)
+ feMul(&z3, &tmp0, &x2)
+ feMul(&z2, &z2, &tmp1)
+ feSquare(&tmp0, &tmp1)
+ feSquare(&tmp1, &x2)
+ feAdd(&x3, &z3, &z2)
+ feSub(&z2, &z3, &z2)
+ feMul(&x2, &tmp1, &tmp0)
+ feSub(&tmp1, &tmp1, &tmp0)
+ feSquare(&z2, &z2)
+ feMul121666(&z3, &tmp1)
+ feSquare(&x3, &x3)
+ feAdd(&tmp0, &tmp0, &z3)
+ feMul(&z3, &x1, &z2)
+ feMul(&z2, &tmp1, &tmp0)
+ }
+
+ feCSwap(&x2, &x3, swap)
+ feCSwap(&z2, &z3, swap)
+
+ feInvert(&z2, &z2)
+ feMul(&x2, &x2, &z2)
+ feToBytes(out, &x2)
+}
diff --git a/vendor/golang.org/x/crypto/curve25519/doc.go b/vendor/golang.org/x/crypto/curve25519/doc.go
new file mode 100644
index 000000000..ebeea3c2d
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/doc.go
@@ -0,0 +1,23 @@
+// Copyright 2012 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 curve25519 provides an implementation of scalar multiplication on
+// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html
+package curve25519 // import "golang.org/x/crypto/curve25519"
+
+// basePoint is the x coordinate of the generator of the curve.
+var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+// ScalarMult sets dst to the product in*base where dst and base are the x
+// coordinates of group points and all values are in little-endian form.
+func ScalarMult(dst, in, base *[32]byte) {
+ scalarMult(dst, in, base)
+}
+
+// ScalarBaseMult sets dst to the product in*base where dst and base are the x
+// coordinates of group points, base is the standard generator and all values
+// are in little-endian form.
+func ScalarBaseMult(dst, in *[32]byte) {
+ ScalarMult(dst, in, &basePoint)
+}
diff --git a/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s b/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
new file mode 100644
index 000000000..536479bf6
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
@@ -0,0 +1,73 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+#include "const_amd64.h"
+
+// func freeze(inout *[5]uint64)
+TEXT ·freeze(SB),7,$0-8
+ MOVQ inout+0(FP), DI
+
+ MOVQ 0(DI),SI
+ MOVQ 8(DI),DX
+ MOVQ 16(DI),CX
+ MOVQ 24(DI),R8
+ MOVQ 32(DI),R9
+ MOVQ $REDMASK51,AX
+ MOVQ AX,R10
+ SUBQ $18,R10
+ MOVQ $3,R11
+REDUCELOOP:
+ MOVQ SI,R12
+ SHRQ $51,R12
+ ANDQ AX,SI
+ ADDQ R12,DX
+ MOVQ DX,R12
+ SHRQ $51,R12
+ ANDQ AX,DX
+ ADDQ R12,CX
+ MOVQ CX,R12
+ SHRQ $51,R12
+ ANDQ AX,CX
+ ADDQ R12,R8
+ MOVQ R8,R12
+ SHRQ $51,R12
+ ANDQ AX,R8
+ ADDQ R12,R9
+ MOVQ R9,R12
+ SHRQ $51,R12
+ ANDQ AX,R9
+ IMUL3Q $19,R12,R12
+ ADDQ R12,SI
+ SUBQ $1,R11
+ JA REDUCELOOP
+ MOVQ $1,R12
+ CMPQ R10,SI
+ CMOVQLT R11,R12
+ CMPQ AX,DX
+ CMOVQNE R11,R12
+ CMPQ AX,CX
+ CMOVQNE R11,R12
+ CMPQ AX,R8
+ CMOVQNE R11,R12
+ CMPQ AX,R9
+ CMOVQNE R11,R12
+ NEGQ R12
+ ANDQ R12,AX
+ ANDQ R12,R10
+ SUBQ R10,SI
+ SUBQ AX,DX
+ SUBQ AX,CX
+ SUBQ AX,R8
+ SUBQ AX,R9
+ MOVQ SI,0(DI)
+ MOVQ DX,8(DI)
+ MOVQ CX,16(DI)
+ MOVQ R8,24(DI)
+ MOVQ R9,32(DI)
+ RET
diff --git a/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s b/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
new file mode 100644
index 000000000..7074e5cd9
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
@@ -0,0 +1,1377 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+#include "const_amd64.h"
+
+// func ladderstep(inout *[5][5]uint64)
+TEXT ·ladderstep(SB),0,$296-8
+ MOVQ inout+0(FP),DI
+
+ MOVQ 40(DI),SI
+ MOVQ 48(DI),DX
+ MOVQ 56(DI),CX
+ MOVQ 64(DI),R8
+ MOVQ 72(DI),R9
+ MOVQ SI,AX
+ MOVQ DX,R10
+ MOVQ CX,R11
+ MOVQ R8,R12
+ MOVQ R9,R13
+ ADDQ ·_2P0(SB),AX
+ ADDQ ·_2P1234(SB),R10
+ ADDQ ·_2P1234(SB),R11
+ ADDQ ·_2P1234(SB),R12
+ ADDQ ·_2P1234(SB),R13
+ ADDQ 80(DI),SI
+ ADDQ 88(DI),DX
+ ADDQ 96(DI),CX
+ ADDQ 104(DI),R8
+ ADDQ 112(DI),R9
+ SUBQ 80(DI),AX
+ SUBQ 88(DI),R10
+ SUBQ 96(DI),R11
+ SUBQ 104(DI),R12
+ SUBQ 112(DI),R13
+ MOVQ SI,0(SP)
+ MOVQ DX,8(SP)
+ MOVQ CX,16(SP)
+ MOVQ R8,24(SP)
+ MOVQ R9,32(SP)
+ MOVQ AX,40(SP)
+ MOVQ R10,48(SP)
+ MOVQ R11,56(SP)
+ MOVQ R12,64(SP)
+ MOVQ R13,72(SP)
+ MOVQ 40(SP),AX
+ MULQ 40(SP)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 40(SP),AX
+ SHLQ $1,AX
+ MULQ 48(SP)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 40(SP),AX
+ SHLQ $1,AX
+ MULQ 56(SP)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 40(SP),AX
+ SHLQ $1,AX
+ MULQ 64(SP)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 40(SP),AX
+ SHLQ $1,AX
+ MULQ 72(SP)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 48(SP),AX
+ MULQ 48(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 48(SP),AX
+ SHLQ $1,AX
+ MULQ 56(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 48(SP),AX
+ SHLQ $1,AX
+ MULQ 64(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 48(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 72(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 56(SP),AX
+ MULQ 56(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 56(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 64(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 56(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 72(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 64(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 64(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 64(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 72(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 72(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 72(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ ANDQ DX,SI
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ADDQ R10,CX
+ ANDQ DX,R8
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ADDQ R12,CX
+ ANDQ DX,R9
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ADDQ R14,CX
+ ANDQ DX,AX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,80(SP)
+ MOVQ R8,88(SP)
+ MOVQ R9,96(SP)
+ MOVQ AX,104(SP)
+ MOVQ R10,112(SP)
+ MOVQ 0(SP),AX
+ MULQ 0(SP)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 0(SP),AX
+ SHLQ $1,AX
+ MULQ 8(SP)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 0(SP),AX
+ SHLQ $1,AX
+ MULQ 16(SP)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 0(SP),AX
+ SHLQ $1,AX
+ MULQ 24(SP)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 0(SP),AX
+ SHLQ $1,AX
+ MULQ 32(SP)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 8(SP),AX
+ MULQ 8(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 8(SP),AX
+ SHLQ $1,AX
+ MULQ 16(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 8(SP),AX
+ SHLQ $1,AX
+ MULQ 24(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 8(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 32(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 16(SP),AX
+ MULQ 16(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 16(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 24(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 16(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 32(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 24(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 24(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 24(SP),DX
+ IMUL3Q $38,DX,AX
+ MULQ 32(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 32(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ ANDQ DX,SI
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ADDQ R10,CX
+ ANDQ DX,R8
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ADDQ R12,CX
+ ANDQ DX,R9
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ADDQ R14,CX
+ ANDQ DX,AX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,120(SP)
+ MOVQ R8,128(SP)
+ MOVQ R9,136(SP)
+ MOVQ AX,144(SP)
+ MOVQ R10,152(SP)
+ MOVQ SI,SI
+ MOVQ R8,DX
+ MOVQ R9,CX
+ MOVQ AX,R8
+ MOVQ R10,R9
+ ADDQ ·_2P0(SB),SI
+ ADDQ ·_2P1234(SB),DX
+ ADDQ ·_2P1234(SB),CX
+ ADDQ ·_2P1234(SB),R8
+ ADDQ ·_2P1234(SB),R9
+ SUBQ 80(SP),SI
+ SUBQ 88(SP),DX
+ SUBQ 96(SP),CX
+ SUBQ 104(SP),R8
+ SUBQ 112(SP),R9
+ MOVQ SI,160(SP)
+ MOVQ DX,168(SP)
+ MOVQ CX,176(SP)
+ MOVQ R8,184(SP)
+ MOVQ R9,192(SP)
+ MOVQ 120(DI),SI
+ MOVQ 128(DI),DX
+ MOVQ 136(DI),CX
+ MOVQ 144(DI),R8
+ MOVQ 152(DI),R9
+ MOVQ SI,AX
+ MOVQ DX,R10
+ MOVQ CX,R11
+ MOVQ R8,R12
+ MOVQ R9,R13
+ ADDQ ·_2P0(SB),AX
+ ADDQ ·_2P1234(SB),R10
+ ADDQ ·_2P1234(SB),R11
+ ADDQ ·_2P1234(SB),R12
+ ADDQ ·_2P1234(SB),R13
+ ADDQ 160(DI),SI
+ ADDQ 168(DI),DX
+ ADDQ 176(DI),CX
+ ADDQ 184(DI),R8
+ ADDQ 192(DI),R9
+ SUBQ 160(DI),AX
+ SUBQ 168(DI),R10
+ SUBQ 176(DI),R11
+ SUBQ 184(DI),R12
+ SUBQ 192(DI),R13
+ MOVQ SI,200(SP)
+ MOVQ DX,208(SP)
+ MOVQ CX,216(SP)
+ MOVQ R8,224(SP)
+ MOVQ R9,232(SP)
+ MOVQ AX,240(SP)
+ MOVQ R10,248(SP)
+ MOVQ R11,256(SP)
+ MOVQ R12,264(SP)
+ MOVQ R13,272(SP)
+ MOVQ 224(SP),SI
+ IMUL3Q $19,SI,AX
+ MOVQ AX,280(SP)
+ MULQ 56(SP)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 232(SP),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,288(SP)
+ MULQ 48(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 200(SP),AX
+ MULQ 40(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 200(SP),AX
+ MULQ 48(SP)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 200(SP),AX
+ MULQ 56(SP)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 200(SP),AX
+ MULQ 64(SP)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 200(SP),AX
+ MULQ 72(SP)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 208(SP),AX
+ MULQ 40(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 208(SP),AX
+ MULQ 48(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 208(SP),AX
+ MULQ 56(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 208(SP),AX
+ MULQ 64(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 208(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 72(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 216(SP),AX
+ MULQ 40(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 216(SP),AX
+ MULQ 48(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 216(SP),AX
+ MULQ 56(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 216(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 64(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 216(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 72(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 224(SP),AX
+ MULQ 40(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 224(SP),AX
+ MULQ 48(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 280(SP),AX
+ MULQ 64(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 280(SP),AX
+ MULQ 72(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 232(SP),AX
+ MULQ 40(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 288(SP),AX
+ MULQ 56(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 288(SP),AX
+ MULQ 64(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 288(SP),AX
+ MULQ 72(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ANDQ DX,SI
+ ADDQ R10,CX
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ANDQ DX,R8
+ ADDQ R12,CX
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ANDQ DX,R9
+ ADDQ R14,CX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ ANDQ DX,AX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,40(SP)
+ MOVQ R8,48(SP)
+ MOVQ R9,56(SP)
+ MOVQ AX,64(SP)
+ MOVQ R10,72(SP)
+ MOVQ 264(SP),SI
+ IMUL3Q $19,SI,AX
+ MOVQ AX,200(SP)
+ MULQ 16(SP)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 272(SP),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,208(SP)
+ MULQ 8(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 240(SP),AX
+ MULQ 0(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 240(SP),AX
+ MULQ 8(SP)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 240(SP),AX
+ MULQ 16(SP)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 240(SP),AX
+ MULQ 24(SP)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 240(SP),AX
+ MULQ 32(SP)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 248(SP),AX
+ MULQ 0(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 248(SP),AX
+ MULQ 8(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 248(SP),AX
+ MULQ 16(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 248(SP),AX
+ MULQ 24(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 248(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 256(SP),AX
+ MULQ 0(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 256(SP),AX
+ MULQ 8(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 256(SP),AX
+ MULQ 16(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 256(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 24(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 256(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 264(SP),AX
+ MULQ 0(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 264(SP),AX
+ MULQ 8(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 200(SP),AX
+ MULQ 24(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 200(SP),AX
+ MULQ 32(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 272(SP),AX
+ MULQ 0(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 208(SP),AX
+ MULQ 16(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 208(SP),AX
+ MULQ 24(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 208(SP),AX
+ MULQ 32(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ANDQ DX,SI
+ ADDQ R10,CX
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ANDQ DX,R8
+ ADDQ R12,CX
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ANDQ DX,R9
+ ADDQ R14,CX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ ANDQ DX,AX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,DX
+ MOVQ R8,CX
+ MOVQ R9,R11
+ MOVQ AX,R12
+ MOVQ R10,R13
+ ADDQ ·_2P0(SB),DX
+ ADDQ ·_2P1234(SB),CX
+ ADDQ ·_2P1234(SB),R11
+ ADDQ ·_2P1234(SB),R12
+ ADDQ ·_2P1234(SB),R13
+ ADDQ 40(SP),SI
+ ADDQ 48(SP),R8
+ ADDQ 56(SP),R9
+ ADDQ 64(SP),AX
+ ADDQ 72(SP),R10
+ SUBQ 40(SP),DX
+ SUBQ 48(SP),CX
+ SUBQ 56(SP),R11
+ SUBQ 64(SP),R12
+ SUBQ 72(SP),R13
+ MOVQ SI,120(DI)
+ MOVQ R8,128(DI)
+ MOVQ R9,136(DI)
+ MOVQ AX,144(DI)
+ MOVQ R10,152(DI)
+ MOVQ DX,160(DI)
+ MOVQ CX,168(DI)
+ MOVQ R11,176(DI)
+ MOVQ R12,184(DI)
+ MOVQ R13,192(DI)
+ MOVQ 120(DI),AX
+ MULQ 120(DI)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 120(DI),AX
+ SHLQ $1,AX
+ MULQ 128(DI)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 120(DI),AX
+ SHLQ $1,AX
+ MULQ 136(DI)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 120(DI),AX
+ SHLQ $1,AX
+ MULQ 144(DI)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 120(DI),AX
+ SHLQ $1,AX
+ MULQ 152(DI)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 128(DI),AX
+ MULQ 128(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 128(DI),AX
+ SHLQ $1,AX
+ MULQ 136(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 128(DI),AX
+ SHLQ $1,AX
+ MULQ 144(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 128(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 152(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 136(DI),AX
+ MULQ 136(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 136(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 144(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 136(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 152(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 144(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 144(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 144(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 152(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 152(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 152(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ ANDQ DX,SI
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ADDQ R10,CX
+ ANDQ DX,R8
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ADDQ R12,CX
+ ANDQ DX,R9
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ADDQ R14,CX
+ ANDQ DX,AX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,120(DI)
+ MOVQ R8,128(DI)
+ MOVQ R9,136(DI)
+ MOVQ AX,144(DI)
+ MOVQ R10,152(DI)
+ MOVQ 160(DI),AX
+ MULQ 160(DI)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 160(DI),AX
+ SHLQ $1,AX
+ MULQ 168(DI)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 160(DI),AX
+ SHLQ $1,AX
+ MULQ 176(DI)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 160(DI),AX
+ SHLQ $1,AX
+ MULQ 184(DI)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 160(DI),AX
+ SHLQ $1,AX
+ MULQ 192(DI)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 168(DI),AX
+ MULQ 168(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 168(DI),AX
+ SHLQ $1,AX
+ MULQ 176(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 168(DI),AX
+ SHLQ $1,AX
+ MULQ 184(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 168(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 192(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 176(DI),AX
+ MULQ 176(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 176(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 184(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 176(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 192(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 184(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 184(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 184(DI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 192(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 192(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 192(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ ANDQ DX,SI
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ADDQ R10,CX
+ ANDQ DX,R8
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ADDQ R12,CX
+ ANDQ DX,R9
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ADDQ R14,CX
+ ANDQ DX,AX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,160(DI)
+ MOVQ R8,168(DI)
+ MOVQ R9,176(DI)
+ MOVQ AX,184(DI)
+ MOVQ R10,192(DI)
+ MOVQ 184(DI),SI
+ IMUL3Q $19,SI,AX
+ MOVQ AX,0(SP)
+ MULQ 16(DI)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 192(DI),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,8(SP)
+ MULQ 8(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 160(DI),AX
+ MULQ 0(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 160(DI),AX
+ MULQ 8(DI)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 160(DI),AX
+ MULQ 16(DI)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 160(DI),AX
+ MULQ 24(DI)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 160(DI),AX
+ MULQ 32(DI)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 168(DI),AX
+ MULQ 0(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 168(DI),AX
+ MULQ 8(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 168(DI),AX
+ MULQ 16(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 168(DI),AX
+ MULQ 24(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 168(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 176(DI),AX
+ MULQ 0(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 176(DI),AX
+ MULQ 8(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 176(DI),AX
+ MULQ 16(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 176(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 24(DI)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 176(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 184(DI),AX
+ MULQ 0(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 184(DI),AX
+ MULQ 8(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 0(SP),AX
+ MULQ 24(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 0(SP),AX
+ MULQ 32(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 192(DI),AX
+ MULQ 0(DI)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 8(SP),AX
+ MULQ 16(DI)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 8(SP),AX
+ MULQ 24(DI)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 8(SP),AX
+ MULQ 32(DI)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ANDQ DX,SI
+ ADDQ R10,CX
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ANDQ DX,R8
+ ADDQ R12,CX
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ANDQ DX,R9
+ ADDQ R14,CX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ ANDQ DX,AX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,160(DI)
+ MOVQ R8,168(DI)
+ MOVQ R9,176(DI)
+ MOVQ AX,184(DI)
+ MOVQ R10,192(DI)
+ MOVQ 144(SP),SI
+ IMUL3Q $19,SI,AX
+ MOVQ AX,0(SP)
+ MULQ 96(SP)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 152(SP),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,8(SP)
+ MULQ 88(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 120(SP),AX
+ MULQ 80(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 120(SP),AX
+ MULQ 88(SP)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 120(SP),AX
+ MULQ 96(SP)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 120(SP),AX
+ MULQ 104(SP)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 120(SP),AX
+ MULQ 112(SP)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 128(SP),AX
+ MULQ 80(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 128(SP),AX
+ MULQ 88(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 128(SP),AX
+ MULQ 96(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 128(SP),AX
+ MULQ 104(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 128(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 112(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 136(SP),AX
+ MULQ 80(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 136(SP),AX
+ MULQ 88(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 136(SP),AX
+ MULQ 96(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 136(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 104(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 136(SP),DX
+ IMUL3Q $19,DX,AX
+ MULQ 112(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 144(SP),AX
+ MULQ 80(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 144(SP),AX
+ MULQ 88(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 0(SP),AX
+ MULQ 104(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 0(SP),AX
+ MULQ 112(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 152(SP),AX
+ MULQ 80(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 8(SP),AX
+ MULQ 96(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 8(SP),AX
+ MULQ 104(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 8(SP),AX
+ MULQ 112(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ANDQ DX,SI
+ ADDQ R10,CX
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ANDQ DX,R8
+ ADDQ R12,CX
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ANDQ DX,R9
+ ADDQ R14,CX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ ANDQ DX,AX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,40(DI)
+ MOVQ R8,48(DI)
+ MOVQ R9,56(DI)
+ MOVQ AX,64(DI)
+ MOVQ R10,72(DI)
+ MOVQ 160(SP),AX
+ MULQ ·_121666_213(SB)
+ SHRQ $13,AX
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 168(SP),AX
+ MULQ ·_121666_213(SB)
+ SHRQ $13,AX
+ ADDQ AX,CX
+ MOVQ DX,R8
+ MOVQ 176(SP),AX
+ MULQ ·_121666_213(SB)
+ SHRQ $13,AX
+ ADDQ AX,R8
+ MOVQ DX,R9
+ MOVQ 184(SP),AX
+ MULQ ·_121666_213(SB)
+ SHRQ $13,AX
+ ADDQ AX,R9
+ MOVQ DX,R10
+ MOVQ 192(SP),AX
+ MULQ ·_121666_213(SB)
+ SHRQ $13,AX
+ ADDQ AX,R10
+ IMUL3Q $19,DX,DX
+ ADDQ DX,SI
+ ADDQ 80(SP),SI
+ ADDQ 88(SP),CX
+ ADDQ 96(SP),R8
+ ADDQ 104(SP),R9
+ ADDQ 112(SP),R10
+ MOVQ SI,80(DI)
+ MOVQ CX,88(DI)
+ MOVQ R8,96(DI)
+ MOVQ R9,104(DI)
+ MOVQ R10,112(DI)
+ MOVQ 104(DI),SI
+ IMUL3Q $19,SI,AX
+ MOVQ AX,0(SP)
+ MULQ 176(SP)
+ MOVQ AX,SI
+ MOVQ DX,CX
+ MOVQ 112(DI),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,8(SP)
+ MULQ 168(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 80(DI),AX
+ MULQ 160(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 80(DI),AX
+ MULQ 168(SP)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 80(DI),AX
+ MULQ 176(SP)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 80(DI),AX
+ MULQ 184(SP)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 80(DI),AX
+ MULQ 192(SP)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 88(DI),AX
+ MULQ 160(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 88(DI),AX
+ MULQ 168(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 88(DI),AX
+ MULQ 176(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 88(DI),AX
+ MULQ 184(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 88(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 192(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 96(DI),AX
+ MULQ 160(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 96(DI),AX
+ MULQ 168(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 96(DI),AX
+ MULQ 176(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 96(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 184(SP)
+ ADDQ AX,SI
+ ADCQ DX,CX
+ MOVQ 96(DI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 192(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 104(DI),AX
+ MULQ 160(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 104(DI),AX
+ MULQ 168(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 0(SP),AX
+ MULQ 184(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 0(SP),AX
+ MULQ 192(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 112(DI),AX
+ MULQ 160(SP)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 8(SP),AX
+ MULQ 176(SP)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 8(SP),AX
+ MULQ 184(SP)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 8(SP),AX
+ MULQ 192(SP)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ $REDMASK51,DX
+ SHLQ $13,CX:SI
+ ANDQ DX,SI
+ SHLQ $13,R9:R8
+ ANDQ DX,R8
+ ADDQ CX,R8
+ SHLQ $13,R11:R10
+ ANDQ DX,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ DX,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ DX,R14
+ ADDQ R13,R14
+ IMUL3Q $19,R15,CX
+ ADDQ CX,SI
+ MOVQ SI,CX
+ SHRQ $51,CX
+ ADDQ R8,CX
+ MOVQ CX,R8
+ SHRQ $51,CX
+ ANDQ DX,SI
+ ADDQ R10,CX
+ MOVQ CX,R9
+ SHRQ $51,CX
+ ANDQ DX,R8
+ ADDQ R12,CX
+ MOVQ CX,AX
+ SHRQ $51,CX
+ ANDQ DX,R9
+ ADDQ R14,CX
+ MOVQ CX,R10
+ SHRQ $51,CX
+ ANDQ DX,AX
+ IMUL3Q $19,CX,CX
+ ADDQ CX,SI
+ ANDQ DX,R10
+ MOVQ SI,80(DI)
+ MOVQ R8,88(DI)
+ MOVQ R9,96(DI)
+ MOVQ AX,104(DI)
+ MOVQ R10,112(DI)
+ RET
diff --git a/vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go b/vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
new file mode 100644
index 000000000..5822bd533
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
@@ -0,0 +1,240 @@
+// Copyright 2012 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.
+
+// +build amd64,!gccgo,!appengine
+
+package curve25519
+
+// These functions are implemented in the .s files. The names of the functions
+// in the rest of the file are also taken from the SUPERCOP sources to help
+// people following along.
+
+//go:noescape
+
+func cswap(inout *[5]uint64, v uint64)
+
+//go:noescape
+
+func ladderstep(inout *[5][5]uint64)
+
+//go:noescape
+
+func freeze(inout *[5]uint64)
+
+//go:noescape
+
+func mul(dest, a, b *[5]uint64)
+
+//go:noescape
+
+func square(out, in *[5]uint64)
+
+// mladder uses a Montgomery ladder to calculate (xr/zr) *= s.
+func mladder(xr, zr *[5]uint64, s *[32]byte) {
+ var work [5][5]uint64
+
+ work[0] = *xr
+ setint(&work[1], 1)
+ setint(&work[2], 0)
+ work[3] = *xr
+ setint(&work[4], 1)
+
+ j := uint(6)
+ var prevbit byte
+
+ for i := 31; i >= 0; i-- {
+ for j < 8 {
+ bit := ((*s)[i] >> j) & 1
+ swap := bit ^ prevbit
+ prevbit = bit
+ cswap(&work[1], uint64(swap))
+ ladderstep(&work)
+ j--
+ }
+ j = 7
+ }
+
+ *xr = work[1]
+ *zr = work[2]
+}
+
+func scalarMult(out, in, base *[32]byte) {
+ var e [32]byte
+ copy(e[:], (*in)[:])
+ e[0] &= 248
+ e[31] &= 127
+ e[31] |= 64
+
+ var t, z [5]uint64
+ unpack(&t, base)
+ mladder(&t, &z, &e)
+ invert(&z, &z)
+ mul(&t, &t, &z)
+ pack(out, &t)
+}
+
+func setint(r *[5]uint64, v uint64) {
+ r[0] = v
+ r[1] = 0
+ r[2] = 0
+ r[3] = 0
+ r[4] = 0
+}
+
+// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian
+// order.
+func unpack(r *[5]uint64, x *[32]byte) {
+ r[0] = uint64(x[0]) |
+ uint64(x[1])<<8 |
+ uint64(x[2])<<16 |
+ uint64(x[3])<<24 |
+ uint64(x[4])<<32 |
+ uint64(x[5])<<40 |
+ uint64(x[6]&7)<<48
+
+ r[1] = uint64(x[6])>>3 |
+ uint64(x[7])<<5 |
+ uint64(x[8])<<13 |
+ uint64(x[9])<<21 |
+ uint64(x[10])<<29 |
+ uint64(x[11])<<37 |
+ uint64(x[12]&63)<<45
+
+ r[2] = uint64(x[12])>>6 |
+ uint64(x[13])<<2 |
+ uint64(x[14])<<10 |
+ uint64(x[15])<<18 |
+ uint64(x[16])<<26 |
+ uint64(x[17])<<34 |
+ uint64(x[18])<<42 |
+ uint64(x[19]&1)<<50
+
+ r[3] = uint64(x[19])>>1 |
+ uint64(x[20])<<7 |
+ uint64(x[21])<<15 |
+ uint64(x[22])<<23 |
+ uint64(x[23])<<31 |
+ uint64(x[24])<<39 |
+ uint64(x[25]&15)<<47
+
+ r[4] = uint64(x[25])>>4 |
+ uint64(x[26])<<4 |
+ uint64(x[27])<<12 |
+ uint64(x[28])<<20 |
+ uint64(x[29])<<28 |
+ uint64(x[30])<<36 |
+ uint64(x[31]&127)<<44
+}
+
+// pack sets out = x where out is the usual, little-endian form of the 5,
+// 51-bit limbs in x.
+func pack(out *[32]byte, x *[5]uint64) {
+ t := *x
+ freeze(&t)
+
+ out[0] = byte(t[0])
+ out[1] = byte(t[0] >> 8)
+ out[2] = byte(t[0] >> 16)
+ out[3] = byte(t[0] >> 24)
+ out[4] = byte(t[0] >> 32)
+ out[5] = byte(t[0] >> 40)
+ out[6] = byte(t[0] >> 48)
+
+ out[6] ^= byte(t[1]<<3) & 0xf8
+ out[7] = byte(t[1] >> 5)
+ out[8] = byte(t[1] >> 13)
+ out[9] = byte(t[1] >> 21)
+ out[10] = byte(t[1] >> 29)
+ out[11] = byte(t[1] >> 37)
+ out[12] = byte(t[1] >> 45)
+
+ out[12] ^= byte(t[2]<<6) & 0xc0
+ out[13] = byte(t[2] >> 2)
+ out[14] = byte(t[2] >> 10)
+ out[15] = byte(t[2] >> 18)
+ out[16] = byte(t[2] >> 26)
+ out[17] = byte(t[2] >> 34)
+ out[18] = byte(t[2] >> 42)
+ out[19] = byte(t[2] >> 50)
+
+ out[19] ^= byte(t[3]<<1) & 0xfe
+ out[20] = byte(t[3] >> 7)
+ out[21] = byte(t[3] >> 15)
+ out[22] = byte(t[3] >> 23)
+ out[23] = byte(t[3] >> 31)
+ out[24] = byte(t[3] >> 39)
+ out[25] = byte(t[3] >> 47)
+
+ out[25] ^= byte(t[4]<<4) & 0xf0
+ out[26] = byte(t[4] >> 4)
+ out[27] = byte(t[4] >> 12)
+ out[28] = byte(t[4] >> 20)
+ out[29] = byte(t[4] >> 28)
+ out[30] = byte(t[4] >> 36)
+ out[31] = byte(t[4] >> 44)
+}
+
+// invert calculates r = x^-1 mod p using Fermat's little theorem.
+func invert(r *[5]uint64, x *[5]uint64) {
+ var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64
+
+ square(&z2, x) /* 2 */
+ square(&t, &z2) /* 4 */
+ square(&t, &t) /* 8 */
+ mul(&z9, &t, x) /* 9 */
+ mul(&z11, &z9, &z2) /* 11 */
+ square(&t, &z11) /* 22 */
+ mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */
+
+ square(&t, &z2_5_0) /* 2^6 - 2^1 */
+ for i := 1; i < 5; i++ { /* 2^20 - 2^10 */
+ square(&t, &t)
+ }
+ mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */
+
+ square(&t, &z2_10_0) /* 2^11 - 2^1 */
+ for i := 1; i < 10; i++ { /* 2^20 - 2^10 */
+ square(&t, &t)
+ }
+ mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */
+
+ square(&t, &z2_20_0) /* 2^21 - 2^1 */
+ for i := 1; i < 20; i++ { /* 2^40 - 2^20 */
+ square(&t, &t)
+ }
+ mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */
+
+ square(&t, &t) /* 2^41 - 2^1 */
+ for i := 1; i < 10; i++ { /* 2^50 - 2^10 */
+ square(&t, &t)
+ }
+ mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */
+
+ square(&t, &z2_50_0) /* 2^51 - 2^1 */
+ for i := 1; i < 50; i++ { /* 2^100 - 2^50 */
+ square(&t, &t)
+ }
+ mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */
+
+ square(&t, &z2_100_0) /* 2^101 - 2^1 */
+ for i := 1; i < 100; i++ { /* 2^200 - 2^100 */
+ square(&t, &t)
+ }
+ mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */
+
+ square(&t, &t) /* 2^201 - 2^1 */
+ for i := 1; i < 50; i++ { /* 2^250 - 2^50 */
+ square(&t, &t)
+ }
+ mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */
+
+ square(&t, &t) /* 2^251 - 2^1 */
+ square(&t, &t) /* 2^252 - 2^2 */
+ square(&t, &t) /* 2^253 - 2^3 */
+
+ square(&t, &t) /* 2^254 - 2^4 */
+
+ square(&t, &t) /* 2^255 - 2^5 */
+ mul(r, &t, &z11) /* 2^255 - 21 */
+}
diff --git a/vendor/golang.org/x/crypto/curve25519/mul_amd64.s b/vendor/golang.org/x/crypto/curve25519/mul_amd64.s
new file mode 100644
index 000000000..b162e6515
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/mul_amd64.s
@@ -0,0 +1,169 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+#include "const_amd64.h"
+
+// func mul(dest, a, b *[5]uint64)
+TEXT ·mul(SB),0,$16-24
+ MOVQ dest+0(FP), DI
+ MOVQ a+8(FP), SI
+ MOVQ b+16(FP), DX
+
+ MOVQ DX,CX
+ MOVQ 24(SI),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,0(SP)
+ MULQ 16(CX)
+ MOVQ AX,R8
+ MOVQ DX,R9
+ MOVQ 32(SI),DX
+ IMUL3Q $19,DX,AX
+ MOVQ AX,8(SP)
+ MULQ 8(CX)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 0(SI),AX
+ MULQ 0(CX)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 0(SI),AX
+ MULQ 8(CX)
+ MOVQ AX,R10
+ MOVQ DX,R11
+ MOVQ 0(SI),AX
+ MULQ 16(CX)
+ MOVQ AX,R12
+ MOVQ DX,R13
+ MOVQ 0(SI),AX
+ MULQ 24(CX)
+ MOVQ AX,R14
+ MOVQ DX,R15
+ MOVQ 0(SI),AX
+ MULQ 32(CX)
+ MOVQ AX,BX
+ MOVQ DX,BP
+ MOVQ 8(SI),AX
+ MULQ 0(CX)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 8(SI),AX
+ MULQ 8(CX)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 8(SI),AX
+ MULQ 16(CX)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 8(SI),AX
+ MULQ 24(CX)
+ ADDQ AX,BX
+ ADCQ DX,BP
+ MOVQ 8(SI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(CX)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 16(SI),AX
+ MULQ 0(CX)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 16(SI),AX
+ MULQ 8(CX)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 16(SI),AX
+ MULQ 16(CX)
+ ADDQ AX,BX
+ ADCQ DX,BP
+ MOVQ 16(SI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 24(CX)
+ ADDQ AX,R8
+ ADCQ DX,R9
+ MOVQ 16(SI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(CX)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 24(SI),AX
+ MULQ 0(CX)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ 24(SI),AX
+ MULQ 8(CX)
+ ADDQ AX,BX
+ ADCQ DX,BP
+ MOVQ 0(SP),AX
+ MULQ 24(CX)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 0(SP),AX
+ MULQ 32(CX)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 32(SI),AX
+ MULQ 0(CX)
+ ADDQ AX,BX
+ ADCQ DX,BP
+ MOVQ 8(SP),AX
+ MULQ 16(CX)
+ ADDQ AX,R10
+ ADCQ DX,R11
+ MOVQ 8(SP),AX
+ MULQ 24(CX)
+ ADDQ AX,R12
+ ADCQ DX,R13
+ MOVQ 8(SP),AX
+ MULQ 32(CX)
+ ADDQ AX,R14
+ ADCQ DX,R15
+ MOVQ $REDMASK51,SI
+ SHLQ $13,R9:R8
+ ANDQ SI,R8
+ SHLQ $13,R11:R10
+ ANDQ SI,R10
+ ADDQ R9,R10
+ SHLQ $13,R13:R12
+ ANDQ SI,R12
+ ADDQ R11,R12
+ SHLQ $13,R15:R14
+ ANDQ SI,R14
+ ADDQ R13,R14
+ SHLQ $13,BP:BX
+ ANDQ SI,BX
+ ADDQ R15,BX
+ IMUL3Q $19,BP,DX
+ ADDQ DX,R8
+ MOVQ R8,DX
+ SHRQ $51,DX
+ ADDQ R10,DX
+ MOVQ DX,CX
+ SHRQ $51,DX
+ ANDQ SI,R8
+ ADDQ R12,DX
+ MOVQ DX,R9
+ SHRQ $51,DX
+ ANDQ SI,CX
+ ADDQ R14,DX
+ MOVQ DX,AX
+ SHRQ $51,DX
+ ANDQ SI,R9
+ ADDQ BX,DX
+ MOVQ DX,R10
+ SHRQ $51,DX
+ ANDQ SI,AX
+ IMUL3Q $19,DX,DX
+ ADDQ DX,R8
+ ANDQ SI,R10
+ MOVQ R8,0(DI)
+ MOVQ CX,8(DI)
+ MOVQ R9,16(DI)
+ MOVQ AX,24(DI)
+ MOVQ R10,32(DI)
+ RET
diff --git a/vendor/golang.org/x/crypto/curve25519/square_amd64.s b/vendor/golang.org/x/crypto/curve25519/square_amd64.s
new file mode 100644
index 000000000..4e864a83e
--- /dev/null
+++ b/vendor/golang.org/x/crypto/curve25519/square_amd64.s
@@ -0,0 +1,132 @@
+// Copyright 2012 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 code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+#include "const_amd64.h"
+
+// func square(out, in *[5]uint64)
+TEXT ·square(SB),7,$0-16
+ MOVQ out+0(FP), DI
+ MOVQ in+8(FP), SI
+
+ MOVQ 0(SI),AX
+ MULQ 0(SI)
+ MOVQ AX,CX
+ MOVQ DX,R8
+ MOVQ 0(SI),AX
+ SHLQ $1,AX
+ MULQ 8(SI)
+ MOVQ AX,R9
+ MOVQ DX,R10
+ MOVQ 0(SI),AX
+ SHLQ $1,AX
+ MULQ 16(SI)
+ MOVQ AX,R11
+ MOVQ DX,R12
+ MOVQ 0(SI),AX
+ SHLQ $1,AX
+ MULQ 24(SI)
+ MOVQ AX,R13
+ MOVQ DX,R14
+ MOVQ 0(SI),AX
+ SHLQ $1,AX
+ MULQ 32(SI)
+ MOVQ AX,R15
+ MOVQ DX,BX
+ MOVQ 8(SI),AX
+ MULQ 8(SI)
+ ADDQ AX,R11
+ ADCQ DX,R12
+ MOVQ 8(SI),AX
+ SHLQ $1,AX
+ MULQ 16(SI)
+ ADDQ AX,R13
+ ADCQ DX,R14
+ MOVQ 8(SI),AX
+ SHLQ $1,AX
+ MULQ 24(SI)
+ ADDQ AX,R15
+ ADCQ DX,BX
+ MOVQ 8(SI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 32(SI)
+ ADDQ AX,CX
+ ADCQ DX,R8
+ MOVQ 16(SI),AX
+ MULQ 16(SI)
+ ADDQ AX,R15
+ ADCQ DX,BX
+ MOVQ 16(SI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 24(SI)
+ ADDQ AX,CX
+ ADCQ DX,R8
+ MOVQ 16(SI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 32(SI)
+ ADDQ AX,R9
+ ADCQ DX,R10
+ MOVQ 24(SI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 24(SI)
+ ADDQ AX,R9
+ ADCQ DX,R10
+ MOVQ 24(SI),DX
+ IMUL3Q $38,DX,AX
+ MULQ 32(SI)
+ ADDQ AX,R11
+ ADCQ DX,R12
+ MOVQ 32(SI),DX
+ IMUL3Q $19,DX,AX
+ MULQ 32(SI)
+ ADDQ AX,R13
+ ADCQ DX,R14
+ MOVQ $REDMASK51,SI
+ SHLQ $13,R8:CX
+ ANDQ SI,CX
+ SHLQ $13,R10:R9
+ ANDQ SI,R9
+ ADDQ R8,R9
+ SHLQ $13,R12:R11
+ ANDQ SI,R11
+ ADDQ R10,R11
+ SHLQ $13,R14:R13
+ ANDQ SI,R13
+ ADDQ R12,R13
+ SHLQ $13,BX:R15
+ ANDQ SI,R15
+ ADDQ R14,R15
+ IMUL3Q $19,BX,DX
+ ADDQ DX,CX
+ MOVQ CX,DX
+ SHRQ $51,DX
+ ADDQ R9,DX
+ ANDQ SI,CX
+ MOVQ DX,R8
+ SHRQ $51,DX
+ ADDQ R11,DX
+ ANDQ SI,R8
+ MOVQ DX,R9
+ SHRQ $51,DX
+ ADDQ R13,DX
+ ANDQ SI,R9
+ MOVQ DX,AX
+ SHRQ $51,DX
+ ADDQ R15,DX
+ ANDQ SI,AX
+ MOVQ DX,R10
+ SHRQ $51,DX
+ IMUL3Q $19,DX,DX
+ ADDQ DX,CX
+ ANDQ SI,R10
+ MOVQ CX,0(DI)
+ MOVQ R8,8(DI)
+ MOVQ R9,16(DI)
+ MOVQ AX,24(DI)
+ MOVQ R10,32(DI)
+ RET
diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go
new file mode 100644
index 000000000..f1d95674a
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go
@@ -0,0 +1,181 @@
+// Copyright 2016 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 ed25519 implements the Ed25519 signature algorithm. See
+// http://ed25519.cr.yp.to/.
+//
+// These functions are also compatible with the “Ed25519” function defined in
+// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
+package ed25519
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+import (
+ "crypto"
+ cryptorand "crypto/rand"
+ "crypto/sha512"
+ "crypto/subtle"
+ "errors"
+ "io"
+ "strconv"
+
+ "golang.org/x/crypto/ed25519/internal/edwards25519"
+)
+
+const (
+ // PublicKeySize is the size, in bytes, of public keys as used in this package.
+ PublicKeySize = 32
+ // PrivateKeySize is the size, in bytes, of private keys as used in this package.
+ PrivateKeySize = 64
+ // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+ SignatureSize = 64
+)
+
+// PublicKey is the type of Ed25519 public keys.
+type PublicKey []byte
+
+// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
+type PrivateKey []byte
+
+// Public returns the PublicKey corresponding to priv.
+func (priv PrivateKey) Public() crypto.PublicKey {
+ publicKey := make([]byte, PublicKeySize)
+ copy(publicKey, priv[32:])
+ return PublicKey(publicKey)
+}
+
+// Sign signs the given message with priv.
+// Ed25519 performs two passes over messages to be signed and therefore cannot
+// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
+// indicate the message hasn't been hashed. This can be achieved by passing
+// crypto.Hash(0) as the value for opts.
+func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
+ if opts.HashFunc() != crypto.Hash(0) {
+ return nil, errors.New("ed25519: cannot sign hashed message")
+ }
+
+ return Sign(priv, message), nil
+}
+
+// GenerateKey generates a public/private key pair using entropy from rand.
+// If rand is nil, crypto/rand.Reader will be used.
+func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
+ if rand == nil {
+ rand = cryptorand.Reader
+ }
+
+ privateKey = make([]byte, PrivateKeySize)
+ publicKey = make([]byte, PublicKeySize)
+ _, err = io.ReadFull(rand, privateKey[:32])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ digest := sha512.Sum512(privateKey[:32])
+ digest[0] &= 248
+ digest[31] &= 127
+ digest[31] |= 64
+
+ var A edwards25519.ExtendedGroupElement
+ var hBytes [32]byte
+ copy(hBytes[:], digest[:])
+ edwards25519.GeScalarMultBase(&A, &hBytes)
+ var publicKeyBytes [32]byte
+ A.ToBytes(&publicKeyBytes)
+
+ copy(privateKey[32:], publicKeyBytes[:])
+ copy(publicKey, publicKeyBytes[:])
+
+ return publicKey, privateKey, nil
+}
+
+// Sign signs the message with privateKey and returns a signature. It will
+// panic if len(privateKey) is not PrivateKeySize.
+func Sign(privateKey PrivateKey, message []byte) []byte {
+ if l := len(privateKey); l != PrivateKeySize {
+ panic("ed25519: bad private key length: " + strconv.Itoa(l))
+ }
+
+ h := sha512.New()
+ h.Write(privateKey[:32])
+
+ var digest1, messageDigest, hramDigest [64]byte
+ var expandedSecretKey [32]byte
+ h.Sum(digest1[:0])
+ copy(expandedSecretKey[:], digest1[:])
+ expandedSecretKey[0] &= 248
+ expandedSecretKey[31] &= 63
+ expandedSecretKey[31] |= 64
+
+ h.Reset()
+ h.Write(digest1[32:])
+ h.Write(message)
+ h.Sum(messageDigest[:0])
+
+ var messageDigestReduced [32]byte
+ edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
+ var R edwards25519.ExtendedGroupElement
+ edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
+
+ var encodedR [32]byte
+ R.ToBytes(&encodedR)
+
+ h.Reset()
+ h.Write(encodedR[:])
+ h.Write(privateKey[32:])
+ h.Write(message)
+ h.Sum(hramDigest[:0])
+ var hramDigestReduced [32]byte
+ edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
+
+ var s [32]byte
+ edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
+
+ signature := make([]byte, SignatureSize)
+ copy(signature[:], encodedR[:])
+ copy(signature[32:], s[:])
+
+ return signature
+}
+
+// Verify reports whether sig is a valid signature of message by publicKey. It
+// will panic if len(publicKey) is not PublicKeySize.
+func Verify(publicKey PublicKey, message, sig []byte) bool {
+ if l := len(publicKey); l != PublicKeySize {
+ panic("ed25519: bad public key length: " + strconv.Itoa(l))
+ }
+
+ if len(sig) != SignatureSize || sig[63]&224 != 0 {
+ return false
+ }
+
+ var A edwards25519.ExtendedGroupElement
+ var publicKeyBytes [32]byte
+ copy(publicKeyBytes[:], publicKey)
+ if !A.FromBytes(&publicKeyBytes) {
+ return false
+ }
+ edwards25519.FeNeg(&A.X, &A.X)
+ edwards25519.FeNeg(&A.T, &A.T)
+
+ h := sha512.New()
+ h.Write(sig[:32])
+ h.Write(publicKey[:])
+ h.Write(message)
+ var digest [64]byte
+ h.Sum(digest[:0])
+
+ var hReduced [32]byte
+ edwards25519.ScReduce(&hReduced, &digest)
+
+ var R edwards25519.ProjectiveGroupElement
+ var b [32]byte
+ copy(b[:], sig[32:])
+ edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
+
+ var checkR [32]byte
+ R.ToBytes(&checkR)
+ return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
+}
diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
new file mode 100644
index 000000000..e39f086c1
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
@@ -0,0 +1,1422 @@
+// Copyright 2016 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 edwards25519
+
+// These values are from the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// d is a constant in the Edwards curve equation.
+var d = FieldElement{
+ -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116,
+}
+
+// d2 is 2*d.
+var d2 = FieldElement{
+ -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199,
+}
+
+// SqrtM1 is the square-root of -1 in the field.
+var SqrtM1 = FieldElement{
+ -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482,
+}
+
+// A is a constant in the Montgomery-form of curve25519.
+var A = FieldElement{
+ 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+// bi contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var bi = [8]PreComputedGroupElement{
+ {
+ FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+ FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+ FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+ },
+ {
+ FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+ FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+ FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+ },
+ {
+ FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+ FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+ FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+ },
+ {
+ FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+ FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+ FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+ },
+ {
+ FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877},
+ FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951},
+ FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784},
+ },
+ {
+ FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436},
+ FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918},
+ FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877},
+ },
+ {
+ FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800},
+ FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305},
+ FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300},
+ },
+ {
+ FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876},
+ FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619},
+ FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683},
+ },
+}
+
+// base contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var base = [32][8]PreComputedGroupElement{
+ {
+ {
+ FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+ FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+ FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+ },
+ {
+ FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303},
+ FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081},
+ FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697},
+ },
+ {
+ FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+ FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+ FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+ },
+ {
+ FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540},
+ FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397},
+ FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325},
+ },
+ {
+ FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+ FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+ FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+ },
+ {
+ FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777},
+ FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737},
+ FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652},
+ },
+ {
+ FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+ FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+ FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+ },
+ {
+ FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726},
+ FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955},
+ FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425},
+ },
+ },
+ {
+ {
+ FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171},
+ FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510},
+ FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660},
+ },
+ {
+ FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639},
+ FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963},
+ FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950},
+ },
+ {
+ FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568},
+ FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335},
+ FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628},
+ },
+ {
+ FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007},
+ FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772},
+ FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653},
+ },
+ {
+ FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567},
+ FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686},
+ FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372},
+ },
+ {
+ FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887},
+ FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954},
+ FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953},
+ },
+ {
+ FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833},
+ FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532},
+ FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876},
+ },
+ {
+ FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268},
+ FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214},
+ FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038},
+ },
+ },
+ {
+ {
+ FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800},
+ FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645},
+ FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664},
+ },
+ {
+ FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933},
+ FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182},
+ FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222},
+ },
+ {
+ FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991},
+ FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880},
+ FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092},
+ },
+ {
+ FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295},
+ FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788},
+ FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553},
+ },
+ {
+ FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026},
+ FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347},
+ FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033},
+ },
+ {
+ FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395},
+ FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278},
+ FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890},
+ },
+ {
+ FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995},
+ FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596},
+ FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891},
+ },
+ {
+ FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060},
+ FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608},
+ FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606},
+ },
+ },
+ {
+ {
+ FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389},
+ FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016},
+ FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341},
+ },
+ {
+ FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505},
+ FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553},
+ FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655},
+ },
+ {
+ FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220},
+ FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631},
+ FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099},
+ },
+ {
+ FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556},
+ FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749},
+ FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930},
+ },
+ {
+ FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391},
+ FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253},
+ FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066},
+ },
+ {
+ FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958},
+ FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082},
+ FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383},
+ },
+ {
+ FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521},
+ FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807},
+ FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948},
+ },
+ {
+ FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134},
+ FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455},
+ FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629},
+ },
+ },
+ {
+ {
+ FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069},
+ FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746},
+ FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919},
+ },
+ {
+ FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837},
+ FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906},
+ FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771},
+ },
+ {
+ FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817},
+ FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098},
+ FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409},
+ },
+ {
+ FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504},
+ FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727},
+ FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420},
+ },
+ {
+ FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003},
+ FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605},
+ FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384},
+ },
+ {
+ FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701},
+ FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683},
+ FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708},
+ },
+ {
+ FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563},
+ FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260},
+ FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387},
+ },
+ {
+ FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672},
+ FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686},
+ FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665},
+ },
+ },
+ {
+ {
+ FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182},
+ FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277},
+ FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628},
+ },
+ {
+ FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474},
+ FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539},
+ FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822},
+ },
+ {
+ FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970},
+ FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756},
+ FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508},
+ },
+ {
+ FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683},
+ FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655},
+ FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158},
+ },
+ {
+ FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125},
+ FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839},
+ FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664},
+ },
+ {
+ FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294},
+ FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899},
+ FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070},
+ },
+ {
+ FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294},
+ FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949},
+ FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083},
+ },
+ {
+ FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420},
+ FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940},
+ FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396},
+ },
+ },
+ {
+ {
+ FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567},
+ FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127},
+ FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294},
+ },
+ {
+ FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887},
+ FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964},
+ FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195},
+ },
+ {
+ FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244},
+ FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999},
+ FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762},
+ },
+ {
+ FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274},
+ FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236},
+ FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605},
+ },
+ {
+ FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761},
+ FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884},
+ FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482},
+ },
+ {
+ FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638},
+ FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490},
+ FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170},
+ },
+ {
+ FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736},
+ FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124},
+ FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392},
+ },
+ {
+ FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029},
+ FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048},
+ FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958},
+ },
+ },
+ {
+ {
+ FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593},
+ FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071},
+ FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692},
+ },
+ {
+ FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687},
+ FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441},
+ FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001},
+ },
+ {
+ FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460},
+ FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007},
+ FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762},
+ },
+ {
+ FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005},
+ FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674},
+ FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035},
+ },
+ {
+ FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590},
+ FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957},
+ FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812},
+ },
+ {
+ FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740},
+ FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122},
+ FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158},
+ },
+ {
+ FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885},
+ FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140},
+ FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857},
+ },
+ {
+ FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155},
+ FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260},
+ FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483},
+ },
+ },
+ {
+ {
+ FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677},
+ FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815},
+ FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751},
+ },
+ {
+ FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203},
+ FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208},
+ FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230},
+ },
+ {
+ FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850},
+ FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389},
+ FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968},
+ },
+ {
+ FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689},
+ FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880},
+ FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304},
+ },
+ {
+ FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632},
+ FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412},
+ FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566},
+ },
+ {
+ FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038},
+ FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232},
+ FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943},
+ },
+ {
+ FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856},
+ FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738},
+ FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971},
+ },
+ {
+ FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718},
+ FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697},
+ FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883},
+ },
+ },
+ {
+ {
+ FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912},
+ FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358},
+ FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849},
+ },
+ {
+ FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307},
+ FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977},
+ FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335},
+ },
+ {
+ FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644},
+ FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616},
+ FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735},
+ },
+ {
+ FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099},
+ FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341},
+ FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336},
+ },
+ {
+ FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646},
+ FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425},
+ FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388},
+ },
+ {
+ FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743},
+ FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822},
+ FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462},
+ },
+ {
+ FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985},
+ FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702},
+ FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797},
+ },
+ {
+ FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293},
+ FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100},
+ FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688},
+ },
+ },
+ {
+ {
+ FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186},
+ FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610},
+ FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707},
+ },
+ {
+ FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220},
+ FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025},
+ FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044},
+ },
+ {
+ FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992},
+ FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027},
+ FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197},
+ },
+ {
+ FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901},
+ FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952},
+ FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878},
+ },
+ {
+ FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390},
+ FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730},
+ FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730},
+ },
+ {
+ FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180},
+ FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272},
+ FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715},
+ },
+ {
+ FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970},
+ FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772},
+ FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865},
+ },
+ {
+ FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750},
+ FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373},
+ FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348},
+ },
+ },
+ {
+ {
+ FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144},
+ FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195},
+ FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086},
+ },
+ {
+ FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684},
+ FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518},
+ FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233},
+ },
+ {
+ FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793},
+ FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794},
+ FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435},
+ },
+ {
+ FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921},
+ FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518},
+ FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563},
+ },
+ {
+ FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278},
+ FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024},
+ FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030},
+ },
+ {
+ FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783},
+ FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717},
+ FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844},
+ },
+ {
+ FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333},
+ FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048},
+ FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760},
+ },
+ {
+ FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760},
+ FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757},
+ FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112},
+ },
+ },
+ {
+ {
+ FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468},
+ FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184},
+ FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289},
+ },
+ {
+ FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066},
+ FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882},
+ FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226},
+ },
+ {
+ FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101},
+ FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279},
+ FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811},
+ },
+ {
+ FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709},
+ FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714},
+ FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121},
+ },
+ {
+ FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464},
+ FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847},
+ FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400},
+ },
+ {
+ FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414},
+ FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158},
+ FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045},
+ },
+ {
+ FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415},
+ FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459},
+ FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079},
+ },
+ {
+ FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412},
+ FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743},
+ FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836},
+ },
+ },
+ {
+ {
+ FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022},
+ FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429},
+ FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065},
+ },
+ {
+ FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861},
+ FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000},
+ FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101},
+ },
+ {
+ FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815},
+ FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642},
+ FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966},
+ },
+ {
+ FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574},
+ FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742},
+ FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689},
+ },
+ {
+ FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020},
+ FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772},
+ FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982},
+ },
+ {
+ FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953},
+ FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218},
+ FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265},
+ },
+ {
+ FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073},
+ FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325},
+ FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798},
+ },
+ {
+ FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870},
+ FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863},
+ FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927},
+ },
+ },
+ {
+ {
+ FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267},
+ FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663},
+ FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862},
+ },
+ {
+ FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673},
+ FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943},
+ FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020},
+ },
+ {
+ FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238},
+ FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064},
+ FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795},
+ },
+ {
+ FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052},
+ FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904},
+ FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531},
+ },
+ {
+ FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979},
+ FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841},
+ FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431},
+ },
+ {
+ FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324},
+ FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940},
+ FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320},
+ },
+ {
+ FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184},
+ FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114},
+ FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878},
+ },
+ {
+ FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784},
+ FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091},
+ FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585},
+ },
+ },
+ {
+ {
+ FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208},
+ FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864},
+ FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661},
+ },
+ {
+ FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233},
+ FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212},
+ FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525},
+ },
+ {
+ FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068},
+ FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397},
+ FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988},
+ },
+ {
+ FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889},
+ FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038},
+ FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697},
+ },
+ {
+ FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875},
+ FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905},
+ FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656},
+ },
+ {
+ FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818},
+ FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714},
+ FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203},
+ },
+ {
+ FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931},
+ FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024},
+ FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084},
+ },
+ {
+ FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204},
+ FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817},
+ FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667},
+ },
+ },
+ {
+ {
+ FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504},
+ FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768},
+ FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255},
+ },
+ {
+ FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790},
+ FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438},
+ FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333},
+ },
+ {
+ FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971},
+ FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905},
+ FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409},
+ },
+ {
+ FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409},
+ FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499},
+ FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363},
+ },
+ {
+ FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664},
+ FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324},
+ FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940},
+ },
+ {
+ FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990},
+ FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914},
+ FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290},
+ },
+ {
+ FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257},
+ FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433},
+ FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236},
+ },
+ {
+ FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045},
+ FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093},
+ FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347},
+ },
+ },
+ {
+ {
+ FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191},
+ FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507},
+ FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906},
+ },
+ {
+ FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018},
+ FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109},
+ FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926},
+ },
+ {
+ FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528},
+ FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625},
+ FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286},
+ },
+ {
+ FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033},
+ FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866},
+ FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896},
+ },
+ {
+ FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075},
+ FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347},
+ FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437},
+ },
+ {
+ FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165},
+ FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588},
+ FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193},
+ },
+ {
+ FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017},
+ FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883},
+ FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961},
+ },
+ {
+ FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043},
+ FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663},
+ FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362},
+ },
+ },
+ {
+ {
+ FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860},
+ FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466},
+ FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063},
+ },
+ {
+ FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997},
+ FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295},
+ FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369},
+ },
+ {
+ FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385},
+ FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109},
+ FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906},
+ },
+ {
+ FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424},
+ FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185},
+ FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962},
+ },
+ {
+ FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325},
+ FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593},
+ FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404},
+ },
+ {
+ FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644},
+ FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801},
+ FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804},
+ },
+ {
+ FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884},
+ FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577},
+ FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849},
+ },
+ {
+ FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473},
+ FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644},
+ FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319},
+ },
+ },
+ {
+ {
+ FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599},
+ FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768},
+ FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084},
+ },
+ {
+ FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328},
+ FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369},
+ FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920},
+ },
+ {
+ FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815},
+ FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025},
+ FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397},
+ },
+ {
+ FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448},
+ FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981},
+ FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165},
+ },
+ {
+ FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501},
+ FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073},
+ FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861},
+ },
+ {
+ FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845},
+ FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211},
+ FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870},
+ },
+ {
+ FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096},
+ FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803},
+ FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168},
+ },
+ {
+ FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965},
+ FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505},
+ FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598},
+ },
+ },
+ {
+ {
+ FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782},
+ FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900},
+ FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479},
+ },
+ {
+ FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208},
+ FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232},
+ FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719},
+ },
+ {
+ FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271},
+ FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326},
+ FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132},
+ },
+ {
+ FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300},
+ FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570},
+ FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670},
+ },
+ {
+ FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994},
+ FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913},
+ FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317},
+ },
+ {
+ FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730},
+ FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096},
+ FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078},
+ },
+ {
+ FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411},
+ FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905},
+ FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654},
+ },
+ {
+ FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870},
+ FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498},
+ FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579},
+ },
+ },
+ {
+ {
+ FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677},
+ FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647},
+ FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743},
+ },
+ {
+ FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468},
+ FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375},
+ FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155},
+ },
+ {
+ FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725},
+ FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612},
+ FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943},
+ },
+ {
+ FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944},
+ FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928},
+ FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406},
+ },
+ {
+ FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139},
+ FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963},
+ FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693},
+ },
+ {
+ FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734},
+ FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680},
+ FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410},
+ },
+ {
+ FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931},
+ FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654},
+ FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710},
+ },
+ {
+ FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180},
+ FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684},
+ FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895},
+ },
+ },
+ {
+ {
+ FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501},
+ FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413},
+ FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880},
+ },
+ {
+ FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874},
+ FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962},
+ FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899},
+ },
+ {
+ FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152},
+ FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063},
+ FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080},
+ },
+ {
+ FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146},
+ FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183},
+ FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133},
+ },
+ {
+ FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421},
+ FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622},
+ FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197},
+ },
+ {
+ FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663},
+ FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753},
+ FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755},
+ },
+ {
+ FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862},
+ FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118},
+ FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171},
+ },
+ {
+ FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380},
+ FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824},
+ FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270},
+ },
+ },
+ {
+ {
+ FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438},
+ FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584},
+ FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562},
+ },
+ {
+ FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471},
+ FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610},
+ FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269},
+ },
+ {
+ FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650},
+ FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369},
+ FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461},
+ },
+ {
+ FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462},
+ FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793},
+ FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218},
+ },
+ {
+ FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226},
+ FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019},
+ FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037},
+ },
+ {
+ FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171},
+ FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132},
+ FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841},
+ },
+ {
+ FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181},
+ FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210},
+ FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040},
+ },
+ {
+ FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935},
+ FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105},
+ FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814},
+ },
+ },
+ {
+ {
+ FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852},
+ FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581},
+ FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646},
+ },
+ {
+ FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844},
+ FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025},
+ FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453},
+ },
+ {
+ FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068},
+ FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192},
+ FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921},
+ },
+ {
+ FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259},
+ FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426},
+ FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072},
+ },
+ {
+ FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305},
+ FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832},
+ FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943},
+ },
+ {
+ FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011},
+ FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447},
+ FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494},
+ },
+ {
+ FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245},
+ FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859},
+ FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915},
+ },
+ {
+ FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707},
+ FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848},
+ FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224},
+ },
+ },
+ {
+ {
+ FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391},
+ FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215},
+ FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101},
+ },
+ {
+ FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713},
+ FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849},
+ FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930},
+ },
+ {
+ FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940},
+ FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031},
+ FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404},
+ },
+ {
+ FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243},
+ FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116},
+ FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525},
+ },
+ {
+ FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509},
+ FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883},
+ FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865},
+ },
+ {
+ FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660},
+ FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273},
+ FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138},
+ },
+ {
+ FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560},
+ FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135},
+ FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941},
+ },
+ {
+ FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739},
+ FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756},
+ FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819},
+ },
+ },
+ {
+ {
+ FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347},
+ FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028},
+ FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075},
+ },
+ {
+ FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799},
+ FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609},
+ FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817},
+ },
+ {
+ FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989},
+ FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523},
+ FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278},
+ },
+ {
+ FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045},
+ FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377},
+ FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480},
+ },
+ {
+ FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016},
+ FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426},
+ FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525},
+ },
+ {
+ FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396},
+ FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080},
+ FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892},
+ },
+ {
+ FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275},
+ FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074},
+ FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140},
+ },
+ {
+ FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717},
+ FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101},
+ FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127},
+ },
+ },
+ {
+ {
+ FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632},
+ FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415},
+ FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160},
+ },
+ {
+ FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876},
+ FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625},
+ FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478},
+ },
+ {
+ FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164},
+ FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595},
+ FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248},
+ },
+ {
+ FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858},
+ FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193},
+ FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184},
+ },
+ {
+ FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942},
+ FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635},
+ FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948},
+ },
+ {
+ FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935},
+ FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415},
+ FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416},
+ },
+ {
+ FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018},
+ FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778},
+ FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659},
+ },
+ {
+ FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385},
+ FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503},
+ FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329},
+ },
+ },
+ {
+ {
+ FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056},
+ FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838},
+ FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948},
+ },
+ {
+ FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691},
+ FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118},
+ FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517},
+ },
+ {
+ FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269},
+ FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904},
+ FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589},
+ },
+ {
+ FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193},
+ FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910},
+ FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930},
+ },
+ {
+ FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667},
+ FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481},
+ FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876},
+ },
+ {
+ FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640},
+ FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278},
+ FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112},
+ },
+ {
+ FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272},
+ FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012},
+ FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221},
+ },
+ {
+ FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046},
+ FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345},
+ FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310},
+ },
+ },
+ {
+ {
+ FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937},
+ FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636},
+ FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008},
+ },
+ {
+ FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429},
+ FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576},
+ FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066},
+ },
+ {
+ FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490},
+ FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104},
+ FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053},
+ },
+ {
+ FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275},
+ FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511},
+ FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095},
+ },
+ {
+ FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439},
+ FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939},
+ FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424},
+ },
+ {
+ FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310},
+ FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608},
+ FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079},
+ },
+ {
+ FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101},
+ FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418},
+ FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576},
+ },
+ {
+ FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356},
+ FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996},
+ FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099},
+ },
+ },
+ {
+ {
+ FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728},
+ FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658},
+ FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242},
+ },
+ {
+ FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001},
+ FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766},
+ FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373},
+ },
+ {
+ FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458},
+ FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628},
+ FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657},
+ },
+ {
+ FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062},
+ FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616},
+ FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014},
+ },
+ {
+ FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383},
+ FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814},
+ FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718},
+ },
+ {
+ FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417},
+ FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222},
+ FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444},
+ },
+ {
+ FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597},
+ FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970},
+ FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799},
+ },
+ {
+ FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647},
+ FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511},
+ FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032},
+ },
+ },
+ {
+ {
+ FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834},
+ FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461},
+ FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062},
+ },
+ {
+ FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516},
+ FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547},
+ FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240},
+ },
+ {
+ FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038},
+ FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741},
+ FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103},
+ },
+ {
+ FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747},
+ FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323},
+ FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016},
+ },
+ {
+ FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373},
+ FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228},
+ FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141},
+ },
+ {
+ FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399},
+ FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831},
+ FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376},
+ },
+ {
+ FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313},
+ FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958},
+ FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577},
+ },
+ {
+ FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743},
+ FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684},
+ FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476},
+ },
+ },
+}
diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
new file mode 100644
index 000000000..5f8b99478
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
@@ -0,0 +1,1771 @@
+// Copyright 2016 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 edwards25519
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// FieldElement represents an element of the field GF(2^255 - 19). An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
+// context.
+type FieldElement [10]int32
+
+var zero FieldElement
+
+func FeZero(fe *FieldElement) {
+ copy(fe[:], zero[:])
+}
+
+func FeOne(fe *FieldElement) {
+ FeZero(fe)
+ fe[0] = 1
+}
+
+func FeAdd(dst, a, b *FieldElement) {
+ dst[0] = a[0] + b[0]
+ dst[1] = a[1] + b[1]
+ dst[2] = a[2] + b[2]
+ dst[3] = a[3] + b[3]
+ dst[4] = a[4] + b[4]
+ dst[5] = a[5] + b[5]
+ dst[6] = a[6] + b[6]
+ dst[7] = a[7] + b[7]
+ dst[8] = a[8] + b[8]
+ dst[9] = a[9] + b[9]
+}
+
+func FeSub(dst, a, b *FieldElement) {
+ dst[0] = a[0] - b[0]
+ dst[1] = a[1] - b[1]
+ dst[2] = a[2] - b[2]
+ dst[3] = a[3] - b[3]
+ dst[4] = a[4] - b[4]
+ dst[5] = a[5] - b[5]
+ dst[6] = a[6] - b[6]
+ dst[7] = a[7] - b[7]
+ dst[8] = a[8] - b[8]
+ dst[9] = a[9] - b[9]
+}
+
+func FeCopy(dst, src *FieldElement) {
+ copy(dst[:], src[:])
+}
+
+// Replace (f,g) with (g,g) if b == 1;
+// replace (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func FeCMove(f, g *FieldElement, b int32) {
+ b = -b
+ f[0] ^= b & (f[0] ^ g[0])
+ f[1] ^= b & (f[1] ^ g[1])
+ f[2] ^= b & (f[2] ^ g[2])
+ f[3] ^= b & (f[3] ^ g[3])
+ f[4] ^= b & (f[4] ^ g[4])
+ f[5] ^= b & (f[5] ^ g[5])
+ f[6] ^= b & (f[6] ^ g[6])
+ f[7] ^= b & (f[7] ^ g[7])
+ f[8] ^= b & (f[8] ^ g[8])
+ f[9] ^= b & (f[9] ^ g[9])
+}
+
+func load3(in []byte) int64 {
+ var r int64
+ r = int64(in[0])
+ r |= int64(in[1]) << 8
+ r |= int64(in[2]) << 16
+ return r
+}
+
+func load4(in []byte) int64 {
+ var r int64
+ r = int64(in[0])
+ r |= int64(in[1]) << 8
+ r |= int64(in[2]) << 16
+ r |= int64(in[3]) << 24
+ return r
+}
+
+func FeFromBytes(dst *FieldElement, src *[32]byte) {
+ h0 := load4(src[:])
+ h1 := load3(src[4:]) << 6
+ h2 := load3(src[7:]) << 5
+ h3 := load3(src[10:]) << 3
+ h4 := load3(src[13:]) << 2
+ h5 := load4(src[16:])
+ h6 := load3(src[20:]) << 7
+ h7 := load3(src[23:]) << 5
+ h8 := load3(src[26:]) << 4
+ h9 := (load3(src[29:]) & 8388607) << 2
+
+ FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeToBytes marshals h to s.
+// Preconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+// Then 0<y<1.
+//
+// Write r=h-pq.
+// Have 0<=r<=p-1=2^255-20.
+// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+// Write x=r+19(2^-255)r+y.
+// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func FeToBytes(s *[32]byte, h *FieldElement) {
+ var carry [10]int32
+
+ q := (19*h[9] + (1 << 24)) >> 25
+ q = (h[0] + q) >> 26
+ q = (h[1] + q) >> 25
+ q = (h[2] + q) >> 26
+ q = (h[3] + q) >> 25
+ q = (h[4] + q) >> 26
+ q = (h[5] + q) >> 25
+ q = (h[6] + q) >> 26
+ q = (h[7] + q) >> 25
+ q = (h[8] + q) >> 26
+ q = (h[9] + q) >> 25
+
+ // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+ h[0] += 19 * q
+ // Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+ carry[0] = h[0] >> 26
+ h[1] += carry[0]
+ h[0] -= carry[0] << 26
+ carry[1] = h[1] >> 25
+ h[2] += carry[1]
+ h[1] -= carry[1] << 25
+ carry[2] = h[2] >> 26
+ h[3] += carry[2]
+ h[2] -= carry[2] << 26
+ carry[3] = h[3] >> 25
+ h[4] += carry[3]
+ h[3] -= carry[3] << 25
+ carry[4] = h[4] >> 26
+ h[5] += carry[4]
+ h[4] -= carry[4] << 26
+ carry[5] = h[5] >> 25
+ h[6] += carry[5]
+ h[5] -= carry[5] << 25
+ carry[6] = h[6] >> 26
+ h[7] += carry[6]
+ h[6] -= carry[6] << 26
+ carry[7] = h[7] >> 25
+ h[8] += carry[7]
+ h[7] -= carry[7] << 25
+ carry[8] = h[8] >> 26
+ h[9] += carry[8]
+ h[8] -= carry[8] << 26
+ carry[9] = h[9] >> 25
+ h[9] -= carry[9] << 25
+ // h10 = carry9
+
+ // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+ // Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+ // evidently 2^255 h10-2^255 q = 0.
+ // Goal: Output h[0]+...+2^230 h[9].
+
+ s[0] = byte(h[0] >> 0)
+ s[1] = byte(h[0] >> 8)
+ s[2] = byte(h[0] >> 16)
+ s[3] = byte((h[0] >> 24) | (h[1] << 2))
+ s[4] = byte(h[1] >> 6)
+ s[5] = byte(h[1] >> 14)
+ s[6] = byte((h[1] >> 22) | (h[2] << 3))
+ s[7] = byte(h[2] >> 5)
+ s[8] = byte(h[2] >> 13)
+ s[9] = byte((h[2] >> 21) | (h[3] << 5))
+ s[10] = byte(h[3] >> 3)
+ s[11] = byte(h[3] >> 11)
+ s[12] = byte((h[3] >> 19) | (h[4] << 6))
+ s[13] = byte(h[4] >> 2)
+ s[14] = byte(h[4] >> 10)
+ s[15] = byte(h[4] >> 18)
+ s[16] = byte(h[5] >> 0)
+ s[17] = byte(h[5] >> 8)
+ s[18] = byte(h[5] >> 16)
+ s[19] = byte((h[5] >> 24) | (h[6] << 1))
+ s[20] = byte(h[6] >> 7)
+ s[21] = byte(h[6] >> 15)
+ s[22] = byte((h[6] >> 23) | (h[7] << 3))
+ s[23] = byte(h[7] >> 5)
+ s[24] = byte(h[7] >> 13)
+ s[25] = byte((h[7] >> 21) | (h[8] << 4))
+ s[26] = byte(h[8] >> 4)
+ s[27] = byte(h[8] >> 12)
+ s[28] = byte((h[8] >> 20) | (h[9] << 6))
+ s[29] = byte(h[9] >> 2)
+ s[30] = byte(h[9] >> 10)
+ s[31] = byte(h[9] >> 18)
+}
+
+func FeIsNegative(f *FieldElement) byte {
+ var s [32]byte
+ FeToBytes(&s, f)
+ return s[0] & 1
+}
+
+func FeIsNonZero(f *FieldElement) int32 {
+ var s [32]byte
+ FeToBytes(&s, f)
+ var x uint8
+ for _, b := range s {
+ x |= b
+ }
+ x |= x >> 4
+ x |= x >> 2
+ x |= x >> 1
+ return int32(x & 1)
+}
+
+// FeNeg sets h = -f
+//
+// Preconditions:
+// |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeNeg(h, f *FieldElement) {
+ h[0] = -f[0]
+ h[1] = -f[1]
+ h[2] = -f[2]
+ h[3] = -f[3]
+ h[4] = -f[4]
+ h[5] = -f[5]
+ h[6] = -f[6]
+ h[7] = -f[7]
+ h[8] = -f[8]
+ h[9] = -f[9]
+}
+
+func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+ var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64
+
+ /*
+ |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+ i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+ |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+ i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+ */
+
+ c0 = (h0 + (1 << 25)) >> 26
+ h1 += c0
+ h0 -= c0 << 26
+ c4 = (h4 + (1 << 25)) >> 26
+ h5 += c4
+ h4 -= c4 << 26
+ /* |h0| <= 2^25 */
+ /* |h4| <= 2^25 */
+ /* |h1| <= 1.51*2^58 */
+ /* |h5| <= 1.51*2^58 */
+
+ c1 = (h1 + (1 << 24)) >> 25
+ h2 += c1
+ h1 -= c1 << 25
+ c5 = (h5 + (1 << 24)) >> 25
+ h6 += c5
+ h5 -= c5 << 25
+ /* |h1| <= 2^24; from now on fits into int32 */
+ /* |h5| <= 2^24; from now on fits into int32 */
+ /* |h2| <= 1.21*2^59 */
+ /* |h6| <= 1.21*2^59 */
+
+ c2 = (h2 + (1 << 25)) >> 26
+ h3 += c2
+ h2 -= c2 << 26
+ c6 = (h6 + (1 << 25)) >> 26
+ h7 += c6
+ h6 -= c6 << 26
+ /* |h2| <= 2^25; from now on fits into int32 unchanged */
+ /* |h6| <= 2^25; from now on fits into int32 unchanged */
+ /* |h3| <= 1.51*2^58 */
+ /* |h7| <= 1.51*2^58 */
+
+ c3 = (h3 + (1 << 24)) >> 25
+ h4 += c3
+ h3 -= c3 << 25
+ c7 = (h7 + (1 << 24)) >> 25
+ h8 += c7
+ h7 -= c7 << 25
+ /* |h3| <= 2^24; from now on fits into int32 unchanged */
+ /* |h7| <= 2^24; from now on fits into int32 unchanged */
+ /* |h4| <= 1.52*2^33 */
+ /* |h8| <= 1.52*2^33 */
+
+ c4 = (h4 + (1 << 25)) >> 26
+ h5 += c4
+ h4 -= c4 << 26
+ c8 = (h8 + (1 << 25)) >> 26
+ h9 += c8
+ h8 -= c8 << 26
+ /* |h4| <= 2^25; from now on fits into int32 unchanged */
+ /* |h8| <= 2^25; from now on fits into int32 unchanged */
+ /* |h5| <= 1.01*2^24 */
+ /* |h9| <= 1.51*2^58 */
+
+ c9 = (h9 + (1 << 24)) >> 25
+ h0 += c9 * 19
+ h9 -= c9 << 25
+ /* |h9| <= 2^24; from now on fits into int32 unchanged */
+ /* |h0| <= 1.8*2^37 */
+
+ c0 = (h0 + (1 << 25)) >> 26
+ h1 += c0
+ h0 -= c0 << 26
+ /* |h0| <= 2^25; from now on fits into int32 unchanged */
+ /* |h1| <= 1.01*2^24 */
+
+ h[0] = int32(h0)
+ h[1] = int32(h1)
+ h[2] = int32(h2)
+ h[3] = int32(h3)
+ h[4] = int32(h4)
+ h[5] = int32(h5)
+ h[6] = int32(h6)
+ h[7] = int32(h7)
+ h[8] = int32(h8)
+ h[9] = int32(h9)
+}
+
+// FeMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs, can squeeze carries into int32.
+func FeMul(h, f, g *FieldElement) {
+ f0 := int64(f[0])
+ f1 := int64(f[1])
+ f2 := int64(f[2])
+ f3 := int64(f[3])
+ f4 := int64(f[4])
+ f5 := int64(f[5])
+ f6 := int64(f[6])
+ f7 := int64(f[7])
+ f8 := int64(f[8])
+ f9 := int64(f[9])
+
+ f1_2 := int64(2 * f[1])
+ f3_2 := int64(2 * f[3])
+ f5_2 := int64(2 * f[5])
+ f7_2 := int64(2 * f[7])
+ f9_2 := int64(2 * f[9])
+
+ g0 := int64(g[0])
+ g1 := int64(g[1])
+ g2 := int64(g[2])
+ g3 := int64(g[3])
+ g4 := int64(g[4])
+ g5 := int64(g[5])
+ g6 := int64(g[6])
+ g7 := int64(g[7])
+ g8 := int64(g[8])
+ g9 := int64(g[9])
+
+ g1_19 := int64(19 * g[1]) /* 1.4*2^29 */
+ g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */
+ g3_19 := int64(19 * g[3])
+ g4_19 := int64(19 * g[4])
+ g5_19 := int64(19 * g[5])
+ g6_19 := int64(19 * g[6])
+ g7_19 := int64(19 * g[7])
+ g8_19 := int64(19 * g[8])
+ g9_19 := int64(19 * g[9])
+
+ h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19
+ h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19
+ h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19
+ h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19
+ h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19
+ h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19
+ h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19
+ h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19
+ h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19
+ h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0
+
+ FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+ f0 := int64(f[0])
+ f1 := int64(f[1])
+ f2 := int64(f[2])
+ f3 := int64(f[3])
+ f4 := int64(f[4])
+ f5 := int64(f[5])
+ f6 := int64(f[6])
+ f7 := int64(f[7])
+ f8 := int64(f[8])
+ f9 := int64(f[9])
+ f0_2 := int64(2 * f[0])
+ f1_2 := int64(2 * f[1])
+ f2_2 := int64(2 * f[2])
+ f3_2 := int64(2 * f[3])
+ f4_2 := int64(2 * f[4])
+ f5_2 := int64(2 * f[5])
+ f6_2 := int64(2 * f[6])
+ f7_2 := int64(2 * f[7])
+ f5_38 := 38 * f5 // 1.31*2^30
+ f6_19 := 19 * f6 // 1.31*2^30
+ f7_38 := 38 * f7 // 1.31*2^30
+ f8_19 := 19 * f8 // 1.31*2^30
+ f9_38 := 38 * f9 // 1.31*2^30
+
+ h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38
+ h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19
+ h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19
+ h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38
+ h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38
+ h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19
+ h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19
+ h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38
+ h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38
+ h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5
+
+ return
+}
+
+// FeSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeSquare(h, f *FieldElement) {
+ h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+ FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeSquare2 sets h = 2 * f * f
+//
+// Can overlap h with f.
+//
+// Preconditions:
+// |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+// See fe_mul.c for discussion of implementation strategy.
+func FeSquare2(h, f *FieldElement) {
+ h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+
+ h0 += h0
+ h1 += h1
+ h2 += h2
+ h3 += h3
+ h4 += h4
+ h5 += h5
+ h6 += h6
+ h7 += h7
+ h8 += h8
+ h9 += h9
+
+ FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func FeInvert(out, z *FieldElement) {
+ var t0, t1, t2, t3 FieldElement
+ var i int
+
+ FeSquare(&t0, z) // 2^1
+ FeSquare(&t1, &t0) // 2^2
+ for i = 1; i < 2; i++ { // 2^3
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, z, &t1) // 2^3 + 2^0
+ FeMul(&t0, &t0, &t1) // 2^3 + 2^1 + 2^0
+ FeSquare(&t2, &t0) // 2^4 + 2^2 + 2^1
+ FeMul(&t1, &t1, &t2) // 2^4 + 2^3 + 2^2 + 2^1 + 2^0
+ FeSquare(&t2, &t1) // 5,4,3,2,1
+ for i = 1; i < 5; i++ { // 9,8,7,6,5
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0
+ FeSquare(&t2, &t1) // 10..1
+ for i = 1; i < 10; i++ { // 19..10
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t2, &t2, &t1) // 19..0
+ FeSquare(&t3, &t2) // 20..1
+ for i = 1; i < 20; i++ { // 39..20
+ FeSquare(&t3, &t3)
+ }
+ FeMul(&t2, &t3, &t2) // 39..0
+ FeSquare(&t2, &t2) // 40..1
+ for i = 1; i < 10; i++ { // 49..10
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1) // 49..0
+ FeSquare(&t2, &t1) // 50..1
+ for i = 1; i < 50; i++ { // 99..50
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t2, &t2, &t1) // 99..0
+ FeSquare(&t3, &t2) // 100..1
+ for i = 1; i < 100; i++ { // 199..100
+ FeSquare(&t3, &t3)
+ }
+ FeMul(&t2, &t3, &t2) // 199..0
+ FeSquare(&t2, &t2) // 200..1
+ for i = 1; i < 50; i++ { // 249..50
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1) // 249..0
+ FeSquare(&t1, &t1) // 250..1
+ for i = 1; i < 5; i++ { // 254..5
+ FeSquare(&t1, &t1)
+ }
+ FeMul(out, &t1, &t0) // 254..5,3,1,0
+}
+
+func fePow22523(out, z *FieldElement) {
+ var t0, t1, t2 FieldElement
+ var i int
+
+ FeSquare(&t0, z)
+ for i = 1; i < 1; i++ {
+ FeSquare(&t0, &t0)
+ }
+ FeSquare(&t1, &t0)
+ for i = 1; i < 2; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, z, &t1)
+ FeMul(&t0, &t0, &t1)
+ FeSquare(&t0, &t0)
+ for i = 1; i < 1; i++ {
+ FeSquare(&t0, &t0)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t1, &t0)
+ for i = 1; i < 5; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t1, &t0)
+ for i = 1; i < 10; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, &t1, &t0)
+ FeSquare(&t2, &t1)
+ for i = 1; i < 20; i++ {
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1)
+ FeSquare(&t1, &t1)
+ for i = 1; i < 10; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t1, &t0)
+ for i = 1; i < 50; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, &t1, &t0)
+ FeSquare(&t2, &t1)
+ for i = 1; i < 100; i++ {
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1)
+ FeSquare(&t1, &t1)
+ for i = 1; i < 50; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t0, &t0)
+ for i = 1; i < 2; i++ {
+ FeSquare(&t0, &t0)
+ }
+ FeMul(out, &t0, z)
+}
+
+// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 *
+// y^2 where d = -121665/121666.
+//
+// Several representations are used:
+// ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z
+// ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+// CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+// PreComputedGroupElement: (y+x,y-x,2dxy)
+
+type ProjectiveGroupElement struct {
+ X, Y, Z FieldElement
+}
+
+type ExtendedGroupElement struct {
+ X, Y, Z, T FieldElement
+}
+
+type CompletedGroupElement struct {
+ X, Y, Z, T FieldElement
+}
+
+type PreComputedGroupElement struct {
+ yPlusX, yMinusX, xy2d FieldElement
+}
+
+type CachedGroupElement struct {
+ yPlusX, yMinusX, Z, T2d FieldElement
+}
+
+func (p *ProjectiveGroupElement) Zero() {
+ FeZero(&p.X)
+ FeOne(&p.Y)
+ FeOne(&p.Z)
+}
+
+func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) {
+ var t0 FieldElement
+
+ FeSquare(&r.X, &p.X)
+ FeSquare(&r.Z, &p.Y)
+ FeSquare2(&r.T, &p.Z)
+ FeAdd(&r.Y, &p.X, &p.Y)
+ FeSquare(&t0, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.X)
+ FeSub(&r.Z, &r.Z, &r.X)
+ FeSub(&r.X, &t0, &r.Y)
+ FeSub(&r.T, &r.T, &r.Z)
+}
+
+func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) {
+ var recip, x, y FieldElement
+
+ FeInvert(&recip, &p.Z)
+ FeMul(&x, &p.X, &recip)
+ FeMul(&y, &p.Y, &recip)
+ FeToBytes(s, &y)
+ s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) Zero() {
+ FeZero(&p.X)
+ FeOne(&p.Y)
+ FeOne(&p.Z)
+ FeZero(&p.T)
+}
+
+func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) {
+ var q ProjectiveGroupElement
+ p.ToProjective(&q)
+ q.Double(r)
+}
+
+func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) {
+ FeAdd(&r.yPlusX, &p.Y, &p.X)
+ FeSub(&r.yMinusX, &p.Y, &p.X)
+ FeCopy(&r.Z, &p.Z)
+ FeMul(&r.T2d, &p.T, &d2)
+}
+
+func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+ FeCopy(&r.X, &p.X)
+ FeCopy(&r.Y, &p.Y)
+ FeCopy(&r.Z, &p.Z)
+}
+
+func (p *ExtendedGroupElement) ToBytes(s *[32]byte) {
+ var recip, x, y FieldElement
+
+ FeInvert(&recip, &p.Z)
+ FeMul(&x, &p.X, &recip)
+ FeMul(&y, &p.Y, &recip)
+ FeToBytes(s, &y)
+ s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool {
+ var u, v, v3, vxx, check FieldElement
+
+ FeFromBytes(&p.Y, s)
+ FeOne(&p.Z)
+ FeSquare(&u, &p.Y)
+ FeMul(&v, &u, &d)
+ FeSub(&u, &u, &p.Z) // y = y^2-1
+ FeAdd(&v, &v, &p.Z) // v = dy^2+1
+
+ FeSquare(&v3, &v)
+ FeMul(&v3, &v3, &v) // v3 = v^3
+ FeSquare(&p.X, &v3)
+ FeMul(&p.X, &p.X, &v)
+ FeMul(&p.X, &p.X, &u) // x = uv^7
+
+ fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8)
+ FeMul(&p.X, &p.X, &v3)
+ FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8)
+
+ var tmpX, tmp2 [32]byte
+
+ FeSquare(&vxx, &p.X)
+ FeMul(&vxx, &vxx, &v)
+ FeSub(&check, &vxx, &u) // vx^2-u
+ if FeIsNonZero(&check) == 1 {
+ FeAdd(&check, &vxx, &u) // vx^2+u
+ if FeIsNonZero(&check) == 1 {
+ return false
+ }
+ FeMul(&p.X, &p.X, &SqrtM1)
+
+ FeToBytes(&tmpX, &p.X)
+ for i, v := range tmpX {
+ tmp2[31-i] = v
+ }
+ }
+
+ if FeIsNegative(&p.X) != (s[31] >> 7) {
+ FeNeg(&p.X, &p.X)
+ }
+
+ FeMul(&p.T, &p.X, &p.Y)
+ return true
+}
+
+func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+ FeMul(&r.X, &p.X, &p.T)
+ FeMul(&r.Y, &p.Y, &p.Z)
+ FeMul(&r.Z, &p.Z, &p.T)
+}
+
+func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) {
+ FeMul(&r.X, &p.X, &p.T)
+ FeMul(&r.Y, &p.Y, &p.Z)
+ FeMul(&r.Z, &p.Z, &p.T)
+ FeMul(&r.T, &p.X, &p.Y)
+}
+
+func (p *PreComputedGroupElement) Zero() {
+ FeOne(&p.yPlusX)
+ FeOne(&p.yMinusX)
+ FeZero(&p.xy2d)
+}
+
+func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yPlusX)
+ FeMul(&r.Y, &r.Y, &q.yMinusX)
+ FeMul(&r.T, &q.T2d, &p.T)
+ FeMul(&r.X, &p.Z, &q.Z)
+ FeAdd(&t0, &r.X, &r.X)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeAdd(&r.Z, &t0, &r.T)
+ FeSub(&r.T, &t0, &r.T)
+}
+
+func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yMinusX)
+ FeMul(&r.Y, &r.Y, &q.yPlusX)
+ FeMul(&r.T, &q.T2d, &p.T)
+ FeMul(&r.X, &p.Z, &q.Z)
+ FeAdd(&t0, &r.X, &r.X)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeSub(&r.Z, &t0, &r.T)
+ FeAdd(&r.T, &t0, &r.T)
+}
+
+func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yPlusX)
+ FeMul(&r.Y, &r.Y, &q.yMinusX)
+ FeMul(&r.T, &q.xy2d, &p.T)
+ FeAdd(&t0, &p.Z, &p.Z)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeAdd(&r.Z, &t0, &r.T)
+ FeSub(&r.T, &t0, &r.T)
+}
+
+func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yMinusX)
+ FeMul(&r.Y, &r.Y, &q.yPlusX)
+ FeMul(&r.T, &q.xy2d, &p.T)
+ FeAdd(&t0, &p.Z, &p.Z)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeSub(&r.Z, &t0, &r.T)
+ FeAdd(&r.T, &t0, &r.T)
+}
+
+func slide(r *[256]int8, a *[32]byte) {
+ for i := range r {
+ r[i] = int8(1 & (a[i>>3] >> uint(i&7)))
+ }
+
+ for i := range r {
+ if r[i] != 0 {
+ for b := 1; b <= 6 && i+b < 256; b++ {
+ if r[i+b] != 0 {
+ if r[i]+(r[i+b]<<uint(b)) <= 15 {
+ r[i] += r[i+b] << uint(b)
+ r[i+b] = 0
+ } else if r[i]-(r[i+b]<<uint(b)) >= -15 {
+ r[i] -= r[i+b] << uint(b)
+ for k := i + b; k < 256; k++ {
+ if r[k] == 0 {
+ r[k] = 1
+ break
+ }
+ r[k] = 0
+ }
+ } else {
+ break
+ }
+ }
+ }
+ }
+ }
+}
+
+// GeDoubleScalarMultVartime sets r = a*A + b*B
+// where a = a[0]+256*a[1]+...+256^31 a[31].
+// and b = b[0]+256*b[1]+...+256^31 b[31].
+// B is the Ed25519 base point (x,4/5) with x positive.
+func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) {
+ var aSlide, bSlide [256]int8
+ var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A
+ var t CompletedGroupElement
+ var u, A2 ExtendedGroupElement
+ var i int
+
+ slide(&aSlide, a)
+ slide(&bSlide, b)
+
+ A.ToCached(&Ai[0])
+ A.Double(&t)
+ t.ToExtended(&A2)
+
+ for i := 0; i < 7; i++ {
+ geAdd(&t, &A2, &Ai[i])
+ t.ToExtended(&u)
+ u.ToCached(&Ai[i+1])
+ }
+
+ r.Zero()
+
+ for i = 255; i >= 0; i-- {
+ if aSlide[i] != 0 || bSlide[i] != 0 {
+ break
+ }
+ }
+
+ for ; i >= 0; i-- {
+ r.Double(&t)
+
+ if aSlide[i] > 0 {
+ t.ToExtended(&u)
+ geAdd(&t, &u, &Ai[aSlide[i]/2])
+ } else if aSlide[i] < 0 {
+ t.ToExtended(&u)
+ geSub(&t, &u, &Ai[(-aSlide[i])/2])
+ }
+
+ if bSlide[i] > 0 {
+ t.ToExtended(&u)
+ geMixedAdd(&t, &u, &bi[bSlide[i]/2])
+ } else if bSlide[i] < 0 {
+ t.ToExtended(&u)
+ geMixedSub(&t, &u, &bi[(-bSlide[i])/2])
+ }
+
+ t.ToProjective(r)
+ }
+}
+
+// equal returns 1 if b == c and 0 otherwise, assuming that b and c are
+// non-negative.
+func equal(b, c int32) int32 {
+ x := uint32(b ^ c)
+ x--
+ return int32(x >> 31)
+}
+
+// negative returns 1 if b < 0 and 0 otherwise.
+func negative(b int32) int32 {
+ return (b >> 31) & 1
+}
+
+func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) {
+ FeCMove(&t.yPlusX, &u.yPlusX, b)
+ FeCMove(&t.yMinusX, &u.yMinusX, b)
+ FeCMove(&t.xy2d, &u.xy2d, b)
+}
+
+func selectPoint(t *PreComputedGroupElement, pos int32, b int32) {
+ var minusT PreComputedGroupElement
+ bNegative := negative(b)
+ bAbs := b - (((-bNegative) & b) << 1)
+
+ t.Zero()
+ for i := int32(0); i < 8; i++ {
+ PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1))
+ }
+ FeCopy(&minusT.yPlusX, &t.yMinusX)
+ FeCopy(&minusT.yMinusX, &t.yPlusX)
+ FeNeg(&minusT.xy2d, &t.xy2d)
+ PreComputedGroupElementCMove(t, &minusT, bNegative)
+}
+
+// GeScalarMultBase computes h = a*B, where
+// a = a[0]+256*a[1]+...+256^31 a[31]
+// B is the Ed25519 base point (x,4/5) with x positive.
+//
+// Preconditions:
+// a[31] <= 127
+func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) {
+ var e [64]int8
+
+ for i, v := range a {
+ e[2*i] = int8(v & 15)
+ e[2*i+1] = int8((v >> 4) & 15)
+ }
+
+ // each e[i] is between 0 and 15 and e[63] is between 0 and 7.
+
+ carry := int8(0)
+ for i := 0; i < 63; i++ {
+ e[i] += carry
+ carry = (e[i] + 8) >> 4
+ e[i] -= carry << 4
+ }
+ e[63] += carry
+ // each e[i] is between -8 and 8.
+
+ h.Zero()
+ var t PreComputedGroupElement
+ var r CompletedGroupElement
+ for i := int32(1); i < 64; i += 2 {
+ selectPoint(&t, i/2, int32(e[i]))
+ geMixedAdd(&r, h, &t)
+ r.ToExtended(h)
+ }
+
+ var s ProjectiveGroupElement
+
+ h.Double(&r)
+ r.ToProjective(&s)
+ s.Double(&r)
+ r.ToProjective(&s)
+ s.Double(&r)
+ r.ToProjective(&s)
+ s.Double(&r)
+ r.ToExtended(h)
+
+ for i := int32(0); i < 64; i += 2 {
+ selectPoint(&t, i/2, int32(e[i]))
+ geMixedAdd(&r, h, &t)
+ r.ToExtended(h)
+ }
+}
+
+// The scalars are GF(2^252 + 27742317777372353535851937790883648493).
+
+// Input:
+// a[0]+256*a[1]+...+256^31*a[31] = a
+// b[0]+256*b[1]+...+256^31*b[31] = b
+// c[0]+256*c[1]+...+256^31*c[31] = c
+//
+// Output:
+// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+func ScMulAdd(s, a, b, c *[32]byte) {
+ a0 := 2097151 & load3(a[:])
+ a1 := 2097151 & (load4(a[2:]) >> 5)
+ a2 := 2097151 & (load3(a[5:]) >> 2)
+ a3 := 2097151 & (load4(a[7:]) >> 7)
+ a4 := 2097151 & (load4(a[10:]) >> 4)
+ a5 := 2097151 & (load3(a[13:]) >> 1)
+ a6 := 2097151 & (load4(a[15:]) >> 6)
+ a7 := 2097151 & (load3(a[18:]) >> 3)
+ a8 := 2097151 & load3(a[21:])
+ a9 := 2097151 & (load4(a[23:]) >> 5)
+ a10 := 2097151 & (load3(a[26:]) >> 2)
+ a11 := (load4(a[28:]) >> 7)
+ b0 := 2097151 & load3(b[:])
+ b1 := 2097151 & (load4(b[2:]) >> 5)
+ b2 := 2097151 & (load3(b[5:]) >> 2)
+ b3 := 2097151 & (load4(b[7:]) >> 7)
+ b4 := 2097151 & (load4(b[10:]) >> 4)
+ b5 := 2097151 & (load3(b[13:]) >> 1)
+ b6 := 2097151 & (load4(b[15:]) >> 6)
+ b7 := 2097151 & (load3(b[18:]) >> 3)
+ b8 := 2097151 & load3(b[21:])
+ b9 := 2097151 & (load4(b[23:]) >> 5)
+ b10 := 2097151 & (load3(b[26:]) >> 2)
+ b11 := (load4(b[28:]) >> 7)
+ c0 := 2097151 & load3(c[:])
+ c1 := 2097151 & (load4(c[2:]) >> 5)
+ c2 := 2097151 & (load3(c[5:]) >> 2)
+ c3 := 2097151 & (load4(c[7:]) >> 7)
+ c4 := 2097151 & (load4(c[10:]) >> 4)
+ c5 := 2097151 & (load3(c[13:]) >> 1)
+ c6 := 2097151 & (load4(c[15:]) >> 6)
+ c7 := 2097151 & (load3(c[18:]) >> 3)
+ c8 := 2097151 & load3(c[21:])
+ c9 := 2097151 & (load4(c[23:]) >> 5)
+ c10 := 2097151 & (load3(c[26:]) >> 2)
+ c11 := (load4(c[28:]) >> 7)
+ var carry [23]int64
+
+ s0 := c0 + a0*b0
+ s1 := c1 + a0*b1 + a1*b0
+ s2 := c2 + a0*b2 + a1*b1 + a2*b0
+ s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0
+ s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0
+ s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0
+ s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0
+ s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0
+ s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0
+ s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0
+ s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0
+ s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0
+ s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1
+ s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2
+ s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3
+ s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4
+ s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5
+ s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6
+ s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7
+ s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8
+ s20 := a9*b11 + a10*b10 + a11*b9
+ s21 := a10*b11 + a11*b10
+ s22 := a11 * b11
+ s23 := int64(0)
+
+ carry[0] = (s0 + (1 << 20)) >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[2] = (s2 + (1 << 20)) >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[4] = (s4 + (1 << 20)) >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[12] = (s12 + (1 << 20)) >> 21
+ s13 += carry[12]
+ s12 -= carry[12] << 21
+ carry[14] = (s14 + (1 << 20)) >> 21
+ s15 += carry[14]
+ s14 -= carry[14] << 21
+ carry[16] = (s16 + (1 << 20)) >> 21
+ s17 += carry[16]
+ s16 -= carry[16] << 21
+ carry[18] = (s18 + (1 << 20)) >> 21
+ s19 += carry[18]
+ s18 -= carry[18] << 21
+ carry[20] = (s20 + (1 << 20)) >> 21
+ s21 += carry[20]
+ s20 -= carry[20] << 21
+ carry[22] = (s22 + (1 << 20)) >> 21
+ s23 += carry[22]
+ s22 -= carry[22] << 21
+
+ carry[1] = (s1 + (1 << 20)) >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[3] = (s3 + (1 << 20)) >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[5] = (s5 + (1 << 20)) >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+ carry[13] = (s13 + (1 << 20)) >> 21
+ s14 += carry[13]
+ s13 -= carry[13] << 21
+ carry[15] = (s15 + (1 << 20)) >> 21
+ s16 += carry[15]
+ s15 -= carry[15] << 21
+ carry[17] = (s17 + (1 << 20)) >> 21
+ s18 += carry[17]
+ s17 -= carry[17] << 21
+ carry[19] = (s19 + (1 << 20)) >> 21
+ s20 += carry[19]
+ s19 -= carry[19] << 21
+ carry[21] = (s21 + (1 << 20)) >> 21
+ s22 += carry[21]
+ s21 -= carry[21] << 21
+
+ s11 += s23 * 666643
+ s12 += s23 * 470296
+ s13 += s23 * 654183
+ s14 -= s23 * 997805
+ s15 += s23 * 136657
+ s16 -= s23 * 683901
+ s23 = 0
+
+ s10 += s22 * 666643
+ s11 += s22 * 470296
+ s12 += s22 * 654183
+ s13 -= s22 * 997805
+ s14 += s22 * 136657
+ s15 -= s22 * 683901
+ s22 = 0
+
+ s9 += s21 * 666643
+ s10 += s21 * 470296
+ s11 += s21 * 654183
+ s12 -= s21 * 997805
+ s13 += s21 * 136657
+ s14 -= s21 * 683901
+ s21 = 0
+
+ s8 += s20 * 666643
+ s9 += s20 * 470296
+ s10 += s20 * 654183
+ s11 -= s20 * 997805
+ s12 += s20 * 136657
+ s13 -= s20 * 683901
+ s20 = 0
+
+ s7 += s19 * 666643
+ s8 += s19 * 470296
+ s9 += s19 * 654183
+ s10 -= s19 * 997805
+ s11 += s19 * 136657
+ s12 -= s19 * 683901
+ s19 = 0
+
+ s6 += s18 * 666643
+ s7 += s18 * 470296
+ s8 += s18 * 654183
+ s9 -= s18 * 997805
+ s10 += s18 * 136657
+ s11 -= s18 * 683901
+ s18 = 0
+
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[12] = (s12 + (1 << 20)) >> 21
+ s13 += carry[12]
+ s12 -= carry[12] << 21
+ carry[14] = (s14 + (1 << 20)) >> 21
+ s15 += carry[14]
+ s14 -= carry[14] << 21
+ carry[16] = (s16 + (1 << 20)) >> 21
+ s17 += carry[16]
+ s16 -= carry[16] << 21
+
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+ carry[13] = (s13 + (1 << 20)) >> 21
+ s14 += carry[13]
+ s13 -= carry[13] << 21
+ carry[15] = (s15 + (1 << 20)) >> 21
+ s16 += carry[15]
+ s15 -= carry[15] << 21
+
+ s5 += s17 * 666643
+ s6 += s17 * 470296
+ s7 += s17 * 654183
+ s8 -= s17 * 997805
+ s9 += s17 * 136657
+ s10 -= s17 * 683901
+ s17 = 0
+
+ s4 += s16 * 666643
+ s5 += s16 * 470296
+ s6 += s16 * 654183
+ s7 -= s16 * 997805
+ s8 += s16 * 136657
+ s9 -= s16 * 683901
+ s16 = 0
+
+ s3 += s15 * 666643
+ s4 += s15 * 470296
+ s5 += s15 * 654183
+ s6 -= s15 * 997805
+ s7 += s15 * 136657
+ s8 -= s15 * 683901
+ s15 = 0
+
+ s2 += s14 * 666643
+ s3 += s14 * 470296
+ s4 += s14 * 654183
+ s5 -= s14 * 997805
+ s6 += s14 * 136657
+ s7 -= s14 * 683901
+ s14 = 0
+
+ s1 += s13 * 666643
+ s2 += s13 * 470296
+ s3 += s13 * 654183
+ s4 -= s13 * 997805
+ s5 += s13 * 136657
+ s6 -= s13 * 683901
+ s13 = 0
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = (s0 + (1 << 20)) >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[2] = (s2 + (1 << 20)) >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[4] = (s4 + (1 << 20)) >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ carry[1] = (s1 + (1 << 20)) >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[3] = (s3 + (1 << 20)) >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[5] = (s5 + (1 << 20)) >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[11] = s11 >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ s[0] = byte(s0 >> 0)
+ s[1] = byte(s0 >> 8)
+ s[2] = byte((s0 >> 16) | (s1 << 5))
+ s[3] = byte(s1 >> 3)
+ s[4] = byte(s1 >> 11)
+ s[5] = byte((s1 >> 19) | (s2 << 2))
+ s[6] = byte(s2 >> 6)
+ s[7] = byte((s2 >> 14) | (s3 << 7))
+ s[8] = byte(s3 >> 1)
+ s[9] = byte(s3 >> 9)
+ s[10] = byte((s3 >> 17) | (s4 << 4))
+ s[11] = byte(s4 >> 4)
+ s[12] = byte(s4 >> 12)
+ s[13] = byte((s4 >> 20) | (s5 << 1))
+ s[14] = byte(s5 >> 7)
+ s[15] = byte((s5 >> 15) | (s6 << 6))
+ s[16] = byte(s6 >> 2)
+ s[17] = byte(s6 >> 10)
+ s[18] = byte((s6 >> 18) | (s7 << 3))
+ s[19] = byte(s7 >> 5)
+ s[20] = byte(s7 >> 13)
+ s[21] = byte(s8 >> 0)
+ s[22] = byte(s8 >> 8)
+ s[23] = byte((s8 >> 16) | (s9 << 5))
+ s[24] = byte(s9 >> 3)
+ s[25] = byte(s9 >> 11)
+ s[26] = byte((s9 >> 19) | (s10 << 2))
+ s[27] = byte(s10 >> 6)
+ s[28] = byte((s10 >> 14) | (s11 << 7))
+ s[29] = byte(s11 >> 1)
+ s[30] = byte(s11 >> 9)
+ s[31] = byte(s11 >> 17)
+}
+
+// Input:
+// s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// Output:
+// s[0]+256*s[1]+...+256^31*s[31] = s mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+func ScReduce(out *[32]byte, s *[64]byte) {
+ s0 := 2097151 & load3(s[:])
+ s1 := 2097151 & (load4(s[2:]) >> 5)
+ s2 := 2097151 & (load3(s[5:]) >> 2)
+ s3 := 2097151 & (load4(s[7:]) >> 7)
+ s4 := 2097151 & (load4(s[10:]) >> 4)
+ s5 := 2097151 & (load3(s[13:]) >> 1)
+ s6 := 2097151 & (load4(s[15:]) >> 6)
+ s7 := 2097151 & (load3(s[18:]) >> 3)
+ s8 := 2097151 & load3(s[21:])
+ s9 := 2097151 & (load4(s[23:]) >> 5)
+ s10 := 2097151 & (load3(s[26:]) >> 2)
+ s11 := 2097151 & (load4(s[28:]) >> 7)
+ s12 := 2097151 & (load4(s[31:]) >> 4)
+ s13 := 2097151 & (load3(s[34:]) >> 1)
+ s14 := 2097151 & (load4(s[36:]) >> 6)
+ s15 := 2097151 & (load3(s[39:]) >> 3)
+ s16 := 2097151 & load3(s[42:])
+ s17 := 2097151 & (load4(s[44:]) >> 5)
+ s18 := 2097151 & (load3(s[47:]) >> 2)
+ s19 := 2097151 & (load4(s[49:]) >> 7)
+ s20 := 2097151 & (load4(s[52:]) >> 4)
+ s21 := 2097151 & (load3(s[55:]) >> 1)
+ s22 := 2097151 & (load4(s[57:]) >> 6)
+ s23 := (load4(s[60:]) >> 3)
+
+ s11 += s23 * 666643
+ s12 += s23 * 470296
+ s13 += s23 * 654183
+ s14 -= s23 * 997805
+ s15 += s23 * 136657
+ s16 -= s23 * 683901
+ s23 = 0
+
+ s10 += s22 * 666643
+ s11 += s22 * 470296
+ s12 += s22 * 654183
+ s13 -= s22 * 997805
+ s14 += s22 * 136657
+ s15 -= s22 * 683901
+ s22 = 0
+
+ s9 += s21 * 666643
+ s10 += s21 * 470296
+ s11 += s21 * 654183
+ s12 -= s21 * 997805
+ s13 += s21 * 136657
+ s14 -= s21 * 683901
+ s21 = 0
+
+ s8 += s20 * 666643
+ s9 += s20 * 470296
+ s10 += s20 * 654183
+ s11 -= s20 * 997805
+ s12 += s20 * 136657
+ s13 -= s20 * 683901
+ s20 = 0
+
+ s7 += s19 * 666643
+ s8 += s19 * 470296
+ s9 += s19 * 654183
+ s10 -= s19 * 997805
+ s11 += s19 * 136657
+ s12 -= s19 * 683901
+ s19 = 0
+
+ s6 += s18 * 666643
+ s7 += s18 * 470296
+ s8 += s18 * 654183
+ s9 -= s18 * 997805
+ s10 += s18 * 136657
+ s11 -= s18 * 683901
+ s18 = 0
+
+ var carry [17]int64
+
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[12] = (s12 + (1 << 20)) >> 21
+ s13 += carry[12]
+ s12 -= carry[12] << 21
+ carry[14] = (s14 + (1 << 20)) >> 21
+ s15 += carry[14]
+ s14 -= carry[14] << 21
+ carry[16] = (s16 + (1 << 20)) >> 21
+ s17 += carry[16]
+ s16 -= carry[16] << 21
+
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+ carry[13] = (s13 + (1 << 20)) >> 21
+ s14 += carry[13]
+ s13 -= carry[13] << 21
+ carry[15] = (s15 + (1 << 20)) >> 21
+ s16 += carry[15]
+ s15 -= carry[15] << 21
+
+ s5 += s17 * 666643
+ s6 += s17 * 470296
+ s7 += s17 * 654183
+ s8 -= s17 * 997805
+ s9 += s17 * 136657
+ s10 -= s17 * 683901
+ s17 = 0
+
+ s4 += s16 * 666643
+ s5 += s16 * 470296
+ s6 += s16 * 654183
+ s7 -= s16 * 997805
+ s8 += s16 * 136657
+ s9 -= s16 * 683901
+ s16 = 0
+
+ s3 += s15 * 666643
+ s4 += s15 * 470296
+ s5 += s15 * 654183
+ s6 -= s15 * 997805
+ s7 += s15 * 136657
+ s8 -= s15 * 683901
+ s15 = 0
+
+ s2 += s14 * 666643
+ s3 += s14 * 470296
+ s4 += s14 * 654183
+ s5 -= s14 * 997805
+ s6 += s14 * 136657
+ s7 -= s14 * 683901
+ s14 = 0
+
+ s1 += s13 * 666643
+ s2 += s13 * 470296
+ s3 += s13 * 654183
+ s4 -= s13 * 997805
+ s5 += s13 * 136657
+ s6 -= s13 * 683901
+ s13 = 0
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = (s0 + (1 << 20)) >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[2] = (s2 + (1 << 20)) >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[4] = (s4 + (1 << 20)) >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ carry[1] = (s1 + (1 << 20)) >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[3] = (s3 + (1 << 20)) >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[5] = (s5 + (1 << 20)) >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[11] = s11 >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ out[0] = byte(s0 >> 0)
+ out[1] = byte(s0 >> 8)
+ out[2] = byte((s0 >> 16) | (s1 << 5))
+ out[3] = byte(s1 >> 3)
+ out[4] = byte(s1 >> 11)
+ out[5] = byte((s1 >> 19) | (s2 << 2))
+ out[6] = byte(s2 >> 6)
+ out[7] = byte((s2 >> 14) | (s3 << 7))
+ out[8] = byte(s3 >> 1)
+ out[9] = byte(s3 >> 9)
+ out[10] = byte((s3 >> 17) | (s4 << 4))
+ out[11] = byte(s4 >> 4)
+ out[12] = byte(s4 >> 12)
+ out[13] = byte((s4 >> 20) | (s5 << 1))
+ out[14] = byte(s5 >> 7)
+ out[15] = byte((s5 >> 15) | (s6 << 6))
+ out[16] = byte(s6 >> 2)
+ out[17] = byte(s6 >> 10)
+ out[18] = byte((s6 >> 18) | (s7 << 3))
+ out[19] = byte(s7 >> 5)
+ out[20] = byte(s7 >> 13)
+ out[21] = byte(s8 >> 0)
+ out[22] = byte(s8 >> 8)
+ out[23] = byte((s8 >> 16) | (s9 << 5))
+ out[24] = byte(s9 >> 3)
+ out[25] = byte(s9 >> 11)
+ out[26] = byte((s9 >> 19) | (s10 << 2))
+ out[27] = byte(s10 >> 6)
+ out[28] = byte((s10 >> 14) | (s11 << 7))
+ out[29] = byte(s11 >> 1)
+ out[30] = byte(s11 >> 9)
+ out[31] = byte(s11 >> 17)
+}
diff --git a/vendor/golang.org/x/crypto/ssh/buffer.go b/vendor/golang.org/x/crypto/ssh/buffer.go
new file mode 100644
index 000000000..6931b5114
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/buffer.go
@@ -0,0 +1,98 @@
+// Copyright 2012 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 ssh
+
+import (
+ "io"
+ "sync"
+)
+
+// buffer provides a linked list buffer for data exchange
+// between producer and consumer. Theoretically the buffer is
+// of unlimited capacity as it does no allocation of its own.
+type buffer struct {
+ // protects concurrent access to head, tail and closed
+ *sync.Cond
+
+ head *element // the buffer that will be read first
+ tail *element // the buffer that will be read last
+
+ closed bool
+}
+
+// An element represents a single link in a linked list.
+type element struct {
+ buf []byte
+ next *element
+}
+
+// newBuffer returns an empty buffer that is not closed.
+func newBuffer() *buffer {
+ e := new(element)
+ b := &buffer{
+ Cond: newCond(),
+ head: e,
+ tail: e,
+ }
+ return b
+}
+
+// write makes buf available for Read to receive.
+// buf must not be modified after the call to write.
+func (b *buffer) write(buf []byte) {
+ b.Cond.L.Lock()
+ e := &element{buf: buf}
+ b.tail.next = e
+ b.tail = e
+ b.Cond.Signal()
+ b.Cond.L.Unlock()
+}
+
+// eof closes the buffer. Reads from the buffer once all
+// the data has been consumed will receive os.EOF.
+func (b *buffer) eof() error {
+ b.Cond.L.Lock()
+ b.closed = true
+ b.Cond.Signal()
+ b.Cond.L.Unlock()
+ return nil
+}
+
+// Read reads data from the internal buffer in buf. Reads will block
+// if no data is available, or until the buffer is closed.
+func (b *buffer) Read(buf []byte) (n int, err error) {
+ b.Cond.L.Lock()
+ defer b.Cond.L.Unlock()
+
+ for len(buf) > 0 {
+ // if there is data in b.head, copy it
+ if len(b.head.buf) > 0 {
+ r := copy(buf, b.head.buf)
+ buf, b.head.buf = buf[r:], b.head.buf[r:]
+ n += r
+ continue
+ }
+ // if there is a next buffer, make it the head
+ if len(b.head.buf) == 0 && b.head != b.tail {
+ b.head = b.head.next
+ continue
+ }
+
+ // if at least one byte has been copied, return
+ if n > 0 {
+ break
+ }
+
+ // if nothing was read, and there is nothing outstanding
+ // check to see if the buffer is closed.
+ if b.closed {
+ err = io.EOF
+ break
+ }
+ // out of buffers, wait for producer
+ b.Cond.Wait()
+ }
+ return
+}
diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go
new file mode 100644
index 000000000..6331c94d5
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/certs.go
@@ -0,0 +1,503 @@
+// Copyright 2012 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 ssh
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "sort"
+ "time"
+)
+
+// These constants from [PROTOCOL.certkeys] represent the algorithm names
+// for certificate types supported by this package.
+const (
+ CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
+ CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
+ CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+ CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+ CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+ CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
+)
+
+// Certificate types distinguish between host and user
+// certificates. The values can be set in the CertType field of
+// Certificate.
+const (
+ UserCert = 1
+ HostCert = 2
+)
+
+// Signature represents a cryptographic signature.
+type Signature struct {
+ Format string
+ Blob []byte
+}
+
+// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that
+// a certificate does not expire.
+const CertTimeInfinity = 1<<64 - 1
+
+// An Certificate represents an OpenSSH certificate as defined in
+// [PROTOCOL.certkeys]?rev=1.8.
+type Certificate struct {
+ Nonce []byte
+ Key PublicKey
+ Serial uint64
+ CertType uint32
+ KeyId string
+ ValidPrincipals []string
+ ValidAfter uint64
+ ValidBefore uint64
+ Permissions
+ Reserved []byte
+ SignatureKey PublicKey
+ Signature *Signature
+}
+
+// genericCertData holds the key-independent part of the certificate data.
+// Overall, certificates contain an nonce, public key fields and
+// key-independent fields.
+type genericCertData struct {
+ Serial uint64
+ CertType uint32
+ KeyId string
+ ValidPrincipals []byte
+ ValidAfter uint64
+ ValidBefore uint64
+ CriticalOptions []byte
+ Extensions []byte
+ Reserved []byte
+ SignatureKey []byte
+ Signature []byte
+}
+
+func marshalStringList(namelist []string) []byte {
+ var to []byte
+ for _, name := range namelist {
+ s := struct{ N string }{name}
+ to = append(to, Marshal(&s)...)
+ }
+ return to
+}
+
+type optionsTuple struct {
+ Key string
+ Value []byte
+}
+
+type optionsTupleValue struct {
+ Value string
+}
+
+// serialize a map of critical options or extensions
+// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
+// we need two length prefixes for a non-empty string value
+func marshalTuples(tups map[string]string) []byte {
+ keys := make([]string, 0, len(tups))
+ for key := range tups {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ var ret []byte
+ for _, key := range keys {
+ s := optionsTuple{Key: key}
+ if value := tups[key]; len(value) > 0 {
+ s.Value = Marshal(&optionsTupleValue{value})
+ }
+ ret = append(ret, Marshal(&s)...)
+ }
+ return ret
+}
+
+// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
+// we need two length prefixes for a non-empty option value
+func parseTuples(in []byte) (map[string]string, error) {
+ tups := map[string]string{}
+ var lastKey string
+ var haveLastKey bool
+
+ for len(in) > 0 {
+ var key, val, extra []byte
+ var ok bool
+
+ if key, in, ok = parseString(in); !ok {
+ return nil, errShortRead
+ }
+ keyStr := string(key)
+ // according to [PROTOCOL.certkeys], the names must be in
+ // lexical order.
+ if haveLastKey && keyStr <= lastKey {
+ return nil, fmt.Errorf("ssh: certificate options are not in lexical order")
+ }
+ lastKey, haveLastKey = keyStr, true
+ // the next field is a data field, which if non-empty has a string embedded
+ if val, in, ok = parseString(in); !ok {
+ return nil, errShortRead
+ }
+ if len(val) > 0 {
+ val, extra, ok = parseString(val)
+ if !ok {
+ return nil, errShortRead
+ }
+ if len(extra) > 0 {
+ return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value")
+ }
+ tups[keyStr] = string(val)
+ } else {
+ tups[keyStr] = ""
+ }
+ }
+ return tups, nil
+}
+
+func parseCert(in []byte, privAlgo string) (*Certificate, error) {
+ nonce, rest, ok := parseString(in)
+ if !ok {
+ return nil, errShortRead
+ }
+
+ key, rest, err := parsePubKey(rest, privAlgo)
+ if err != nil {
+ return nil, err
+ }
+
+ var g genericCertData
+ if err := Unmarshal(rest, &g); err != nil {
+ return nil, err
+ }
+
+ c := &Certificate{
+ Nonce: nonce,
+ Key: key,
+ Serial: g.Serial,
+ CertType: g.CertType,
+ KeyId: g.KeyId,
+ ValidAfter: g.ValidAfter,
+ ValidBefore: g.ValidBefore,
+ }
+
+ for principals := g.ValidPrincipals; len(principals) > 0; {
+ principal, rest, ok := parseString(principals)
+ if !ok {
+ return nil, errShortRead
+ }
+ c.ValidPrincipals = append(c.ValidPrincipals, string(principal))
+ principals = rest
+ }
+
+ c.CriticalOptions, err = parseTuples(g.CriticalOptions)
+ if err != nil {
+ return nil, err
+ }
+ c.Extensions, err = parseTuples(g.Extensions)
+ if err != nil {
+ return nil, err
+ }
+ c.Reserved = g.Reserved
+ k, err := ParsePublicKey(g.SignatureKey)
+ if err != nil {
+ return nil, err
+ }
+
+ c.SignatureKey = k
+ c.Signature, rest, ok = parseSignatureBody(g.Signature)
+ if !ok || len(rest) > 0 {
+ return nil, errors.New("ssh: signature parse error")
+ }
+
+ return c, nil
+}
+
+type openSSHCertSigner struct {
+ pub *Certificate
+ signer Signer
+}
+
+// NewCertSigner returns a Signer that signs with the given Certificate, whose
+// private key is held by signer. It returns an error if the public key in cert
+// doesn't match the key used by signer.
+func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
+ if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
+ return nil, errors.New("ssh: signer and cert have different public key")
+ }
+
+ return &openSSHCertSigner{cert, signer}, nil
+}
+
+func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
+ return s.signer.Sign(rand, data)
+}
+
+func (s *openSSHCertSigner) PublicKey() PublicKey {
+ return s.pub
+}
+
+const sourceAddressCriticalOption = "source-address"
+
+// CertChecker does the work of verifying a certificate. Its methods
+// can be plugged into ClientConfig.HostKeyCallback and
+// ServerConfig.PublicKeyCallback. For the CertChecker to work,
+// minimally, the IsAuthority callback should be set.
+type CertChecker struct {
+ // SupportedCriticalOptions lists the CriticalOptions that the
+ // server application layer understands. These are only used
+ // for user certificates.
+ SupportedCriticalOptions []string
+
+ // IsAuthority should return true if the key is recognized as
+ // an authority. This allows for certificates to be signed by other
+ // certificates.
+ IsAuthority func(auth PublicKey) bool
+
+ // Clock is used for verifying time stamps. If nil, time.Now
+ // is used.
+ Clock func() time.Time
+
+ // UserKeyFallback is called when CertChecker.Authenticate encounters a
+ // public key that is not a certificate. It must implement validation
+ // of user keys or else, if nil, all such keys are rejected.
+ UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
+
+ // HostKeyFallback is called when CertChecker.CheckHostKey encounters a
+ // public key that is not a certificate. It must implement host key
+ // validation or else, if nil, all such keys are rejected.
+ HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error
+
+ // IsRevoked is called for each certificate so that revocation checking
+ // can be implemented. It should return true if the given certificate
+ // is revoked and false otherwise. If nil, no certificates are
+ // considered to have been revoked.
+ IsRevoked func(cert *Certificate) bool
+}
+
+// CheckHostKey checks a host key certificate. This method can be
+// plugged into ClientConfig.HostKeyCallback.
+func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error {
+ cert, ok := key.(*Certificate)
+ if !ok {
+ if c.HostKeyFallback != nil {
+ return c.HostKeyFallback(addr, remote, key)
+ }
+ return errors.New("ssh: non-certificate host key")
+ }
+ if cert.CertType != HostCert {
+ return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType)
+ }
+
+ return c.CheckCert(addr, cert)
+}
+
+// Authenticate checks a user certificate. Authenticate can be used as
+// a value for ServerConfig.PublicKeyCallback.
+func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) {
+ cert, ok := pubKey.(*Certificate)
+ if !ok {
+ if c.UserKeyFallback != nil {
+ return c.UserKeyFallback(conn, pubKey)
+ }
+ return nil, errors.New("ssh: normal key pairs not accepted")
+ }
+
+ if cert.CertType != UserCert {
+ return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType)
+ }
+
+ if err := c.CheckCert(conn.User(), cert); err != nil {
+ return nil, err
+ }
+
+ return &cert.Permissions, nil
+}
+
+// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and
+// the signature of the certificate.
+func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
+ if c.IsRevoked != nil && c.IsRevoked(cert) {
+ return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial)
+ }
+
+ for opt, _ := range cert.CriticalOptions {
+ // sourceAddressCriticalOption will be enforced by
+ // serverAuthenticate
+ if opt == sourceAddressCriticalOption {
+ continue
+ }
+
+ found := false
+ for _, supp := range c.SupportedCriticalOptions {
+ if supp == opt {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt)
+ }
+ }
+
+ if len(cert.ValidPrincipals) > 0 {
+ // By default, certs are valid for all users/hosts.
+ found := false
+ for _, p := range cert.ValidPrincipals {
+ if p == principal {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals)
+ }
+ }
+
+ if !c.IsAuthority(cert.SignatureKey) {
+ return fmt.Errorf("ssh: certificate signed by unrecognized authority")
+ }
+
+ clock := c.Clock
+ if clock == nil {
+ clock = time.Now
+ }
+
+ unixNow := clock().Unix()
+ if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) {
+ return fmt.Errorf("ssh: cert is not yet valid")
+ }
+ if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) {
+ return fmt.Errorf("ssh: cert has expired")
+ }
+ if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil {
+ return fmt.Errorf("ssh: certificate signature does not verify")
+ }
+
+ return nil
+}
+
+// SignCert sets c.SignatureKey to the authority's public key and stores a
+// Signature, by authority, in the certificate.
+func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
+ c.Nonce = make([]byte, 32)
+ if _, err := io.ReadFull(rand, c.Nonce); err != nil {
+ return err
+ }
+ c.SignatureKey = authority.PublicKey()
+
+ sig, err := authority.Sign(rand, c.bytesForSigning())
+ if err != nil {
+ return err
+ }
+ c.Signature = sig
+ return nil
+}
+
+var certAlgoNames = map[string]string{
+ KeyAlgoRSA: CertAlgoRSAv01,
+ KeyAlgoDSA: CertAlgoDSAv01,
+ KeyAlgoECDSA256: CertAlgoECDSA256v01,
+ KeyAlgoECDSA384: CertAlgoECDSA384v01,
+ KeyAlgoECDSA521: CertAlgoECDSA521v01,
+ KeyAlgoED25519: CertAlgoED25519v01,
+}
+
+// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
+// Panics if a non-certificate algorithm is passed.
+func certToPrivAlgo(algo string) string {
+ for privAlgo, pubAlgo := range certAlgoNames {
+ if pubAlgo == algo {
+ return privAlgo
+ }
+ }
+ panic("unknown cert algorithm")
+}
+
+func (cert *Certificate) bytesForSigning() []byte {
+ c2 := *cert
+ c2.Signature = nil
+ out := c2.Marshal()
+ // Drop trailing signature length.
+ return out[:len(out)-4]
+}
+
+// Marshal serializes c into OpenSSH's wire format. It is part of the
+// PublicKey interface.
+func (c *Certificate) Marshal() []byte {
+ generic := genericCertData{
+ Serial: c.Serial,
+ CertType: c.CertType,
+ KeyId: c.KeyId,
+ ValidPrincipals: marshalStringList(c.ValidPrincipals),
+ ValidAfter: uint64(c.ValidAfter),
+ ValidBefore: uint64(c.ValidBefore),
+ CriticalOptions: marshalTuples(c.CriticalOptions),
+ Extensions: marshalTuples(c.Extensions),
+ Reserved: c.Reserved,
+ SignatureKey: c.SignatureKey.Marshal(),
+ }
+ if c.Signature != nil {
+ generic.Signature = Marshal(c.Signature)
+ }
+ genericBytes := Marshal(&generic)
+ keyBytes := c.Key.Marshal()
+ _, keyBytes, _ = parseString(keyBytes)
+ prefix := Marshal(&struct {
+ Name string
+ Nonce []byte
+ Key []byte `ssh:"rest"`
+ }{c.Type(), c.Nonce, keyBytes})
+
+ result := make([]byte, 0, len(prefix)+len(genericBytes))
+ result = append(result, prefix...)
+ result = append(result, genericBytes...)
+ return result
+}
+
+// Type returns the key name. It is part of the PublicKey interface.
+func (c *Certificate) Type() string {
+ algo, ok := certAlgoNames[c.Key.Type()]
+ if !ok {
+ panic("unknown cert key type " + c.Key.Type())
+ }
+ return algo
+}
+
+// Verify verifies a signature against the certificate's public
+// key. It is part of the PublicKey interface.
+func (c *Certificate) Verify(data []byte, sig *Signature) error {
+ return c.Key.Verify(data, sig)
+}
+
+func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) {
+ format, in, ok := parseString(in)
+ if !ok {
+ return
+ }
+
+ out = &Signature{
+ Format: string(format),
+ }
+
+ if out.Blob, in, ok = parseString(in); !ok {
+ return
+ }
+
+ return out, in, ok
+}
+
+func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) {
+ sigBytes, rest, ok := parseString(in)
+ if !ok {
+ return
+ }
+
+ out, trailing, ok := parseSignatureBody(sigBytes)
+ if !ok || len(trailing) > 0 {
+ return nil, nil, false
+ }
+ return
+}
diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go
new file mode 100644
index 000000000..195530ea0
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/channel.go
@@ -0,0 +1,633 @@
+// Copyright 2011 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 ssh
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "sync"
+)
+
+const (
+ minPacketLength = 9
+ // channelMaxPacket contains the maximum number of bytes that will be
+ // sent in a single packet. As per RFC 4253, section 6.1, 32k is also
+ // the minimum.
+ channelMaxPacket = 1 << 15
+ // We follow OpenSSH here.
+ channelWindowSize = 64 * channelMaxPacket
+)
+
+// NewChannel represents an incoming request to a channel. It must either be
+// accepted for use by calling Accept, or rejected by calling Reject.
+type NewChannel interface {
+ // Accept accepts the channel creation request. It returns the Channel
+ // and a Go channel containing SSH requests. The Go channel must be
+ // serviced otherwise the Channel will hang.
+ Accept() (Channel, <-chan *Request, error)
+
+ // Reject rejects the channel creation request. After calling
+ // this, no other methods on the Channel may be called.
+ Reject(reason RejectionReason, message string) error
+
+ // ChannelType returns the type of the channel, as supplied by the
+ // client.
+ ChannelType() string
+
+ // ExtraData returns the arbitrary payload for this channel, as supplied
+ // by the client. This data is specific to the channel type.
+ ExtraData() []byte
+}
+
+// A Channel is an ordered, reliable, flow-controlled, duplex stream
+// that is multiplexed over an SSH connection.
+type Channel interface {
+ // Read reads up to len(data) bytes from the channel.
+ Read(data []byte) (int, error)
+
+ // Write writes len(data) bytes to the channel.
+ Write(data []byte) (int, error)
+
+ // Close signals end of channel use. No data may be sent after this
+ // call.
+ Close() error
+
+ // CloseWrite signals the end of sending in-band
+ // data. Requests may still be sent, and the other side may
+ // still send data
+ CloseWrite() error
+
+ // SendRequest sends a channel request. If wantReply is true,
+ // it will wait for a reply and return the result as a
+ // boolean, otherwise the return value will be false. Channel
+ // requests are out-of-band messages so they may be sent even
+ // if the data stream is closed or blocked by flow control.
+ // If the channel is closed before a reply is returned, io.EOF
+ // is returned.
+ SendRequest(name string, wantReply bool, payload []byte) (bool, error)
+
+ // Stderr returns an io.ReadWriter that writes to this channel
+ // with the extended data type set to stderr. Stderr may
+ // safely be read and written from a different goroutine than
+ // Read and Write respectively.
+ Stderr() io.ReadWriter
+}
+
+// Request is a request sent outside of the normal stream of
+// data. Requests can either be specific to an SSH channel, or they
+// can be global.
+type Request struct {
+ Type string
+ WantReply bool
+ Payload []byte
+
+ ch *channel
+ mux *mux
+}
+
+// Reply sends a response to a request. It must be called for all requests
+// where WantReply is true and is a no-op otherwise. The payload argument is
+// ignored for replies to channel-specific requests.
+func (r *Request) Reply(ok bool, payload []byte) error {
+ if !r.WantReply {
+ return nil
+ }
+
+ if r.ch == nil {
+ return r.mux.ackRequest(ok, payload)
+ }
+
+ return r.ch.ackRequest(ok)
+}
+
+// RejectionReason is an enumeration used when rejecting channel creation
+// requests. See RFC 4254, section 5.1.
+type RejectionReason uint32
+
+const (
+ Prohibited RejectionReason = iota + 1
+ ConnectionFailed
+ UnknownChannelType
+ ResourceShortage
+)
+
+// String converts the rejection reason to human readable form.
+func (r RejectionReason) String() string {
+ switch r {
+ case Prohibited:
+ return "administratively prohibited"
+ case ConnectionFailed:
+ return "connect failed"
+ case UnknownChannelType:
+ return "unknown channel type"
+ case ResourceShortage:
+ return "resource shortage"
+ }
+ return fmt.Sprintf("unknown reason %d", int(r))
+}
+
+func min(a uint32, b int) uint32 {
+ if a < uint32(b) {
+ return a
+ }
+ return uint32(b)
+}
+
+type channelDirection uint8
+
+const (
+ channelInbound channelDirection = iota
+ channelOutbound
+)
+
+// channel is an implementation of the Channel interface that works
+// with the mux class.
+type channel struct {
+ // R/O after creation
+ chanType string
+ extraData []byte
+ localId, remoteId uint32
+
+ // maxIncomingPayload and maxRemotePayload are the maximum
+ // payload sizes of normal and extended data packets for
+ // receiving and sending, respectively. The wire packet will
+ // be 9 or 13 bytes larger (excluding encryption overhead).
+ maxIncomingPayload uint32
+ maxRemotePayload uint32
+
+ mux *mux
+
+ // decided is set to true if an accept or reject message has been sent
+ // (for outbound channels) or received (for inbound channels).
+ decided bool
+
+ // direction contains either channelOutbound, for channels created
+ // locally, or channelInbound, for channels created by the peer.
+ direction channelDirection
+
+ // Pending internal channel messages.
+ msg chan interface{}
+
+ // Since requests have no ID, there can be only one request
+ // with WantReply=true outstanding. This lock is held by a
+ // goroutine that has such an outgoing request pending.
+ sentRequestMu sync.Mutex
+
+ incomingRequests chan *Request
+
+ sentEOF bool
+
+ // thread-safe data
+ remoteWin window
+ pending *buffer
+ extPending *buffer
+
+ // windowMu protects myWindow, the flow-control window.
+ windowMu sync.Mutex
+ myWindow uint32
+
+ // writeMu serializes calls to mux.conn.writePacket() and
+ // protects sentClose and packetPool. This mutex must be
+ // different from windowMu, as writePacket can block if there
+ // is a key exchange pending.
+ writeMu sync.Mutex
+ sentClose bool
+
+ // packetPool has a buffer for each extended channel ID to
+ // save allocations during writes.
+ packetPool map[uint32][]byte
+}
+
+// writePacket sends a packet. If the packet is a channel close, it updates
+// sentClose. This method takes the lock c.writeMu.
+func (c *channel) writePacket(packet []byte) error {
+ c.writeMu.Lock()
+ if c.sentClose {
+ c.writeMu.Unlock()
+ return io.EOF
+ }
+ c.sentClose = (packet[0] == msgChannelClose)
+ err := c.mux.conn.writePacket(packet)
+ c.writeMu.Unlock()
+ return err
+}
+
+func (c *channel) sendMessage(msg interface{}) error {
+ if debugMux {
+ log.Printf("send(%d): %#v", c.mux.chanList.offset, msg)
+ }
+
+ p := Marshal(msg)
+ binary.BigEndian.PutUint32(p[1:], c.remoteId)
+ return c.writePacket(p)
+}
+
+// WriteExtended writes data to a specific extended stream. These streams are
+// used, for example, for stderr.
+func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) {
+ if c.sentEOF {
+ return 0, io.EOF
+ }
+ // 1 byte message type, 4 bytes remoteId, 4 bytes data length
+ opCode := byte(msgChannelData)
+ headerLength := uint32(9)
+ if extendedCode > 0 {
+ headerLength += 4
+ opCode = msgChannelExtendedData
+ }
+
+ c.writeMu.Lock()
+ packet := c.packetPool[extendedCode]
+ // We don't remove the buffer from packetPool, so
+ // WriteExtended calls from different goroutines will be
+ // flagged as errors by the race detector.
+ c.writeMu.Unlock()
+
+ for len(data) > 0 {
+ space := min(c.maxRemotePayload, len(data))
+ if space, err = c.remoteWin.reserve(space); err != nil {
+ return n, err
+ }
+ if want := headerLength + space; uint32(cap(packet)) < want {
+ packet = make([]byte, want)
+ } else {
+ packet = packet[:want]
+ }
+
+ todo := data[:space]
+
+ packet[0] = opCode
+ binary.BigEndian.PutUint32(packet[1:], c.remoteId)
+ if extendedCode > 0 {
+ binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode))
+ }
+ binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo)))
+ copy(packet[headerLength:], todo)
+ if err = c.writePacket(packet); err != nil {
+ return n, err
+ }
+
+ n += len(todo)
+ data = data[len(todo):]
+ }
+
+ c.writeMu.Lock()
+ c.packetPool[extendedCode] = packet
+ c.writeMu.Unlock()
+
+ return n, err
+}
+
+func (c *channel) handleData(packet []byte) error {
+ headerLen := 9
+ isExtendedData := packet[0] == msgChannelExtendedData
+ if isExtendedData {
+ headerLen = 13
+ }
+ if len(packet) < headerLen {
+ // malformed data packet
+ return parseError(packet[0])
+ }
+
+ var extended uint32
+ if isExtendedData {
+ extended = binary.BigEndian.Uint32(packet[5:])
+ }
+
+ length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen])
+ if length == 0 {
+ return nil
+ }
+ if length > c.maxIncomingPayload {
+ // TODO(hanwen): should send Disconnect?
+ return errors.New("ssh: incoming packet exceeds maximum payload size")
+ }
+
+ data := packet[headerLen:]
+ if length != uint32(len(data)) {
+ return errors.New("ssh: wrong packet length")
+ }
+
+ c.windowMu.Lock()
+ if c.myWindow < length {
+ c.windowMu.Unlock()
+ // TODO(hanwen): should send Disconnect with reason?
+ return errors.New("ssh: remote side wrote too much")
+ }
+ c.myWindow -= length
+ c.windowMu.Unlock()
+
+ if extended == 1 {
+ c.extPending.write(data)
+ } else if extended > 0 {
+ // discard other extended data.
+ } else {
+ c.pending.write(data)
+ }
+ return nil
+}
+
+func (c *channel) adjustWindow(n uint32) error {
+ c.windowMu.Lock()
+ // Since myWindow is managed on our side, and can never exceed
+ // the initial window setting, we don't worry about overflow.
+ c.myWindow += uint32(n)
+ c.windowMu.Unlock()
+ return c.sendMessage(windowAdjustMsg{
+ AdditionalBytes: uint32(n),
+ })
+}
+
+func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) {
+ switch extended {
+ case 1:
+ n, err = c.extPending.Read(data)
+ case 0:
+ n, err = c.pending.Read(data)
+ default:
+ return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended)
+ }
+
+ if n > 0 {
+ err = c.adjustWindow(uint32(n))
+ // sendWindowAdjust can return io.EOF if the remote
+ // peer has closed the connection, however we want to
+ // defer forwarding io.EOF to the caller of Read until
+ // the buffer has been drained.
+ if n > 0 && err == io.EOF {
+ err = nil
+ }
+ }
+
+ return n, err
+}
+
+func (c *channel) close() {
+ c.pending.eof()
+ c.extPending.eof()
+ close(c.msg)
+ close(c.incomingRequests)
+ c.writeMu.Lock()
+ // This is not necessary for a normal channel teardown, but if
+ // there was another error, it is.
+ c.sentClose = true
+ c.writeMu.Unlock()
+ // Unblock writers.
+ c.remoteWin.close()
+}
+
+// responseMessageReceived is called when a success or failure message is
+// received on a channel to check that such a message is reasonable for the
+// given channel.
+func (c *channel) responseMessageReceived() error {
+ if c.direction == channelInbound {
+ return errors.New("ssh: channel response message received on inbound channel")
+ }
+ if c.decided {
+ return errors.New("ssh: duplicate response received for channel")
+ }
+ c.decided = true
+ return nil
+}
+
+func (c *channel) handlePacket(packet []byte) error {
+ switch packet[0] {
+ case msgChannelData, msgChannelExtendedData:
+ return c.handleData(packet)
+ case msgChannelClose:
+ c.sendMessage(channelCloseMsg{PeersId: c.remoteId})
+ c.mux.chanList.remove(c.localId)
+ c.close()
+ return nil
+ case msgChannelEOF:
+ // RFC 4254 is mute on how EOF affects dataExt messages but
+ // it is logical to signal EOF at the same time.
+ c.extPending.eof()
+ c.pending.eof()
+ return nil
+ }
+
+ decoded, err := decode(packet)
+ if err != nil {
+ return err
+ }
+
+ switch msg := decoded.(type) {
+ case *channelOpenFailureMsg:
+ if err := c.responseMessageReceived(); err != nil {
+ return err
+ }
+ c.mux.chanList.remove(msg.PeersId)
+ c.msg <- msg
+ case *channelOpenConfirmMsg:
+ if err := c.responseMessageReceived(); err != nil {
+ return err
+ }
+ if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
+ return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize)
+ }
+ c.remoteId = msg.MyId
+ c.maxRemotePayload = msg.MaxPacketSize
+ c.remoteWin.add(msg.MyWindow)
+ c.msg <- msg
+ case *windowAdjustMsg:
+ if !c.remoteWin.add(msg.AdditionalBytes) {
+ return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes)
+ }
+ case *channelRequestMsg:
+ req := Request{
+ Type: msg.Request,
+ WantReply: msg.WantReply,
+ Payload: msg.RequestSpecificData,
+ ch: c,
+ }
+
+ c.incomingRequests <- &req
+ default:
+ c.msg <- msg
+ }
+ return nil
+}
+
+func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel {
+ ch := &channel{
+ remoteWin: window{Cond: newCond()},
+ myWindow: channelWindowSize,
+ pending: newBuffer(),
+ extPending: newBuffer(),
+ direction: direction,
+ incomingRequests: make(chan *Request, chanSize),
+ msg: make(chan interface{}, chanSize),
+ chanType: chanType,
+ extraData: extraData,
+ mux: m,
+ packetPool: make(map[uint32][]byte),
+ }
+ ch.localId = m.chanList.add(ch)
+ return ch
+}
+
+var errUndecided = errors.New("ssh: must Accept or Reject channel")
+var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once")
+
+type extChannel struct {
+ code uint32
+ ch *channel
+}
+
+func (e *extChannel) Write(data []byte) (n int, err error) {
+ return e.ch.WriteExtended(data, e.code)
+}
+
+func (e *extChannel) Read(data []byte) (n int, err error) {
+ return e.ch.ReadExtended(data, e.code)
+}
+
+func (c *channel) Accept() (Channel, <-chan *Request, error) {
+ if c.decided {
+ return nil, nil, errDecidedAlready
+ }
+ c.maxIncomingPayload = channelMaxPacket
+ confirm := channelOpenConfirmMsg{
+ PeersId: c.remoteId,
+ MyId: c.localId,
+ MyWindow: c.myWindow,
+ MaxPacketSize: c.maxIncomingPayload,
+ }
+ c.decided = true
+ if err := c.sendMessage(confirm); err != nil {
+ return nil, nil, err
+ }
+
+ return c, c.incomingRequests, nil
+}
+
+func (ch *channel) Reject(reason RejectionReason, message string) error {
+ if ch.decided {
+ return errDecidedAlready
+ }
+ reject := channelOpenFailureMsg{
+ PeersId: ch.remoteId,
+ Reason: reason,
+ Message: message,
+ Language: "en",
+ }
+ ch.decided = true
+ return ch.sendMessage(reject)
+}
+
+func (ch *channel) Read(data []byte) (int, error) {
+ if !ch.decided {
+ return 0, errUndecided
+ }
+ return ch.ReadExtended(data, 0)
+}
+
+func (ch *channel) Write(data []byte) (int, error) {
+ if !ch.decided {
+ return 0, errUndecided
+ }
+ return ch.WriteExtended(data, 0)
+}
+
+func (ch *channel) CloseWrite() error {
+ if !ch.decided {
+ return errUndecided
+ }
+ ch.sentEOF = true
+ return ch.sendMessage(channelEOFMsg{
+ PeersId: ch.remoteId})
+}
+
+func (ch *channel) Close() error {
+ if !ch.decided {
+ return errUndecided
+ }
+
+ return ch.sendMessage(channelCloseMsg{
+ PeersId: ch.remoteId})
+}
+
+// Extended returns an io.ReadWriter that sends and receives data on the given,
+// SSH extended stream. Such streams are used, for example, for stderr.
+func (ch *channel) Extended(code uint32) io.ReadWriter {
+ if !ch.decided {
+ return nil
+ }
+ return &extChannel{code, ch}
+}
+
+func (ch *channel) Stderr() io.ReadWriter {
+ return ch.Extended(1)
+}
+
+func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
+ if !ch.decided {
+ return false, errUndecided
+ }
+
+ if wantReply {
+ ch.sentRequestMu.Lock()
+ defer ch.sentRequestMu.Unlock()
+ }
+
+ msg := channelRequestMsg{
+ PeersId: ch.remoteId,
+ Request: name,
+ WantReply: wantReply,
+ RequestSpecificData: payload,
+ }
+
+ if err := ch.sendMessage(msg); err != nil {
+ return false, err
+ }
+
+ if wantReply {
+ m, ok := (<-ch.msg)
+ if !ok {
+ return false, io.EOF
+ }
+ switch m.(type) {
+ case *channelRequestFailureMsg:
+ return false, nil
+ case *channelRequestSuccessMsg:
+ return true, nil
+ default:
+ return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m)
+ }
+ }
+
+ return false, nil
+}
+
+// ackRequest either sends an ack or nack to the channel request.
+func (ch *channel) ackRequest(ok bool) error {
+ if !ch.decided {
+ return errUndecided
+ }
+
+ var msg interface{}
+ if !ok {
+ msg = channelRequestFailureMsg{
+ PeersId: ch.remoteId,
+ }
+ } else {
+ msg = channelRequestSuccessMsg{
+ PeersId: ch.remoteId,
+ }
+ }
+ return ch.sendMessage(msg)
+}
+
+func (ch *channel) ChannelType() string {
+ return ch.chanType
+}
+
+func (ch *channel) ExtraData() []byte {
+ return ch.extraData
+}
diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go
new file mode 100644
index 000000000..13484ab4b
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/cipher.go
@@ -0,0 +1,627 @@
+// Copyright 2011 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 ssh
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/rc4"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "io/ioutil"
+)
+
+const (
+ packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher.
+
+ // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations
+ // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC
+ // indicates implementations SHOULD be able to handle larger packet sizes, but then
+ // waffles on about reasonable limits.
+ //
+ // OpenSSH caps their maxPacket at 256kB so we choose to do
+ // the same. maxPacket is also used to ensure that uint32
+ // length fields do not overflow, so it should remain well
+ // below 4G.
+ maxPacket = 256 * 1024
+)
+
+// noneCipher implements cipher.Stream and provides no encryption. It is used
+// by the transport before the first key-exchange.
+type noneCipher struct{}
+
+func (c noneCipher) XORKeyStream(dst, src []byte) {
+ copy(dst, src)
+}
+
+func newAESCTR(key, iv []byte) (cipher.Stream, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ return cipher.NewCTR(c, iv), nil
+}
+
+func newRC4(key, iv []byte) (cipher.Stream, error) {
+ return rc4.NewCipher(key)
+}
+
+type streamCipherMode struct {
+ keySize int
+ ivSize int
+ skip int
+ createFunc func(key, iv []byte) (cipher.Stream, error)
+}
+
+func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) {
+ if len(key) < c.keySize {
+ panic("ssh: key length too small for cipher")
+ }
+ if len(iv) < c.ivSize {
+ panic("ssh: iv too small for cipher")
+ }
+
+ stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize])
+ if err != nil {
+ return nil, err
+ }
+
+ var streamDump []byte
+ if c.skip > 0 {
+ streamDump = make([]byte, 512)
+ }
+
+ for remainingToDump := c.skip; remainingToDump > 0; {
+ dumpThisTime := remainingToDump
+ if dumpThisTime > len(streamDump) {
+ dumpThisTime = len(streamDump)
+ }
+ stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
+ remainingToDump -= dumpThisTime
+ }
+
+ return stream, nil
+}
+
+// cipherModes documents properties of supported ciphers. Ciphers not included
+// are not supported and will not be negotiated, even if explicitly requested in
+// ClientConfig.Crypto.Ciphers.
+var cipherModes = map[string]*streamCipherMode{
+ // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
+ // are defined in the order specified in the RFC.
+ "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR},
+ "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR},
+ "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR},
+
+ // Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
+ // They are defined in the order specified in the RFC.
+ "arcfour128": {16, 0, 1536, newRC4},
+ "arcfour256": {32, 0, 1536, newRC4},
+
+ // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
+ // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
+ // RC4) has problems with weak keys, and should be used with caution."
+ // RFC4345 introduces improved versions of Arcfour.
+ "arcfour": {16, 0, 0, newRC4},
+
+ // AES-GCM is not a stream cipher, so it is constructed with a
+ // special case. If we add any more non-stream ciphers, we
+ // should invest a cleaner way to do this.
+ gcmCipherID: {16, 12, 0, nil},
+
+ // CBC mode is insecure and so is not included in the default config.
+ // (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
+ // needed, it's possible to specify a custom Config to enable it.
+ // You should expect that an active attacker can recover plaintext if
+ // you do.
+ aes128cbcID: {16, aes.BlockSize, 0, nil},
+
+ // 3des-cbc is insecure and is disabled by default.
+ tripledescbcID: {24, des.BlockSize, 0, nil},
+}
+
+// prefixLen is the length of the packet prefix that contains the packet length
+// and number of padding bytes.
+const prefixLen = 5
+
+// streamPacketCipher is a packetCipher using a stream cipher.
+type streamPacketCipher struct {
+ mac hash.Hash
+ cipher cipher.Stream
+ etm bool
+
+ // The following members are to avoid per-packet allocations.
+ prefix [prefixLen]byte
+ seqNumBytes [4]byte
+ padding [2 * packetSizeMultiple]byte
+ packetData []byte
+ macResult []byte
+}
+
+// readPacket reads and decrypt a single packet from the reader argument.
+func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+ if _, err := io.ReadFull(r, s.prefix[:]); err != nil {
+ return nil, err
+ }
+
+ var encryptedPaddingLength [1]byte
+ if s.mac != nil && s.etm {
+ copy(encryptedPaddingLength[:], s.prefix[4:5])
+ s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
+ } else {
+ s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
+ }
+
+ length := binary.BigEndian.Uint32(s.prefix[0:4])
+ paddingLength := uint32(s.prefix[4])
+
+ var macSize uint32
+ if s.mac != nil {
+ s.mac.Reset()
+ binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
+ s.mac.Write(s.seqNumBytes[:])
+ if s.etm {
+ s.mac.Write(s.prefix[:4])
+ s.mac.Write(encryptedPaddingLength[:])
+ } else {
+ s.mac.Write(s.prefix[:])
+ }
+ macSize = uint32(s.mac.Size())
+ }
+
+ if length <= paddingLength+1 {
+ return nil, errors.New("ssh: invalid packet length, packet too small")
+ }
+
+ if length > maxPacket {
+ return nil, errors.New("ssh: invalid packet length, packet too large")
+ }
+
+ // the maxPacket check above ensures that length-1+macSize
+ // does not overflow.
+ if uint32(cap(s.packetData)) < length-1+macSize {
+ s.packetData = make([]byte, length-1+macSize)
+ } else {
+ s.packetData = s.packetData[:length-1+macSize]
+ }
+
+ if _, err := io.ReadFull(r, s.packetData); err != nil {
+ return nil, err
+ }
+ mac := s.packetData[length-1:]
+ data := s.packetData[:length-1]
+
+ if s.mac != nil && s.etm {
+ s.mac.Write(data)
+ }
+
+ s.cipher.XORKeyStream(data, data)
+
+ if s.mac != nil {
+ if !s.etm {
+ s.mac.Write(data)
+ }
+ s.macResult = s.mac.Sum(s.macResult[:0])
+ if subtle.ConstantTimeCompare(s.macResult, mac) != 1 {
+ return nil, errors.New("ssh: MAC failure")
+ }
+ }
+
+ return s.packetData[:length-paddingLength-1], nil
+}
+
+// writePacket encrypts and sends a packet of data to the writer argument
+func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
+ if len(packet) > maxPacket {
+ return errors.New("ssh: packet too large")
+ }
+
+ aadlen := 0
+ if s.mac != nil && s.etm {
+ // packet length is not encrypted for EtM modes
+ aadlen = 4
+ }
+
+ paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple
+ if paddingLength < 4 {
+ paddingLength += packetSizeMultiple
+ }
+
+ length := len(packet) + 1 + paddingLength
+ binary.BigEndian.PutUint32(s.prefix[:], uint32(length))
+ s.prefix[4] = byte(paddingLength)
+ padding := s.padding[:paddingLength]
+ if _, err := io.ReadFull(rand, padding); err != nil {
+ return err
+ }
+
+ if s.mac != nil {
+ s.mac.Reset()
+ binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
+ s.mac.Write(s.seqNumBytes[:])
+
+ if s.etm {
+ // For EtM algorithms, the packet length must stay unencrypted,
+ // but the following data (padding length) must be encrypted
+ s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
+ }
+
+ s.mac.Write(s.prefix[:])
+
+ if !s.etm {
+ // For non-EtM algorithms, the algorithm is applied on unencrypted data
+ s.mac.Write(packet)
+ s.mac.Write(padding)
+ }
+ }
+
+ if !(s.mac != nil && s.etm) {
+ // For EtM algorithms, the padding length has already been encrypted
+ // and the packet length must remain unencrypted
+ s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
+ }
+
+ s.cipher.XORKeyStream(packet, packet)
+ s.cipher.XORKeyStream(padding, padding)
+
+ if s.mac != nil && s.etm {
+ // For EtM algorithms, packet and padding must be encrypted
+ s.mac.Write(packet)
+ s.mac.Write(padding)
+ }
+
+ if _, err := w.Write(s.prefix[:]); err != nil {
+ return err
+ }
+ if _, err := w.Write(packet); err != nil {
+ return err
+ }
+ if _, err := w.Write(padding); err != nil {
+ return err
+ }
+
+ if s.mac != nil {
+ s.macResult = s.mac.Sum(s.macResult[:0])
+ if _, err := w.Write(s.macResult); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+type gcmCipher struct {
+ aead cipher.AEAD
+ prefix [4]byte
+ iv []byte
+ buf []byte
+}
+
+func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ aead, err := cipher.NewGCM(c)
+ if err != nil {
+ return nil, err
+ }
+
+ return &gcmCipher{
+ aead: aead,
+ iv: iv,
+ }, nil
+}
+
+const gcmTagSize = 16
+
+func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
+ // Pad out to multiple of 16 bytes. This is different from the
+ // stream cipher because that encrypts the length too.
+ padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple)
+ if padding < 4 {
+ padding += packetSizeMultiple
+ }
+
+ length := uint32(len(packet) + int(padding) + 1)
+ binary.BigEndian.PutUint32(c.prefix[:], length)
+ if _, err := w.Write(c.prefix[:]); err != nil {
+ return err
+ }
+
+ if cap(c.buf) < int(length) {
+ c.buf = make([]byte, length)
+ } else {
+ c.buf = c.buf[:length]
+ }
+
+ c.buf[0] = padding
+ copy(c.buf[1:], packet)
+ if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil {
+ return err
+ }
+ c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:])
+ if _, err := w.Write(c.buf); err != nil {
+ return err
+ }
+ c.incIV()
+
+ return nil
+}
+
+func (c *gcmCipher) incIV() {
+ for i := 4 + 7; i >= 4; i-- {
+ c.iv[i]++
+ if c.iv[i] != 0 {
+ break
+ }
+ }
+}
+
+func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+ if _, err := io.ReadFull(r, c.prefix[:]); err != nil {
+ return nil, err
+ }
+ length := binary.BigEndian.Uint32(c.prefix[:])
+ if length > maxPacket {
+ return nil, errors.New("ssh: max packet length exceeded.")
+ }
+
+ if cap(c.buf) < int(length+gcmTagSize) {
+ c.buf = make([]byte, length+gcmTagSize)
+ } else {
+ c.buf = c.buf[:length+gcmTagSize]
+ }
+
+ if _, err := io.ReadFull(r, c.buf); err != nil {
+ return nil, err
+ }
+
+ plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:])
+ if err != nil {
+ return nil, err
+ }
+ c.incIV()
+
+ padding := plain[0]
+ if padding < 4 || padding >= 20 {
+ return nil, fmt.Errorf("ssh: illegal padding %d", padding)
+ }
+
+ if int(padding+1) >= len(plain) {
+ return nil, fmt.Errorf("ssh: padding %d too large", padding)
+ }
+ plain = plain[1 : length-uint32(padding)]
+ return plain, nil
+}
+
+// cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1
+type cbcCipher struct {
+ mac hash.Hash
+ macSize uint32
+ decrypter cipher.BlockMode
+ encrypter cipher.BlockMode
+
+ // The following members are to avoid per-packet allocations.
+ seqNumBytes [4]byte
+ packetData []byte
+ macResult []byte
+
+ // Amount of data we should still read to hide which
+ // verification error triggered.
+ oracleCamouflage uint32
+}
+
+func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
+ cbc := &cbcCipher{
+ mac: macModes[algs.MAC].new(macKey),
+ decrypter: cipher.NewCBCDecrypter(c, iv),
+ encrypter: cipher.NewCBCEncrypter(c, iv),
+ packetData: make([]byte, 1024),
+ }
+ if cbc.mac != nil {
+ cbc.macSize = uint32(cbc.mac.Size())
+ }
+
+ return cbc, nil
+}
+
+func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ cbc, err := newCBCCipher(c, iv, key, macKey, algs)
+ if err != nil {
+ return nil, err
+ }
+
+ return cbc, nil
+}
+
+func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
+ c, err := des.NewTripleDESCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ cbc, err := newCBCCipher(c, iv, key, macKey, algs)
+ if err != nil {
+ return nil, err
+ }
+
+ return cbc, nil
+}
+
+func maxUInt32(a, b int) uint32 {
+ if a > b {
+ return uint32(a)
+ }
+ return uint32(b)
+}
+
+const (
+ cbcMinPacketSizeMultiple = 8
+ cbcMinPacketSize = 16
+ cbcMinPaddingSize = 4
+)
+
+// cbcError represents a verification error that may leak information.
+type cbcError string
+
+func (e cbcError) Error() string { return string(e) }
+
+func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
+ p, err := c.readPacketLeaky(seqNum, r)
+ if err != nil {
+ if _, ok := err.(cbcError); ok {
+ // Verification error: read a fixed amount of
+ // data, to make distinguishing between
+ // failing MAC and failing length check more
+ // difficult.
+ io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage))
+ }
+ }
+ return p, err
+}
+
+func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
+ blockSize := c.decrypter.BlockSize()
+
+ // Read the header, which will include some of the subsequent data in the
+ // case of block ciphers - this is copied back to the payload later.
+ // How many bytes of payload/padding will be read with this first read.
+ firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize)
+ firstBlock := c.packetData[:firstBlockLength]
+ if _, err := io.ReadFull(r, firstBlock); err != nil {
+ return nil, err
+ }
+
+ c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength
+
+ c.decrypter.CryptBlocks(firstBlock, firstBlock)
+ length := binary.BigEndian.Uint32(firstBlock[:4])
+ if length > maxPacket {
+ return nil, cbcError("ssh: packet too large")
+ }
+ if length+4 < maxUInt32(cbcMinPacketSize, blockSize) {
+ // The minimum size of a packet is 16 (or the cipher block size, whichever
+ // is larger) bytes.
+ return nil, cbcError("ssh: packet too small")
+ }
+ // The length of the packet (including the length field but not the MAC) must
+ // be a multiple of the block size or 8, whichever is larger.
+ if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 {
+ return nil, cbcError("ssh: invalid packet length multiple")
+ }
+
+ paddingLength := uint32(firstBlock[4])
+ if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 {
+ return nil, cbcError("ssh: invalid packet length")
+ }
+
+ // Positions within the c.packetData buffer:
+ macStart := 4 + length
+ paddingStart := macStart - paddingLength
+
+ // Entire packet size, starting before length, ending at end of mac.
+ entirePacketSize := macStart + c.macSize
+
+ // Ensure c.packetData is large enough for the entire packet data.
+ if uint32(cap(c.packetData)) < entirePacketSize {
+ // Still need to upsize and copy, but this should be rare at runtime, only
+ // on upsizing the packetData buffer.
+ c.packetData = make([]byte, entirePacketSize)
+ copy(c.packetData, firstBlock)
+ } else {
+ c.packetData = c.packetData[:entirePacketSize]
+ }
+
+ if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil {
+ return nil, err
+ } else {
+ c.oracleCamouflage -= uint32(n)
+ }
+
+ remainingCrypted := c.packetData[firstBlockLength:macStart]
+ c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted)
+
+ mac := c.packetData[macStart:]
+ if c.mac != nil {
+ c.mac.Reset()
+ binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
+ c.mac.Write(c.seqNumBytes[:])
+ c.mac.Write(c.packetData[:macStart])
+ c.macResult = c.mac.Sum(c.macResult[:0])
+ if subtle.ConstantTimeCompare(c.macResult, mac) != 1 {
+ return nil, cbcError("ssh: MAC failure")
+ }
+ }
+
+ return c.packetData[prefixLen:paddingStart], nil
+}
+
+func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
+ effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize())
+
+ // Length of encrypted portion of the packet (header, payload, padding).
+ // Enforce minimum padding and packet size.
+ encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize)
+ // Enforce block size.
+ encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize
+
+ length := encLength - 4
+ paddingLength := int(length) - (1 + len(packet))
+
+ // Overall buffer contains: header, payload, padding, mac.
+ // Space for the MAC is reserved in the capacity but not the slice length.
+ bufferSize := encLength + c.macSize
+ if uint32(cap(c.packetData)) < bufferSize {
+ c.packetData = make([]byte, encLength, bufferSize)
+ } else {
+ c.packetData = c.packetData[:encLength]
+ }
+
+ p := c.packetData
+
+ // Packet header.
+ binary.BigEndian.PutUint32(p, length)
+ p = p[4:]
+ p[0] = byte(paddingLength)
+
+ // Payload.
+ p = p[1:]
+ copy(p, packet)
+
+ // Padding.
+ p = p[len(packet):]
+ if _, err := io.ReadFull(rand, p); err != nil {
+ return err
+ }
+
+ if c.mac != nil {
+ c.mac.Reset()
+ binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
+ c.mac.Write(c.seqNumBytes[:])
+ c.mac.Write(c.packetData)
+ // The MAC is now appended into the capacity reserved for it earlier.
+ c.packetData = c.mac.Sum(c.packetData)
+ }
+
+ c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength])
+
+ if _, err := w.Write(c.packetData); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go
new file mode 100644
index 000000000..c97f2978e
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/client.go
@@ -0,0 +1,211 @@
+// Copyright 2011 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 ssh
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "time"
+)
+
+// Client implements a traditional SSH client that supports shells,
+// subprocesses, port forwarding and tunneled dialing.
+type Client struct {
+ Conn
+
+ forwards forwardList // forwarded tcpip connections from the remote side
+ mu sync.Mutex
+ channelHandlers map[string]chan NewChannel
+}
+
+// HandleChannelOpen returns a channel on which NewChannel requests
+// for the given type are sent. If the type already is being handled,
+// nil is returned. The channel is closed when the connection is closed.
+func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.channelHandlers == nil {
+ // The SSH channel has been closed.
+ c := make(chan NewChannel)
+ close(c)
+ return c
+ }
+
+ ch := c.channelHandlers[channelType]
+ if ch != nil {
+ return nil
+ }
+
+ ch = make(chan NewChannel, chanSize)
+ c.channelHandlers[channelType] = ch
+ return ch
+}
+
+// NewClient creates a Client on top of the given connection.
+func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
+ conn := &Client{
+ Conn: c,
+ channelHandlers: make(map[string]chan NewChannel, 1),
+ }
+
+ go conn.handleGlobalRequests(reqs)
+ go conn.handleChannelOpens(chans)
+ go func() {
+ conn.Wait()
+ conn.forwards.closeAll()
+ }()
+ go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip"))
+ return conn
+}
+
+// NewClientConn establishes an authenticated SSH connection using c
+// as the underlying transport. The Request and NewChannel channels
+// must be serviced or the connection will hang.
+func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) {
+ fullConf := *config
+ fullConf.SetDefaults()
+ conn := &connection{
+ sshConn: sshConn{conn: c},
+ }
+
+ if err := conn.clientHandshake(addr, &fullConf); err != nil {
+ c.Close()
+ return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err)
+ }
+ conn.mux = newMux(conn.transport)
+ return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
+}
+
+// clientHandshake performs the client side key exchange. See RFC 4253 Section
+// 7.
+func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error {
+ if config.ClientVersion != "" {
+ c.clientVersion = []byte(config.ClientVersion)
+ } else {
+ c.clientVersion = []byte(packageVersion)
+ }
+ var err error
+ c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion)
+ if err != nil {
+ return err
+ }
+
+ c.transport = newClientTransport(
+ newTransport(c.sshConn.conn, config.Rand, true /* is client */),
+ c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
+ if err := c.transport.waitSession(); err != nil {
+ return err
+ }
+
+ c.sessionID = c.transport.getSessionID()
+ return c.clientAuthenticate(config)
+}
+
+// verifyHostKeySignature verifies the host key obtained in the key
+// exchange.
+func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error {
+ sig, rest, ok := parseSignatureBody(result.Signature)
+ if len(rest) > 0 || !ok {
+ return errors.New("ssh: signature parse error")
+ }
+
+ return hostKey.Verify(result.H, sig)
+}
+
+// NewSession opens a new Session for this client. (A session is a remote
+// execution of a program.)
+func (c *Client) NewSession() (*Session, error) {
+ ch, in, err := c.OpenChannel("session", nil)
+ if err != nil {
+ return nil, err
+ }
+ return newSession(ch, in)
+}
+
+func (c *Client) handleGlobalRequests(incoming <-chan *Request) {
+ for r := range incoming {
+ // This handles keepalive messages and matches
+ // the behaviour of OpenSSH.
+ r.Reply(false, nil)
+ }
+}
+
+// handleChannelOpens channel open messages from the remote side.
+func (c *Client) handleChannelOpens(in <-chan NewChannel) {
+ for ch := range in {
+ c.mu.Lock()
+ handler := c.channelHandlers[ch.ChannelType()]
+ c.mu.Unlock()
+
+ if handler != nil {
+ handler <- ch
+ } else {
+ ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType()))
+ }
+ }
+
+ c.mu.Lock()
+ for _, ch := range c.channelHandlers {
+ close(ch)
+ }
+ c.channelHandlers = nil
+ c.mu.Unlock()
+}
+
+// Dial starts a client connection to the given SSH server. It is a
+// convenience function that connects to the given network address,
+// initiates the SSH handshake, and then sets up a Client. For access
+// to incoming channels and requests, use net.Dial with NewClientConn
+// instead.
+func Dial(network, addr string, config *ClientConfig) (*Client, error) {
+ conn, err := net.DialTimeout(network, addr, config.Timeout)
+ if err != nil {
+ return nil, err
+ }
+ c, chans, reqs, err := NewClientConn(conn, addr, config)
+ if err != nil {
+ return nil, err
+ }
+ return NewClient(c, chans, reqs), nil
+}
+
+// A ClientConfig structure is used to configure a Client. It must not be
+// modified after having been passed to an SSH function.
+type ClientConfig struct {
+ // Config contains configuration that is shared between clients and
+ // servers.
+ Config
+
+ // User contains the username to authenticate as.
+ User string
+
+ // Auth contains possible authentication methods to use with the
+ // server. Only the first instance of a particular RFC 4252 method will
+ // be used during authentication.
+ Auth []AuthMethod
+
+ // HostKeyCallback, if not nil, is called during the cryptographic
+ // handshake to validate the server's host key. A nil HostKeyCallback
+ // implies that all host keys are accepted.
+ HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
+
+ // ClientVersion contains the version identification string that will
+ // be used for the connection. If empty, a reasonable default is used.
+ ClientVersion string
+
+ // HostKeyAlgorithms lists the key types that the client will
+ // accept from the server as host key, in order of
+ // preference. If empty, a reasonable default is used. Any
+ // string returned from PublicKey.Type method may be used, or
+ // any of the CertAlgoXxxx and KeyAlgoXxxx constants.
+ HostKeyAlgorithms []string
+
+ // Timeout is the maximum amount of time for the TCP connection to establish.
+ //
+ // A Timeout of zero means no timeout.
+ Timeout time.Duration
+}
diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go
new file mode 100644
index 000000000..fd1ec5dda
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/client_auth.go
@@ -0,0 +1,475 @@
+// Copyright 2011 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 ssh
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// clientAuthenticate authenticates with the remote server. See RFC 4252.
+func (c *connection) clientAuthenticate(config *ClientConfig) error {
+ // initiate user auth session
+ if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil {
+ return err
+ }
+ packet, err := c.transport.readPacket()
+ if err != nil {
+ return err
+ }
+ var serviceAccept serviceAcceptMsg
+ if err := Unmarshal(packet, &serviceAccept); err != nil {
+ return err
+ }
+
+ // during the authentication phase the client first attempts the "none" method
+ // then any untried methods suggested by the server.
+ tried := make(map[string]bool)
+ var lastMethods []string
+
+ sessionID := c.transport.getSessionID()
+ for auth := AuthMethod(new(noneAuth)); auth != nil; {
+ ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand)
+ if err != nil {
+ return err
+ }
+ if ok {
+ // success
+ return nil
+ }
+ tried[auth.method()] = true
+ if methods == nil {
+ methods = lastMethods
+ }
+ lastMethods = methods
+
+ auth = nil
+
+ findNext:
+ for _, a := range config.Auth {
+ candidateMethod := a.method()
+ if tried[candidateMethod] {
+ continue
+ }
+ for _, meth := range methods {
+ if meth == candidateMethod {
+ auth = a
+ break findNext
+ }
+ }
+ }
+ }
+ return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried))
+}
+
+func keys(m map[string]bool) []string {
+ s := make([]string, 0, len(m))
+
+ for key := range m {
+ s = append(s, key)
+ }
+ return s
+}
+
+// An AuthMethod represents an instance of an RFC 4252 authentication method.
+type AuthMethod interface {
+ // auth authenticates user over transport t.
+ // Returns true if authentication is successful.
+ // If authentication is not successful, a []string of alternative
+ // method names is returned. If the slice is nil, it will be ignored
+ // and the previous set of possible methods will be reused.
+ auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error)
+
+ // method returns the RFC 4252 method name.
+ method() string
+}
+
+// "none" authentication, RFC 4252 section 5.2.
+type noneAuth int
+
+func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
+ if err := c.writePacket(Marshal(&userAuthRequestMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: "none",
+ })); err != nil {
+ return false, nil, err
+ }
+
+ return handleAuthResponse(c)
+}
+
+func (n *noneAuth) method() string {
+ return "none"
+}
+
+// passwordCallback is an AuthMethod that fetches the password through
+// a function call, e.g. by prompting the user.
+type passwordCallback func() (password string, err error)
+
+func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
+ type passwordAuthMsg struct {
+ User string `sshtype:"50"`
+ Service string
+ Method string
+ Reply bool
+ Password string
+ }
+
+ pw, err := cb()
+ // REVIEW NOTE: is there a need to support skipping a password attempt?
+ // The program may only find out that the user doesn't have a password
+ // when prompting.
+ if err != nil {
+ return false, nil, err
+ }
+
+ if err := c.writePacket(Marshal(&passwordAuthMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: cb.method(),
+ Reply: false,
+ Password: pw,
+ })); err != nil {
+ return false, nil, err
+ }
+
+ return handleAuthResponse(c)
+}
+
+func (cb passwordCallback) method() string {
+ return "password"
+}
+
+// Password returns an AuthMethod using the given password.
+func Password(secret string) AuthMethod {
+ return passwordCallback(func() (string, error) { return secret, nil })
+}
+
+// PasswordCallback returns an AuthMethod that uses a callback for
+// fetching a password.
+func PasswordCallback(prompt func() (secret string, err error)) AuthMethod {
+ return passwordCallback(prompt)
+}
+
+type publickeyAuthMsg struct {
+ User string `sshtype:"50"`
+ Service string
+ Method string
+ // HasSig indicates to the receiver packet that the auth request is signed and
+ // should be used for authentication of the request.
+ HasSig bool
+ Algoname string
+ PubKey []byte
+ // Sig is tagged with "rest" so Marshal will exclude it during
+ // validateKey
+ Sig []byte `ssh:"rest"`
+}
+
+// publicKeyCallback is an AuthMethod that uses a set of key
+// pairs for authentication.
+type publicKeyCallback func() ([]Signer, error)
+
+func (cb publicKeyCallback) method() string {
+ return "publickey"
+}
+
+func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
+ // Authentication is performed in two stages. The first stage sends an
+ // enquiry to test if each key is acceptable to the remote. The second
+ // stage attempts to authenticate with the valid keys obtained in the
+ // first stage.
+
+ signers, err := cb()
+ if err != nil {
+ return false, nil, err
+ }
+ var validKeys []Signer
+ for _, signer := range signers {
+ if ok, err := validateKey(signer.PublicKey(), user, c); ok {
+ validKeys = append(validKeys, signer)
+ } else {
+ if err != nil {
+ return false, nil, err
+ }
+ }
+ }
+
+ // methods that may continue if this auth is not successful.
+ var methods []string
+ for _, signer := range validKeys {
+ pub := signer.PublicKey()
+
+ pubKey := pub.Marshal()
+ sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: cb.method(),
+ }, []byte(pub.Type()), pubKey))
+ if err != nil {
+ return false, nil, err
+ }
+
+ // manually wrap the serialized signature in a string
+ s := Marshal(sign)
+ sig := make([]byte, stringLength(len(s)))
+ marshalString(sig, s)
+ msg := publickeyAuthMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: cb.method(),
+ HasSig: true,
+ Algoname: pub.Type(),
+ PubKey: pubKey,
+ Sig: sig,
+ }
+ p := Marshal(&msg)
+ if err := c.writePacket(p); err != nil {
+ return false, nil, err
+ }
+ var success bool
+ success, methods, err = handleAuthResponse(c)
+ if err != nil {
+ return false, nil, err
+ }
+ if success {
+ return success, methods, err
+ }
+ }
+ return false, methods, nil
+}
+
+// validateKey validates the key provided is acceptable to the server.
+func validateKey(key PublicKey, user string, c packetConn) (bool, error) {
+ pubKey := key.Marshal()
+ msg := publickeyAuthMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: "publickey",
+ HasSig: false,
+ Algoname: key.Type(),
+ PubKey: pubKey,
+ }
+ if err := c.writePacket(Marshal(&msg)); err != nil {
+ return false, err
+ }
+
+ return confirmKeyAck(key, c)
+}
+
+func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
+ pubKey := key.Marshal()
+ algoname := key.Type()
+
+ for {
+ packet, err := c.readPacket()
+ if err != nil {
+ return false, err
+ }
+ switch packet[0] {
+ case msgUserAuthBanner:
+ // TODO(gpaul): add callback to present the banner to the user
+ case msgUserAuthPubKeyOk:
+ var msg userAuthPubKeyOkMsg
+ if err := Unmarshal(packet, &msg); err != nil {
+ return false, err
+ }
+ if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) {
+ return false, nil
+ }
+ return true, nil
+ case msgUserAuthFailure:
+ return false, nil
+ default:
+ return false, unexpectedMessageError(msgUserAuthSuccess, packet[0])
+ }
+ }
+}
+
+// PublicKeys returns an AuthMethod that uses the given key
+// pairs.
+func PublicKeys(signers ...Signer) AuthMethod {
+ return publicKeyCallback(func() ([]Signer, error) { return signers, nil })
+}
+
+// PublicKeysCallback returns an AuthMethod that runs the given
+// function to obtain a list of key pairs.
+func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod {
+ return publicKeyCallback(getSigners)
+}
+
+// handleAuthResponse returns whether the preceding authentication request succeeded
+// along with a list of remaining authentication methods to try next and
+// an error if an unexpected response was received.
+func handleAuthResponse(c packetConn) (bool, []string, error) {
+ for {
+ packet, err := c.readPacket()
+ if err != nil {
+ return false, nil, err
+ }
+
+ switch packet[0] {
+ case msgUserAuthBanner:
+ // TODO: add callback to present the banner to the user
+ case msgUserAuthFailure:
+ var msg userAuthFailureMsg
+ if err := Unmarshal(packet, &msg); err != nil {
+ return false, nil, err
+ }
+ return false, msg.Methods, nil
+ case msgUserAuthSuccess:
+ return true, nil, nil
+ default:
+ return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
+ }
+ }
+}
+
+// KeyboardInteractiveChallenge should print questions, optionally
+// disabling echoing (e.g. for passwords), and return all the answers.
+// Challenge may be called multiple times in a single session. After
+// successful authentication, the server may send a challenge with no
+// questions, for which the user and instruction messages should be
+// printed. RFC 4256 section 3.3 details how the UI should behave for
+// both CLI and GUI environments.
+type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error)
+
+// KeyboardInteractive returns a AuthMethod using a prompt/response
+// sequence controlled by the server.
+func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
+ return challenge
+}
+
+func (cb KeyboardInteractiveChallenge) method() string {
+ return "keyboard-interactive"
+}
+
+func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
+ type initiateMsg struct {
+ User string `sshtype:"50"`
+ Service string
+ Method string
+ Language string
+ Submethods string
+ }
+
+ if err := c.writePacket(Marshal(&initiateMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: "keyboard-interactive",
+ })); err != nil {
+ return false, nil, err
+ }
+
+ for {
+ packet, err := c.readPacket()
+ if err != nil {
+ return false, nil, err
+ }
+
+ // like handleAuthResponse, but with less options.
+ switch packet[0] {
+ case msgUserAuthBanner:
+ // TODO: Print banners during userauth.
+ continue
+ case msgUserAuthInfoRequest:
+ // OK
+ case msgUserAuthFailure:
+ var msg userAuthFailureMsg
+ if err := Unmarshal(packet, &msg); err != nil {
+ return false, nil, err
+ }
+ return false, msg.Methods, nil
+ case msgUserAuthSuccess:
+ return true, nil, nil
+ default:
+ return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
+ }
+
+ var msg userAuthInfoRequestMsg
+ if err := Unmarshal(packet, &msg); err != nil {
+ return false, nil, err
+ }
+
+ // Manually unpack the prompt/echo pairs.
+ rest := msg.Prompts
+ var prompts []string
+ var echos []bool
+ for i := 0; i < int(msg.NumPrompts); i++ {
+ prompt, r, ok := parseString(rest)
+ if !ok || len(r) == 0 {
+ return false, nil, errors.New("ssh: prompt format error")
+ }
+ prompts = append(prompts, string(prompt))
+ echos = append(echos, r[0] != 0)
+ rest = r[1:]
+ }
+
+ if len(rest) != 0 {
+ return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
+ }
+
+ answers, err := cb(msg.User, msg.Instruction, prompts, echos)
+ if err != nil {
+ return false, nil, err
+ }
+
+ if len(answers) != len(prompts) {
+ return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
+ }
+ responseLength := 1 + 4
+ for _, a := range answers {
+ responseLength += stringLength(len(a))
+ }
+ serialized := make([]byte, responseLength)
+ p := serialized
+ p[0] = msgUserAuthInfoResponse
+ p = p[1:]
+ p = marshalUint32(p, uint32(len(answers)))
+ for _, a := range answers {
+ p = marshalString(p, []byte(a))
+ }
+
+ if err := c.writePacket(serialized); err != nil {
+ return false, nil, err
+ }
+ }
+}
+
+type retryableAuthMethod struct {
+ authMethod AuthMethod
+ maxTries int
+}
+
+func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
+ for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
+ ok, methods, err = r.authMethod.auth(session, user, c, rand)
+ if ok || err != nil { // either success or error terminate
+ return ok, methods, err
+ }
+ }
+ return ok, methods, err
+}
+
+func (r *retryableAuthMethod) method() string {
+ return r.authMethod.method()
+}
+
+// RetryableAuthMethod is a decorator for other auth methods enabling them to
+// be retried up to maxTries before considering that AuthMethod itself failed.
+// If maxTries is <= 0, will retry indefinitely
+//
+// This is useful for interactive clients using challenge/response type
+// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
+// could mistype their response resulting in the server issuing a
+// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
+// [keyboard-interactive]); Without this decorator, the non-retryable
+// AuthMethod would be removed from future consideration, and never tried again
+// (and so the user would never be able to retry their entry).
+func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
+ return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
+}
diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go
new file mode 100644
index 000000000..8656d0f85
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/common.go
@@ -0,0 +1,371 @@
+// Copyright 2011 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 ssh
+
+import (
+ "crypto"
+ "crypto/rand"
+ "fmt"
+ "io"
+ "sync"
+
+ _ "crypto/sha1"
+ _ "crypto/sha256"
+ _ "crypto/sha512"
+)
+
+// These are string constants in the SSH protocol.
+const (
+ compressionNone = "none"
+ serviceUserAuth = "ssh-userauth"
+ serviceSSH = "ssh-connection"
+)
+
+// supportedCiphers specifies the supported ciphers in preference order.
+var supportedCiphers = []string{
+ "aes128-ctr", "aes192-ctr", "aes256-ctr",
+ "aes128-gcm@openssh.com",
+ "arcfour256", "arcfour128",
+}
+
+// supportedKexAlgos specifies the supported key-exchange algorithms in
+// preference order.
+var supportedKexAlgos = []string{
+ kexAlgoCurve25519SHA256,
+ // P384 and P521 are not constant-time yet, but since we don't
+ // reuse ephemeral keys, using them for ECDH should be OK.
+ kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
+ kexAlgoDH14SHA1, kexAlgoDH1SHA1,
+}
+
+// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods
+// of authenticating servers) in preference order.
+var supportedHostKeyAlgos = []string{
+ CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
+ CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
+
+ KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
+ KeyAlgoRSA, KeyAlgoDSA,
+
+ KeyAlgoED25519,
+}
+
+// supportedMACs specifies a default set of MAC algorithms in preference order.
+// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
+// because they have reached the end of their useful life.
+var supportedMACs = []string{
+ "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96",
+}
+
+var supportedCompressions = []string{compressionNone}
+
+// hashFuncs keeps the mapping of supported algorithms to their respective
+// hashes needed for signature verification.
+var hashFuncs = map[string]crypto.Hash{
+ KeyAlgoRSA: crypto.SHA1,
+ KeyAlgoDSA: crypto.SHA1,
+ KeyAlgoECDSA256: crypto.SHA256,
+ KeyAlgoECDSA384: crypto.SHA384,
+ KeyAlgoECDSA521: crypto.SHA512,
+ CertAlgoRSAv01: crypto.SHA1,
+ CertAlgoDSAv01: crypto.SHA1,
+ CertAlgoECDSA256v01: crypto.SHA256,
+ CertAlgoECDSA384v01: crypto.SHA384,
+ CertAlgoECDSA521v01: crypto.SHA512,
+}
+
+// unexpectedMessageError results when the SSH message that we received didn't
+// match what we wanted.
+func unexpectedMessageError(expected, got uint8) error {
+ return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
+}
+
+// parseError results from a malformed SSH message.
+func parseError(tag uint8) error {
+ return fmt.Errorf("ssh: parse error in message type %d", tag)
+}
+
+func findCommon(what string, client []string, server []string) (common string, err error) {
+ for _, c := range client {
+ for _, s := range server {
+ if c == s {
+ return c, nil
+ }
+ }
+ }
+ return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
+}
+
+type directionAlgorithms struct {
+ Cipher string
+ MAC string
+ Compression string
+}
+
+// rekeyBytes returns a rekeying intervals in bytes.
+func (a *directionAlgorithms) rekeyBytes() int64 {
+ // According to RFC4344 block ciphers should rekey after
+ // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
+ // 128.
+ switch a.Cipher {
+ case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID:
+ return 16 * (1 << 32)
+
+ }
+
+ // For others, stick with RFC4253 recommendation to rekey after 1 Gb of data.
+ return 1 << 30
+}
+
+type algorithms struct {
+ kex string
+ hostKey string
+ w directionAlgorithms
+ r directionAlgorithms
+}
+
+func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
+ result := &algorithms{}
+
+ result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
+ if err != nil {
+ return
+ }
+
+ result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
+ if err != nil {
+ return
+ }
+
+ result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
+ if err != nil {
+ return
+ }
+
+ result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
+ if err != nil {
+ return
+ }
+
+ result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
+ if err != nil {
+ return
+ }
+
+ result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
+ if err != nil {
+ return
+ }
+
+ result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
+ if err != nil {
+ return
+ }
+
+ result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
+ if err != nil {
+ return
+ }
+
+ return result, nil
+}
+
+// If rekeythreshold is too small, we can't make any progress sending
+// stuff.
+const minRekeyThreshold uint64 = 256
+
+// Config contains configuration data common to both ServerConfig and
+// ClientConfig.
+type Config struct {
+ // Rand provides the source of entropy for cryptographic
+ // primitives. If Rand is nil, the cryptographic random reader
+ // in package crypto/rand will be used.
+ Rand io.Reader
+
+ // The maximum number of bytes sent or received after which a
+ // new key is negotiated. It must be at least 256. If
+ // unspecified, 1 gigabyte is used.
+ RekeyThreshold uint64
+
+ // The allowed key exchanges algorithms. If unspecified then a
+ // default set of algorithms is used.
+ KeyExchanges []string
+
+ // The allowed cipher algorithms. If unspecified then a sensible
+ // default is used.
+ Ciphers []string
+
+ // The allowed MAC algorithms. If unspecified then a sensible default
+ // is used.
+ MACs []string
+}
+
+// SetDefaults sets sensible values for unset fields in config. This is
+// exported for testing: Configs passed to SSH functions are copied and have
+// default values set automatically.
+func (c *Config) SetDefaults() {
+ if c.Rand == nil {
+ c.Rand = rand.Reader
+ }
+ if c.Ciphers == nil {
+ c.Ciphers = supportedCiphers
+ }
+ var ciphers []string
+ for _, c := range c.Ciphers {
+ if cipherModes[c] != nil {
+ // reject the cipher if we have no cipherModes definition
+ ciphers = append(ciphers, c)
+ }
+ }
+ c.Ciphers = ciphers
+
+ if c.KeyExchanges == nil {
+ c.KeyExchanges = supportedKexAlgos
+ }
+
+ if c.MACs == nil {
+ c.MACs = supportedMACs
+ }
+
+ if c.RekeyThreshold == 0 {
+ // RFC 4253, section 9 suggests rekeying after 1G.
+ c.RekeyThreshold = 1 << 30
+ }
+ if c.RekeyThreshold < minRekeyThreshold {
+ c.RekeyThreshold = minRekeyThreshold
+ }
+}
+
+// buildDataSignedForAuth returns the data that is signed in order to prove
+// possession of a private key. See RFC 4252, section 7.
+func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
+ data := struct {
+ Session []byte
+ Type byte
+ User string
+ Service string
+ Method string
+ Sign bool
+ Algo []byte
+ PubKey []byte
+ }{
+ sessionId,
+ msgUserAuthRequest,
+ req.User,
+ req.Service,
+ req.Method,
+ true,
+ algo,
+ pubKey,
+ }
+ return Marshal(data)
+}
+
+func appendU16(buf []byte, n uint16) []byte {
+ return append(buf, byte(n>>8), byte(n))
+}
+
+func appendU32(buf []byte, n uint32) []byte {
+ return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
+}
+
+func appendU64(buf []byte, n uint64) []byte {
+ return append(buf,
+ byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
+ byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
+}
+
+func appendInt(buf []byte, n int) []byte {
+ return appendU32(buf, uint32(n))
+}
+
+func appendString(buf []byte, s string) []byte {
+ buf = appendU32(buf, uint32(len(s)))
+ buf = append(buf, s...)
+ return buf
+}
+
+func appendBool(buf []byte, b bool) []byte {
+ if b {
+ return append(buf, 1)
+ }
+ return append(buf, 0)
+}
+
+// newCond is a helper to hide the fact that there is no usable zero
+// value for sync.Cond.
+func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
+
+// window represents the buffer available to clients
+// wishing to write to a channel.
+type window struct {
+ *sync.Cond
+ win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
+ writeWaiters int
+ closed bool
+}
+
+// add adds win to the amount of window available
+// for consumers.
+func (w *window) add(win uint32) bool {
+ // a zero sized window adjust is a noop.
+ if win == 0 {
+ return true
+ }
+ w.L.Lock()
+ if w.win+win < win {
+ w.L.Unlock()
+ return false
+ }
+ w.win += win
+ // It is unusual that multiple goroutines would be attempting to reserve
+ // window space, but not guaranteed. Use broadcast to notify all waiters
+ // that additional window is available.
+ w.Broadcast()
+ w.L.Unlock()
+ return true
+}
+
+// close sets the window to closed, so all reservations fail
+// immediately.
+func (w *window) close() {
+ w.L.Lock()
+ w.closed = true
+ w.Broadcast()
+ w.L.Unlock()
+}
+
+// reserve reserves win from the available window capacity.
+// If no capacity remains, reserve will block. reserve may
+// return less than requested.
+func (w *window) reserve(win uint32) (uint32, error) {
+ var err error
+ w.L.Lock()
+ w.writeWaiters++
+ w.Broadcast()
+ for w.win == 0 && !w.closed {
+ w.Wait()
+ }
+ w.writeWaiters--
+ if w.win < win {
+ win = w.win
+ }
+ w.win -= win
+ if w.closed {
+ err = io.EOF
+ }
+ w.L.Unlock()
+ return win, err
+}
+
+// waitWriterBlocked waits until some goroutine is blocked for further
+// writes. It is used in tests only.
+func (w *window) waitWriterBlocked() {
+ w.Cond.L.Lock()
+ for w.writeWaiters == 0 {
+ w.Cond.Wait()
+ }
+ w.Cond.L.Unlock()
+}
diff --git a/vendor/golang.org/x/crypto/ssh/connection.go b/vendor/golang.org/x/crypto/ssh/connection.go
new file mode 100644
index 000000000..e786f2f9a
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/connection.go
@@ -0,0 +1,143 @@
+// Copyright 2013 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 ssh
+
+import (
+ "fmt"
+ "net"
+)
+
+// OpenChannelError is returned if the other side rejects an
+// OpenChannel request.
+type OpenChannelError struct {
+ Reason RejectionReason
+ Message string
+}
+
+func (e *OpenChannelError) Error() string {
+ return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message)
+}
+
+// ConnMetadata holds metadata for the connection.
+type ConnMetadata interface {
+ // User returns the user ID for this connection.
+ User() string
+
+ // SessionID returns the sesson hash, also denoted by H.
+ SessionID() []byte
+
+ // ClientVersion returns the client's version string as hashed
+ // into the session ID.
+ ClientVersion() []byte
+
+ // ServerVersion returns the server's version string as hashed
+ // into the session ID.
+ ServerVersion() []byte
+
+ // RemoteAddr returns the remote address for this connection.
+ RemoteAddr() net.Addr
+
+ // LocalAddr returns the local address for this connection.
+ LocalAddr() net.Addr
+}
+
+// Conn represents an SSH connection for both server and client roles.
+// Conn is the basis for implementing an application layer, such
+// as ClientConn, which implements the traditional shell access for
+// clients.
+type Conn interface {
+ ConnMetadata
+
+ // SendRequest sends a global request, and returns the
+ // reply. If wantReply is true, it returns the response status
+ // and payload. See also RFC4254, section 4.
+ SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error)
+
+ // OpenChannel tries to open an channel. If the request is
+ // rejected, it returns *OpenChannelError. On success it returns
+ // the SSH Channel and a Go channel for incoming, out-of-band
+ // requests. The Go channel must be serviced, or the
+ // connection will hang.
+ OpenChannel(name string, data []byte) (Channel, <-chan *Request, error)
+
+ // Close closes the underlying network connection
+ Close() error
+
+ // Wait blocks until the connection has shut down, and returns the
+ // error causing the shutdown.
+ Wait() error
+
+ // TODO(hanwen): consider exposing:
+ // RequestKeyChange
+ // Disconnect
+}
+
+// DiscardRequests consumes and rejects all requests from the
+// passed-in channel.
+func DiscardRequests(in <-chan *Request) {
+ for req := range in {
+ if req.WantReply {
+ req.Reply(false, nil)
+ }
+ }
+}
+
+// A connection represents an incoming connection.
+type connection struct {
+ transport *handshakeTransport
+ sshConn
+
+ // The connection protocol.
+ *mux
+}
+
+func (c *connection) Close() error {
+ return c.sshConn.conn.Close()
+}
+
+// sshconn provides net.Conn metadata, but disallows direct reads and
+// writes.
+type sshConn struct {
+ conn net.Conn
+
+ user string
+ sessionID []byte
+ clientVersion []byte
+ serverVersion []byte
+}
+
+func dup(src []byte) []byte {
+ dst := make([]byte, len(src))
+ copy(dst, src)
+ return dst
+}
+
+func (c *sshConn) User() string {
+ return c.user
+}
+
+func (c *sshConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+func (c *sshConn) Close() error {
+ return c.conn.Close()
+}
+
+func (c *sshConn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+func (c *sshConn) SessionID() []byte {
+ return dup(c.sessionID)
+}
+
+func (c *sshConn) ClientVersion() []byte {
+ return dup(c.clientVersion)
+}
+
+func (c *sshConn) ServerVersion() []byte {
+ return dup(c.serverVersion)
+}
diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go
new file mode 100644
index 000000000..d6be89466
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/doc.go
@@ -0,0 +1,18 @@
+// Copyright 2011 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 ssh implements an SSH client and server.
+
+SSH is a transport security protocol, an authentication protocol and a
+family of application protocols. The most typical application level
+protocol is a remote shell and this is specifically implemented. However,
+the multiplexed nature of SSH is exposed to users that wish to support
+others.
+
+References:
+ [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
+ [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
+*/
+package ssh // import "golang.org/x/crypto/ssh"
diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go
new file mode 100644
index 000000000..8de650644
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/handshake.go
@@ -0,0 +1,625 @@
+// Copyright 2013 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 ssh
+
+import (
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "sync"
+)
+
+// debugHandshake, if set, prints messages sent and received. Key
+// exchange messages are printed as if DH were used, so the debug
+// messages are wrong when using ECDH.
+const debugHandshake = false
+
+// chanSize sets the amount of buffering SSH connections. This is
+// primarily for testing: setting chanSize=0 uncovers deadlocks more
+// quickly.
+const chanSize = 16
+
+// keyingTransport is a packet based transport that supports key
+// changes. It need not be thread-safe. It should pass through
+// msgNewKeys in both directions.
+type keyingTransport interface {
+ packetConn
+
+ // prepareKeyChange sets up a key change. The key change for a
+ // direction will be effected if a msgNewKeys message is sent
+ // or received.
+ prepareKeyChange(*algorithms, *kexResult) error
+}
+
+// handshakeTransport implements rekeying on top of a keyingTransport
+// and offers a thread-safe writePacket() interface.
+type handshakeTransport struct {
+ conn keyingTransport
+ config *Config
+
+ serverVersion []byte
+ clientVersion []byte
+
+ // hostKeys is non-empty if we are the server. In that case,
+ // it contains all host keys that can be used to sign the
+ // connection.
+ hostKeys []Signer
+
+ // hostKeyAlgorithms is non-empty if we are the client. In that case,
+ // we accept these key types from the server as host key.
+ hostKeyAlgorithms []string
+
+ // On read error, incoming is closed, and readError is set.
+ incoming chan []byte
+ readError error
+
+ mu sync.Mutex
+ writeError error
+ sentInitPacket []byte
+ sentInitMsg *kexInitMsg
+ pendingPackets [][]byte // Used when a key exchange is in progress.
+
+ // If the read loop wants to schedule a kex, it pings this
+ // channel, and the write loop will send out a kex
+ // message.
+ requestKex chan struct{}
+
+ // If the other side requests or confirms a kex, its kexInit
+ // packet is sent here for the write loop to find it.
+ startKex chan *pendingKex
+
+ // data for host key checking
+ hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
+ dialAddress string
+ remoteAddr net.Addr
+
+ // Algorithms agreed in the last key exchange.
+ algorithms *algorithms
+
+ readPacketsLeft uint32
+ readBytesLeft int64
+
+ writePacketsLeft uint32
+ writeBytesLeft int64
+
+ // The session ID or nil if first kex did not complete yet.
+ sessionID []byte
+}
+
+type pendingKex struct {
+ otherInit []byte
+ done chan error
+}
+
+func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
+ t := &handshakeTransport{
+ conn: conn,
+ serverVersion: serverVersion,
+ clientVersion: clientVersion,
+ incoming: make(chan []byte, chanSize),
+ requestKex: make(chan struct{}, 1),
+ startKex: make(chan *pendingKex, 1),
+
+ config: config,
+ }
+
+ // We always start with a mandatory key exchange.
+ t.requestKex <- struct{}{}
+ return t
+}
+
+func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport {
+ t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
+ t.dialAddress = dialAddr
+ t.remoteAddr = addr
+ t.hostKeyCallback = config.HostKeyCallback
+ if config.HostKeyAlgorithms != nil {
+ t.hostKeyAlgorithms = config.HostKeyAlgorithms
+ } else {
+ t.hostKeyAlgorithms = supportedHostKeyAlgos
+ }
+ go t.readLoop()
+ go t.kexLoop()
+ return t
+}
+
+func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport {
+ t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
+ t.hostKeys = config.hostKeys
+ go t.readLoop()
+ go t.kexLoop()
+ return t
+}
+
+func (t *handshakeTransport) getSessionID() []byte {
+ return t.sessionID
+}
+
+// waitSession waits for the session to be established. This should be
+// the first thing to call after instantiating handshakeTransport.
+func (t *handshakeTransport) waitSession() error {
+ p, err := t.readPacket()
+ if err != nil {
+ return err
+ }
+ if p[0] != msgNewKeys {
+ return fmt.Errorf("ssh: first packet should be msgNewKeys")
+ }
+
+ return nil
+}
+
+func (t *handshakeTransport) id() string {
+ if len(t.hostKeys) > 0 {
+ return "server"
+ }
+ return "client"
+}
+
+func (t *handshakeTransport) printPacket(p []byte, write bool) {
+ action := "got"
+ if write {
+ action = "sent"
+ }
+
+ if p[0] == msgChannelData || p[0] == msgChannelExtendedData {
+ log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p))
+ } else {
+ msg, err := decode(p)
+ log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err)
+ }
+}
+
+func (t *handshakeTransport) readPacket() ([]byte, error) {
+ p, ok := <-t.incoming
+ if !ok {
+ return nil, t.readError
+ }
+ return p, nil
+}
+
+func (t *handshakeTransport) readLoop() {
+ first := true
+ for {
+ p, err := t.readOnePacket(first)
+ first = false
+ if err != nil {
+ t.readError = err
+ close(t.incoming)
+ break
+ }
+ if p[0] == msgIgnore || p[0] == msgDebug {
+ continue
+ }
+ t.incoming <- p
+ }
+
+ // Stop writers too.
+ t.recordWriteError(t.readError)
+
+ // Unblock the writer should it wait for this.
+ close(t.startKex)
+
+ // Don't close t.requestKex; it's also written to from writePacket.
+}
+
+func (t *handshakeTransport) pushPacket(p []byte) error {
+ if debugHandshake {
+ t.printPacket(p, true)
+ }
+ return t.conn.writePacket(p)
+}
+
+func (t *handshakeTransport) getWriteError() error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ return t.writeError
+}
+
+func (t *handshakeTransport) recordWriteError(err error) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.writeError == nil && err != nil {
+ t.writeError = err
+ }
+}
+
+func (t *handshakeTransport) requestKeyExchange() {
+ select {
+ case t.requestKex <- struct{}{}:
+ default:
+ // something already requested a kex, so do nothing.
+ }
+}
+
+func (t *handshakeTransport) kexLoop() {
+
+write:
+ for t.getWriteError() == nil {
+ var request *pendingKex
+ var sent bool
+
+ for request == nil || !sent {
+ var ok bool
+ select {
+ case request, ok = <-t.startKex:
+ if !ok {
+ break write
+ }
+ case <-t.requestKex:
+ break
+ }
+
+ if !sent {
+ if err := t.sendKexInit(); err != nil {
+ t.recordWriteError(err)
+ break
+ }
+ sent = true
+ }
+ }
+
+ if err := t.getWriteError(); err != nil {
+ if request != nil {
+ request.done <- err
+ }
+ break
+ }
+
+ // We're not servicing t.requestKex, but that is OK:
+ // we never block on sending to t.requestKex.
+
+ // We're not servicing t.startKex, but the remote end
+ // has just sent us a kexInitMsg, so it can't send
+ // another key change request, until we close the done
+ // channel on the pendingKex request.
+
+ err := t.enterKeyExchange(request.otherInit)
+
+ t.mu.Lock()
+ t.writeError = err
+ t.sentInitPacket = nil
+ t.sentInitMsg = nil
+ t.writePacketsLeft = packetRekeyThreshold
+ if t.config.RekeyThreshold > 0 {
+ t.writeBytesLeft = int64(t.config.RekeyThreshold)
+ } else if t.algorithms != nil {
+ t.writeBytesLeft = t.algorithms.w.rekeyBytes()
+ }
+
+ // we have completed the key exchange. Since the
+ // reader is still blocked, it is safe to clear out
+ // the requestKex channel. This avoids the situation
+ // where: 1) we consumed our own request for the
+ // initial kex, and 2) the kex from the remote side
+ // caused another send on the requestKex channel,
+ clear:
+ for {
+ select {
+ case <-t.requestKex:
+ //
+ default:
+ break clear
+ }
+ }
+
+ request.done <- t.writeError
+
+ // kex finished. Push packets that we received while
+ // the kex was in progress. Don't look at t.startKex
+ // and don't increment writtenSinceKex: if we trigger
+ // another kex while we are still busy with the last
+ // one, things will become very confusing.
+ for _, p := range t.pendingPackets {
+ t.writeError = t.pushPacket(p)
+ if t.writeError != nil {
+ break
+ }
+ }
+ t.pendingPackets = t.pendingPackets[:0]
+ t.mu.Unlock()
+ }
+
+ // drain startKex channel. We don't service t.requestKex
+ // because nobody does blocking sends there.
+ go func() {
+ for init := range t.startKex {
+ init.done <- t.writeError
+ }
+ }()
+
+ // Unblock reader.
+ t.conn.Close()
+}
+
+// The protocol uses uint32 for packet counters, so we can't let them
+// reach 1<<32. We will actually read and write more packets than
+// this, though: the other side may send more packets, and after we
+// hit this limit on writing we will send a few more packets for the
+// key exchange itself.
+const packetRekeyThreshold = (1 << 31)
+
+func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
+ p, err := t.conn.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ if t.readPacketsLeft > 0 {
+ t.readPacketsLeft--
+ } else {
+ t.requestKeyExchange()
+ }
+
+ if t.readBytesLeft > 0 {
+ t.readBytesLeft -= int64(len(p))
+ } else {
+ t.requestKeyExchange()
+ }
+
+ if debugHandshake {
+ t.printPacket(p, false)
+ }
+
+ if first && p[0] != msgKexInit {
+ return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
+ }
+
+ if p[0] != msgKexInit {
+ return p, nil
+ }
+
+ firstKex := t.sessionID == nil
+
+ kex := pendingKex{
+ done: make(chan error, 1),
+ otherInit: p,
+ }
+ t.startKex <- &kex
+ err = <-kex.done
+
+ if debugHandshake {
+ log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ t.readPacketsLeft = packetRekeyThreshold
+ if t.config.RekeyThreshold > 0 {
+ t.readBytesLeft = int64(t.config.RekeyThreshold)
+ } else {
+ t.readBytesLeft = t.algorithms.r.rekeyBytes()
+ }
+
+ // By default, a key exchange is hidden from higher layers by
+ // translating it into msgIgnore.
+ successPacket := []byte{msgIgnore}
+ if firstKex {
+ // sendKexInit() for the first kex waits for
+ // msgNewKeys so the authentication process is
+ // guaranteed to happen over an encrypted transport.
+ successPacket = []byte{msgNewKeys}
+ }
+
+ return successPacket, nil
+}
+
+// sendKexInit sends a key change message.
+func (t *handshakeTransport) sendKexInit() error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.sentInitMsg != nil {
+ // kexInits may be sent either in response to the other side,
+ // or because our side wants to initiate a key change, so we
+ // may have already sent a kexInit. In that case, don't send a
+ // second kexInit.
+ return nil
+ }
+
+ msg := &kexInitMsg{
+ KexAlgos: t.config.KeyExchanges,
+ CiphersClientServer: t.config.Ciphers,
+ CiphersServerClient: t.config.Ciphers,
+ MACsClientServer: t.config.MACs,
+ MACsServerClient: t.config.MACs,
+ CompressionClientServer: supportedCompressions,
+ CompressionServerClient: supportedCompressions,
+ }
+ io.ReadFull(rand.Reader, msg.Cookie[:])
+
+ if len(t.hostKeys) > 0 {
+ for _, k := range t.hostKeys {
+ msg.ServerHostKeyAlgos = append(
+ msg.ServerHostKeyAlgos, k.PublicKey().Type())
+ }
+ } else {
+ msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
+ }
+ packet := Marshal(msg)
+
+ // writePacket destroys the contents, so save a copy.
+ packetCopy := make([]byte, len(packet))
+ copy(packetCopy, packet)
+
+ if err := t.pushPacket(packetCopy); err != nil {
+ return err
+ }
+
+ t.sentInitMsg = msg
+ t.sentInitPacket = packet
+
+ return nil
+}
+
+func (t *handshakeTransport) writePacket(p []byte) error {
+ switch p[0] {
+ case msgKexInit:
+ return errors.New("ssh: only handshakeTransport can send kexInit")
+ case msgNewKeys:
+ return errors.New("ssh: only handshakeTransport can send newKeys")
+ }
+
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.writeError != nil {
+ return t.writeError
+ }
+
+ if t.sentInitMsg != nil {
+ // Copy the packet so the writer can reuse the buffer.
+ cp := make([]byte, len(p))
+ copy(cp, p)
+ t.pendingPackets = append(t.pendingPackets, cp)
+ return nil
+ }
+
+ if t.writeBytesLeft > 0 {
+ t.writeBytesLeft -= int64(len(p))
+ } else {
+ t.requestKeyExchange()
+ }
+
+ if t.writePacketsLeft > 0 {
+ t.writePacketsLeft--
+ } else {
+ t.requestKeyExchange()
+ }
+
+ if err := t.pushPacket(p); err != nil {
+ t.writeError = err
+ }
+
+ return nil
+}
+
+func (t *handshakeTransport) Close() error {
+ return t.conn.Close()
+}
+
+func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
+ if debugHandshake {
+ log.Printf("%s entered key exchange", t.id())
+ }
+
+ otherInit := &kexInitMsg{}
+ if err := Unmarshal(otherInitPacket, otherInit); err != nil {
+ return err
+ }
+
+ magics := handshakeMagics{
+ clientVersion: t.clientVersion,
+ serverVersion: t.serverVersion,
+ clientKexInit: otherInitPacket,
+ serverKexInit: t.sentInitPacket,
+ }
+
+ clientInit := otherInit
+ serverInit := t.sentInitMsg
+ if len(t.hostKeys) == 0 {
+ clientInit, serverInit = serverInit, clientInit
+
+ magics.clientKexInit = t.sentInitPacket
+ magics.serverKexInit = otherInitPacket
+ }
+
+ var err error
+ t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit)
+ if err != nil {
+ return err
+ }
+
+ // We don't send FirstKexFollows, but we handle receiving it.
+ //
+ // RFC 4253 section 7 defines the kex and the agreement method for
+ // first_kex_packet_follows. It states that the guessed packet
+ // should be ignored if the "kex algorithm and/or the host
+ // key algorithm is guessed wrong (server and client have
+ // different preferred algorithm), or if any of the other
+ // algorithms cannot be agreed upon". The other algorithms have
+ // already been checked above so the kex algorithm and host key
+ // algorithm are checked here.
+ if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) {
+ // other side sent a kex message for the wrong algorithm,
+ // which we have to ignore.
+ if _, err := t.conn.readPacket(); err != nil {
+ return err
+ }
+ }
+
+ kex, ok := kexAlgoMap[t.algorithms.kex]
+ if !ok {
+ return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex)
+ }
+
+ var result *kexResult
+ if len(t.hostKeys) > 0 {
+ result, err = t.server(kex, t.algorithms, &magics)
+ } else {
+ result, err = t.client(kex, t.algorithms, &magics)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ if t.sessionID == nil {
+ t.sessionID = result.H
+ }
+ result.SessionID = t.sessionID
+
+ t.conn.prepareKeyChange(t.algorithms, result)
+ if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
+ return err
+ }
+ if packet, err := t.conn.readPacket(); err != nil {
+ return err
+ } else if packet[0] != msgNewKeys {
+ return unexpectedMessageError(msgNewKeys, packet[0])
+ }
+
+ return nil
+}
+
+func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) {
+ var hostKey Signer
+ for _, k := range t.hostKeys {
+ if algs.hostKey == k.PublicKey().Type() {
+ hostKey = k
+ }
+ }
+
+ r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey)
+ return r, err
+}
+
+func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) {
+ result, err := kex.Client(t.conn, t.config.Rand, magics)
+ if err != nil {
+ return nil, err
+ }
+
+ hostKey, err := ParsePublicKey(result.HostKey)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := verifyHostKeySignature(hostKey, result); err != nil {
+ return nil, err
+ }
+
+ if t.hostKeyCallback != nil {
+ err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return result, nil
+}
diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go
new file mode 100644
index 000000000..c87fbebfd
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/kex.go
@@ -0,0 +1,540 @@
+// Copyright 2013 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 ssh
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/subtle"
+ "errors"
+ "io"
+ "math/big"
+
+ "golang.org/x/crypto/curve25519"
+)
+
+const (
+ kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
+ kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
+ kexAlgoECDH256 = "ecdh-sha2-nistp256"
+ kexAlgoECDH384 = "ecdh-sha2-nistp384"
+ kexAlgoECDH521 = "ecdh-sha2-nistp521"
+ kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
+)
+
+// kexResult captures the outcome of a key exchange.
+type kexResult struct {
+ // Session hash. See also RFC 4253, section 8.
+ H []byte
+
+ // Shared secret. See also RFC 4253, section 8.
+ K []byte
+
+ // Host key as hashed into H.
+ HostKey []byte
+
+ // Signature of H.
+ Signature []byte
+
+ // A cryptographic hash function that matches the security
+ // level of the key exchange algorithm. It is used for
+ // calculating H, and for deriving keys from H and K.
+ Hash crypto.Hash
+
+ // The session ID, which is the first H computed. This is used
+ // to derive key material inside the transport.
+ SessionID []byte
+}
+
+// handshakeMagics contains data that is always included in the
+// session hash.
+type handshakeMagics struct {
+ clientVersion, serverVersion []byte
+ clientKexInit, serverKexInit []byte
+}
+
+func (m *handshakeMagics) write(w io.Writer) {
+ writeString(w, m.clientVersion)
+ writeString(w, m.serverVersion)
+ writeString(w, m.clientKexInit)
+ writeString(w, m.serverKexInit)
+}
+
+// kexAlgorithm abstracts different key exchange algorithms.
+type kexAlgorithm interface {
+ // Server runs server-side key agreement, signing the result
+ // with a hostkey.
+ Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error)
+
+ // Client runs the client-side key agreement. Caller is
+ // responsible for verifying the host key signature.
+ Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error)
+}
+
+// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
+type dhGroup struct {
+ g, p, pMinus1 *big.Int
+}
+
+func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
+ if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 {
+ return nil, errors.New("ssh: DH parameter out of bounds")
+ }
+ return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
+}
+
+func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
+ hashFunc := crypto.SHA1
+
+ var x *big.Int
+ for {
+ var err error
+ if x, err = rand.Int(randSource, group.pMinus1); err != nil {
+ return nil, err
+ }
+ if x.Sign() > 0 {
+ break
+ }
+ }
+
+ X := new(big.Int).Exp(group.g, x, group.p)
+ kexDHInit := kexDHInitMsg{
+ X: X,
+ }
+ if err := c.writePacket(Marshal(&kexDHInit)); err != nil {
+ return nil, err
+ }
+
+ packet, err := c.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ var kexDHReply kexDHReplyMsg
+ if err = Unmarshal(packet, &kexDHReply); err != nil {
+ return nil, err
+ }
+
+ kInt, err := group.diffieHellman(kexDHReply.Y, x)
+ if err != nil {
+ return nil, err
+ }
+
+ h := hashFunc.New()
+ magics.write(h)
+ writeString(h, kexDHReply.HostKey)
+ writeInt(h, X)
+ writeInt(h, kexDHReply.Y)
+ K := make([]byte, intLength(kInt))
+ marshalInt(K, kInt)
+ h.Write(K)
+
+ return &kexResult{
+ H: h.Sum(nil),
+ K: K,
+ HostKey: kexDHReply.HostKey,
+ Signature: kexDHReply.Signature,
+ Hash: crypto.SHA1,
+ }, nil
+}
+
+func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
+ hashFunc := crypto.SHA1
+ packet, err := c.readPacket()
+ if err != nil {
+ return
+ }
+ var kexDHInit kexDHInitMsg
+ if err = Unmarshal(packet, &kexDHInit); err != nil {
+ return
+ }
+
+ var y *big.Int
+ for {
+ if y, err = rand.Int(randSource, group.pMinus1); err != nil {
+ return
+ }
+ if y.Sign() > 0 {
+ break
+ }
+ }
+
+ Y := new(big.Int).Exp(group.g, y, group.p)
+ kInt, err := group.diffieHellman(kexDHInit.X, y)
+ if err != nil {
+ return nil, err
+ }
+
+ hostKeyBytes := priv.PublicKey().Marshal()
+
+ h := hashFunc.New()
+ magics.write(h)
+ writeString(h, hostKeyBytes)
+ writeInt(h, kexDHInit.X)
+ writeInt(h, Y)
+
+ K := make([]byte, intLength(kInt))
+ marshalInt(K, kInt)
+ h.Write(K)
+
+ H := h.Sum(nil)
+
+ // H is already a hash, but the hostkey signing will apply its
+ // own key-specific hash algorithm.
+ sig, err := signAndMarshal(priv, randSource, H)
+ if err != nil {
+ return nil, err
+ }
+
+ kexDHReply := kexDHReplyMsg{
+ HostKey: hostKeyBytes,
+ Y: Y,
+ Signature: sig,
+ }
+ packet = Marshal(&kexDHReply)
+
+ err = c.writePacket(packet)
+ return &kexResult{
+ H: H,
+ K: K,
+ HostKey: hostKeyBytes,
+ Signature: sig,
+ Hash: crypto.SHA1,
+ }, nil
+}
+
+// ecdh performs Elliptic Curve Diffie-Hellman key exchange as
+// described in RFC 5656, section 4.
+type ecdh struct {
+ curve elliptic.Curve
+}
+
+func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
+ ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
+ if err != nil {
+ return nil, err
+ }
+
+ kexInit := kexECDHInitMsg{
+ ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y),
+ }
+
+ serialized := Marshal(&kexInit)
+ if err := c.writePacket(serialized); err != nil {
+ return nil, err
+ }
+
+ packet, err := c.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ var reply kexECDHReplyMsg
+ if err = Unmarshal(packet, &reply); err != nil {
+ return nil, err
+ }
+
+ x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey)
+ if err != nil {
+ return nil, err
+ }
+
+ // generate shared secret
+ secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes())
+
+ h := ecHash(kex.curve).New()
+ magics.write(h)
+ writeString(h, reply.HostKey)
+ writeString(h, kexInit.ClientPubKey)
+ writeString(h, reply.EphemeralPubKey)
+ K := make([]byte, intLength(secret))
+ marshalInt(K, secret)
+ h.Write(K)
+
+ return &kexResult{
+ H: h.Sum(nil),
+ K: K,
+ HostKey: reply.HostKey,
+ Signature: reply.Signature,
+ Hash: ecHash(kex.curve),
+ }, nil
+}
+
+// unmarshalECKey parses and checks an EC key.
+func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) {
+ x, y = elliptic.Unmarshal(curve, pubkey)
+ if x == nil {
+ return nil, nil, errors.New("ssh: elliptic.Unmarshal failure")
+ }
+ if !validateECPublicKey(curve, x, y) {
+ return nil, nil, errors.New("ssh: public key not on curve")
+ }
+ return x, y, nil
+}
+
+// validateECPublicKey checks that the point is a valid public key for
+// the given curve. See [SEC1], 3.2.2
+func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
+ if x.Sign() == 0 && y.Sign() == 0 {
+ return false
+ }
+
+ if x.Cmp(curve.Params().P) >= 0 {
+ return false
+ }
+
+ if y.Cmp(curve.Params().P) >= 0 {
+ return false
+ }
+
+ if !curve.IsOnCurve(x, y) {
+ return false
+ }
+
+ // We don't check if N * PubKey == 0, since
+ //
+ // - the NIST curves have cofactor = 1, so this is implicit.
+ // (We don't foresee an implementation that supports non NIST
+ // curves)
+ //
+ // - for ephemeral keys, we don't need to worry about small
+ // subgroup attacks.
+ return true
+}
+
+func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
+ packet, err := c.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ var kexECDHInit kexECDHInitMsg
+ if err = Unmarshal(packet, &kexECDHInit); err != nil {
+ return nil, err
+ }
+
+ clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey)
+ if err != nil {
+ return nil, err
+ }
+
+ // We could cache this key across multiple users/multiple
+ // connection attempts, but the benefit is small. OpenSSH
+ // generates a new key for each incoming connection.
+ ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
+ if err != nil {
+ return nil, err
+ }
+
+ hostKeyBytes := priv.PublicKey().Marshal()
+
+ serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y)
+
+ // generate shared secret
+ secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes())
+
+ h := ecHash(kex.curve).New()
+ magics.write(h)
+ writeString(h, hostKeyBytes)
+ writeString(h, kexECDHInit.ClientPubKey)
+ writeString(h, serializedEphKey)
+
+ K := make([]byte, intLength(secret))
+ marshalInt(K, secret)
+ h.Write(K)
+
+ H := h.Sum(nil)
+
+ // H is already a hash, but the hostkey signing will apply its
+ // own key-specific hash algorithm.
+ sig, err := signAndMarshal(priv, rand, H)
+ if err != nil {
+ return nil, err
+ }
+
+ reply := kexECDHReplyMsg{
+ EphemeralPubKey: serializedEphKey,
+ HostKey: hostKeyBytes,
+ Signature: sig,
+ }
+
+ serialized := Marshal(&reply)
+ if err := c.writePacket(serialized); err != nil {
+ return nil, err
+ }
+
+ return &kexResult{
+ H: H,
+ K: K,
+ HostKey: reply.HostKey,
+ Signature: sig,
+ Hash: ecHash(kex.curve),
+ }, nil
+}
+
+var kexAlgoMap = map[string]kexAlgorithm{}
+
+func init() {
+ // This is the group called diffie-hellman-group1-sha1 in RFC
+ // 4253 and Oakley Group 2 in RFC 2409.
+ p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
+ kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
+ g: new(big.Int).SetInt64(2),
+ p: p,
+ pMinus1: new(big.Int).Sub(p, bigOne),
+ }
+
+ // This is the group called diffie-hellman-group14-sha1 in RFC
+ // 4253 and Oakley Group 14 in RFC 3526.
+ p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
+
+ kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
+ g: new(big.Int).SetInt64(2),
+ p: p,
+ pMinus1: new(big.Int).Sub(p, bigOne),
+ }
+
+ kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
+ kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
+ kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
+ kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
+}
+
+// curve25519sha256 implements the curve25519-sha256@libssh.org key
+// agreement protocol, as described in
+// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
+type curve25519sha256 struct{}
+
+type curve25519KeyPair struct {
+ priv [32]byte
+ pub [32]byte
+}
+
+func (kp *curve25519KeyPair) generate(rand io.Reader) error {
+ if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
+ return err
+ }
+ curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
+ return nil
+}
+
+// curve25519Zeros is just an array of 32 zero bytes so that we have something
+// convenient to compare against in order to reject curve25519 points with the
+// wrong order.
+var curve25519Zeros [32]byte
+
+func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
+ var kp curve25519KeyPair
+ if err := kp.generate(rand); err != nil {
+ return nil, err
+ }
+ if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
+ return nil, err
+ }
+
+ packet, err := c.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ var reply kexECDHReplyMsg
+ if err = Unmarshal(packet, &reply); err != nil {
+ return nil, err
+ }
+ if len(reply.EphemeralPubKey) != 32 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
+ }
+
+ var servPub, secret [32]byte
+ copy(servPub[:], reply.EphemeralPubKey)
+ curve25519.ScalarMult(&secret, &kp.priv, &servPub)
+ if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
+ }
+
+ h := crypto.SHA256.New()
+ magics.write(h)
+ writeString(h, reply.HostKey)
+ writeString(h, kp.pub[:])
+ writeString(h, reply.EphemeralPubKey)
+
+ kInt := new(big.Int).SetBytes(secret[:])
+ K := make([]byte, intLength(kInt))
+ marshalInt(K, kInt)
+ h.Write(K)
+
+ return &kexResult{
+ H: h.Sum(nil),
+ K: K,
+ HostKey: reply.HostKey,
+ Signature: reply.Signature,
+ Hash: crypto.SHA256,
+ }, nil
+}
+
+func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
+ packet, err := c.readPacket()
+ if err != nil {
+ return
+ }
+ var kexInit kexECDHInitMsg
+ if err = Unmarshal(packet, &kexInit); err != nil {
+ return
+ }
+
+ if len(kexInit.ClientPubKey) != 32 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
+ }
+
+ var kp curve25519KeyPair
+ if err := kp.generate(rand); err != nil {
+ return nil, err
+ }
+
+ var clientPub, secret [32]byte
+ copy(clientPub[:], kexInit.ClientPubKey)
+ curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
+ if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
+ }
+
+ hostKeyBytes := priv.PublicKey().Marshal()
+
+ h := crypto.SHA256.New()
+ magics.write(h)
+ writeString(h, hostKeyBytes)
+ writeString(h, kexInit.ClientPubKey)
+ writeString(h, kp.pub[:])
+
+ kInt := new(big.Int).SetBytes(secret[:])
+ K := make([]byte, intLength(kInt))
+ marshalInt(K, kInt)
+ h.Write(K)
+
+ H := h.Sum(nil)
+
+ sig, err := signAndMarshal(priv, rand, H)
+ if err != nil {
+ return nil, err
+ }
+
+ reply := kexECDHReplyMsg{
+ EphemeralPubKey: kp.pub[:],
+ HostKey: hostKeyBytes,
+ Signature: sig,
+ }
+ if err := c.writePacket(Marshal(&reply)); err != nil {
+ return nil, err
+ }
+ return &kexResult{
+ H: H,
+ K: K,
+ HostKey: hostKeyBytes,
+ Signature: sig,
+ Hash: crypto.SHA256,
+ }, nil
+}
diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go
new file mode 100644
index 000000000..f38de9898
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/keys.go
@@ -0,0 +1,905 @@
+// Copyright 2012 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 ssh
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/dsa"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/md5"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/asn1"
+ "encoding/base64"
+ "encoding/hex"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "strings"
+
+ "golang.org/x/crypto/ed25519"
+)
+
+// These constants represent the algorithm names for key types supported by this
+// package.
+const (
+ KeyAlgoRSA = "ssh-rsa"
+ KeyAlgoDSA = "ssh-dss"
+ KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
+ KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
+ KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
+ KeyAlgoED25519 = "ssh-ed25519"
+)
+
+// parsePubKey parses a public key of the given algorithm.
+// Use ParsePublicKey for keys with prepended algorithm.
+func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) {
+ switch algo {
+ case KeyAlgoRSA:
+ return parseRSA(in)
+ case KeyAlgoDSA:
+ return parseDSA(in)
+ case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
+ return parseECDSA(in)
+ case KeyAlgoED25519:
+ return parseED25519(in)
+ case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
+ cert, err := parseCert(in, certToPrivAlgo(algo))
+ if err != nil {
+ return nil, nil, err
+ }
+ return cert, nil, nil
+ }
+ return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
+}
+
+// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
+// (see sshd(8) manual page) once the options and key type fields have been
+// removed.
+func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) {
+ in = bytes.TrimSpace(in)
+
+ i := bytes.IndexAny(in, " \t")
+ if i == -1 {
+ i = len(in)
+ }
+ base64Key := in[:i]
+
+ key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key)))
+ n, err := base64.StdEncoding.Decode(key, base64Key)
+ if err != nil {
+ return nil, "", err
+ }
+ key = key[:n]
+ out, err = ParsePublicKey(key)
+ if err != nil {
+ return nil, "", err
+ }
+ comment = string(bytes.TrimSpace(in[i:]))
+ return out, comment, nil
+}
+
+// ParseKnownHosts parses an entry in the format of the known_hosts file.
+//
+// The known_hosts format is documented in the sshd(8) manual page. This
+// function will parse a single entry from in. On successful return, marker
+// will contain the optional marker value (i.e. "cert-authority" or "revoked")
+// or else be empty, hosts will contain the hosts that this entry matches,
+// pubKey will contain the public key and comment will contain any trailing
+// comment at the end of the line. See the sshd(8) manual page for the various
+// forms that a host string can take.
+//
+// The unparsed remainder of the input will be returned in rest. This function
+// can be called repeatedly to parse multiple entries.
+//
+// If no entries were found in the input then err will be io.EOF. Otherwise a
+// non-nil err value indicates a parse error.
+func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) {
+ for len(in) > 0 {
+ end := bytes.IndexByte(in, '\n')
+ if end != -1 {
+ rest = in[end+1:]
+ in = in[:end]
+ } else {
+ rest = nil
+ }
+
+ end = bytes.IndexByte(in, '\r')
+ if end != -1 {
+ in = in[:end]
+ }
+
+ in = bytes.TrimSpace(in)
+ if len(in) == 0 || in[0] == '#' {
+ in = rest
+ continue
+ }
+
+ i := bytes.IndexAny(in, " \t")
+ if i == -1 {
+ in = rest
+ continue
+ }
+
+ // Strip out the beginning of the known_host key.
+ // This is either an optional marker or a (set of) hostname(s).
+ keyFields := bytes.Fields(in)
+ if len(keyFields) < 3 || len(keyFields) > 5 {
+ return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data")
+ }
+
+ // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated
+ // list of hosts
+ marker := ""
+ if keyFields[0][0] == '@' {
+ marker = string(keyFields[0][1:])
+ keyFields = keyFields[1:]
+ }
+
+ hosts := string(keyFields[0])
+ // keyFields[1] contains the key type (e.g. “ssh-rsa”).
+ // However, that information is duplicated inside the
+ // base64-encoded key and so is ignored here.
+
+ key := bytes.Join(keyFields[2:], []byte(" "))
+ if pubKey, comment, err = parseAuthorizedKey(key); err != nil {
+ return "", nil, nil, "", nil, err
+ }
+
+ return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil
+ }
+
+ return "", nil, nil, "", nil, io.EOF
+}
+
+// ParseAuthorizedKeys parses a public key from an authorized_keys
+// file used in OpenSSH according to the sshd(8) manual page.
+func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
+ for len(in) > 0 {
+ end := bytes.IndexByte(in, '\n')
+ if end != -1 {
+ rest = in[end+1:]
+ in = in[:end]
+ } else {
+ rest = nil
+ }
+
+ end = bytes.IndexByte(in, '\r')
+ if end != -1 {
+ in = in[:end]
+ }
+
+ in = bytes.TrimSpace(in)
+ if len(in) == 0 || in[0] == '#' {
+ in = rest
+ continue
+ }
+
+ i := bytes.IndexAny(in, " \t")
+ if i == -1 {
+ in = rest
+ continue
+ }
+
+ if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
+ return out, comment, options, rest, nil
+ }
+
+ // No key type recognised. Maybe there's an options field at
+ // the beginning.
+ var b byte
+ inQuote := false
+ var candidateOptions []string
+ optionStart := 0
+ for i, b = range in {
+ isEnd := !inQuote && (b == ' ' || b == '\t')
+ if (b == ',' && !inQuote) || isEnd {
+ if i-optionStart > 0 {
+ candidateOptions = append(candidateOptions, string(in[optionStart:i]))
+ }
+ optionStart = i + 1
+ }
+ if isEnd {
+ break
+ }
+ if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) {
+ inQuote = !inQuote
+ }
+ }
+ for i < len(in) && (in[i] == ' ' || in[i] == '\t') {
+ i++
+ }
+ if i == len(in) {
+ // Invalid line: unmatched quote
+ in = rest
+ continue
+ }
+
+ in = in[i:]
+ i = bytes.IndexAny(in, " \t")
+ if i == -1 {
+ in = rest
+ continue
+ }
+
+ if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
+ options = candidateOptions
+ return out, comment, options, rest, nil
+ }
+
+ in = rest
+ continue
+ }
+
+ return nil, "", nil, nil, errors.New("ssh: no key found")
+}
+
+// ParsePublicKey parses an SSH public key formatted for use in
+// the SSH wire protocol according to RFC 4253, section 6.6.
+func ParsePublicKey(in []byte) (out PublicKey, err error) {
+ algo, in, ok := parseString(in)
+ if !ok {
+ return nil, errShortRead
+ }
+ var rest []byte
+ out, rest, err = parsePubKey(in, string(algo))
+ if len(rest) > 0 {
+ return nil, errors.New("ssh: trailing junk in public key")
+ }
+
+ return out, err
+}
+
+// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH
+// authorized_keys file. The return value ends with newline.
+func MarshalAuthorizedKey(key PublicKey) []byte {
+ b := &bytes.Buffer{}
+ b.WriteString(key.Type())
+ b.WriteByte(' ')
+ e := base64.NewEncoder(base64.StdEncoding, b)
+ e.Write(key.Marshal())
+ e.Close()
+ b.WriteByte('\n')
+ return b.Bytes()
+}
+
+// PublicKey is an abstraction of different types of public keys.
+type PublicKey interface {
+ // Type returns the key's type, e.g. "ssh-rsa".
+ Type() string
+
+ // Marshal returns the serialized key data in SSH wire format,
+ // with the name prefix.
+ Marshal() []byte
+
+ // Verify that sig is a signature on the given data using this
+ // key. This function will hash the data appropriately first.
+ Verify(data []byte, sig *Signature) error
+}
+
+// CryptoPublicKey, if implemented by a PublicKey,
+// returns the underlying crypto.PublicKey form of the key.
+type CryptoPublicKey interface {
+ CryptoPublicKey() crypto.PublicKey
+}
+
+// A Signer can create signatures that verify against a public key.
+type Signer interface {
+ // PublicKey returns an associated PublicKey instance.
+ PublicKey() PublicKey
+
+ // Sign returns raw signature for the given data. This method
+ // will apply the hash specified for the keytype to the data.
+ Sign(rand io.Reader, data []byte) (*Signature, error)
+}
+
+type rsaPublicKey rsa.PublicKey
+
+func (r *rsaPublicKey) Type() string {
+ return "ssh-rsa"
+}
+
+// parseRSA parses an RSA key according to RFC 4253, section 6.6.
+func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
+ var w struct {
+ E *big.Int
+ N *big.Int
+ Rest []byte `ssh:"rest"`
+ }
+ if err := Unmarshal(in, &w); err != nil {
+ return nil, nil, err
+ }
+
+ if w.E.BitLen() > 24 {
+ return nil, nil, errors.New("ssh: exponent too large")
+ }
+ e := w.E.Int64()
+ if e < 3 || e&1 == 0 {
+ return nil, nil, errors.New("ssh: incorrect exponent")
+ }
+
+ var key rsa.PublicKey
+ key.E = int(e)
+ key.N = w.N
+ return (*rsaPublicKey)(&key), w.Rest, nil
+}
+
+func (r *rsaPublicKey) Marshal() []byte {
+ e := new(big.Int).SetInt64(int64(r.E))
+ // RSA publickey struct layout should match the struct used by
+ // parseRSACert in the x/crypto/ssh/agent package.
+ wirekey := struct {
+ Name string
+ E *big.Int
+ N *big.Int
+ }{
+ KeyAlgoRSA,
+ e,
+ r.N,
+ }
+ return Marshal(&wirekey)
+}
+
+func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
+ if sig.Format != r.Type() {
+ return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type())
+ }
+ h := crypto.SHA1.New()
+ h.Write(data)
+ digest := h.Sum(nil)
+ return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
+}
+
+func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
+ return (*rsa.PublicKey)(r)
+}
+
+type dsaPublicKey dsa.PublicKey
+
+func (r *dsaPublicKey) Type() string {
+ return "ssh-dss"
+}
+
+// parseDSA parses an DSA key according to RFC 4253, section 6.6.
+func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
+ var w struct {
+ P, Q, G, Y *big.Int
+ Rest []byte `ssh:"rest"`
+ }
+ if err := Unmarshal(in, &w); err != nil {
+ return nil, nil, err
+ }
+
+ key := &dsaPublicKey{
+ Parameters: dsa.Parameters{
+ P: w.P,
+ Q: w.Q,
+ G: w.G,
+ },
+ Y: w.Y,
+ }
+ return key, w.Rest, nil
+}
+
+func (k *dsaPublicKey) Marshal() []byte {
+ // DSA publickey struct layout should match the struct used by
+ // parseDSACert in the x/crypto/ssh/agent package.
+ w := struct {
+ Name string
+ P, Q, G, Y *big.Int
+ }{
+ k.Type(),
+ k.P,
+ k.Q,
+ k.G,
+ k.Y,
+ }
+
+ return Marshal(&w)
+}
+
+func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error {
+ if sig.Format != k.Type() {
+ return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
+ }
+ h := crypto.SHA1.New()
+ h.Write(data)
+ digest := h.Sum(nil)
+
+ // Per RFC 4253, section 6.6,
+ // The value for 'dss_signature_blob' is encoded as a string containing
+ // r, followed by s (which are 160-bit integers, without lengths or
+ // padding, unsigned, and in network byte order).
+ // For DSS purposes, sig.Blob should be exactly 40 bytes in length.
+ if len(sig.Blob) != 40 {
+ return errors.New("ssh: DSA signature parse error")
+ }
+ r := new(big.Int).SetBytes(sig.Blob[:20])
+ s := new(big.Int).SetBytes(sig.Blob[20:])
+ if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) {
+ return nil
+ }
+ return errors.New("ssh: signature did not verify")
+}
+
+func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey {
+ return (*dsa.PublicKey)(k)
+}
+
+type dsaPrivateKey struct {
+ *dsa.PrivateKey
+}
+
+func (k *dsaPrivateKey) PublicKey() PublicKey {
+ return (*dsaPublicKey)(&k.PrivateKey.PublicKey)
+}
+
+func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
+ h := crypto.SHA1.New()
+ h.Write(data)
+ digest := h.Sum(nil)
+ r, s, err := dsa.Sign(rand, k.PrivateKey, digest)
+ if err != nil {
+ return nil, err
+ }
+
+ sig := make([]byte, 40)
+ rb := r.Bytes()
+ sb := s.Bytes()
+
+ copy(sig[20-len(rb):20], rb)
+ copy(sig[40-len(sb):], sb)
+
+ return &Signature{
+ Format: k.PublicKey().Type(),
+ Blob: sig,
+ }, nil
+}
+
+type ecdsaPublicKey ecdsa.PublicKey
+
+func (key *ecdsaPublicKey) Type() string {
+ return "ecdsa-sha2-" + key.nistID()
+}
+
+func (key *ecdsaPublicKey) nistID() string {
+ switch key.Params().BitSize {
+ case 256:
+ return "nistp256"
+ case 384:
+ return "nistp384"
+ case 521:
+ return "nistp521"
+ }
+ panic("ssh: unsupported ecdsa key size")
+}
+
+type ed25519PublicKey ed25519.PublicKey
+
+func (key ed25519PublicKey) Type() string {
+ return KeyAlgoED25519
+}
+
+func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
+ var w struct {
+ KeyBytes []byte
+ Rest []byte `ssh:"rest"`
+ }
+
+ if err := Unmarshal(in, &w); err != nil {
+ return nil, nil, err
+ }
+
+ key := ed25519.PublicKey(w.KeyBytes)
+
+ return (ed25519PublicKey)(key), w.Rest, nil
+}
+
+func (key ed25519PublicKey) Marshal() []byte {
+ w := struct {
+ Name string
+ KeyBytes []byte
+ }{
+ KeyAlgoED25519,
+ []byte(key),
+ }
+ return Marshal(&w)
+}
+
+func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error {
+ if sig.Format != key.Type() {
+ return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
+ }
+
+ edKey := (ed25519.PublicKey)(key)
+ if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
+ return errors.New("ssh: signature did not verify")
+ }
+
+ return nil
+}
+
+func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey {
+ return ed25519.PublicKey(k)
+}
+
+func supportedEllipticCurve(curve elliptic.Curve) bool {
+ return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
+}
+
+// ecHash returns the hash to match the given elliptic curve, see RFC
+// 5656, section 6.2.1
+func ecHash(curve elliptic.Curve) crypto.Hash {
+ bitSize := curve.Params().BitSize
+ switch {
+ case bitSize <= 256:
+ return crypto.SHA256
+ case bitSize <= 384:
+ return crypto.SHA384
+ }
+ return crypto.SHA512
+}
+
+// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
+func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
+ var w struct {
+ Curve string
+ KeyBytes []byte
+ Rest []byte `ssh:"rest"`
+ }
+
+ if err := Unmarshal(in, &w); err != nil {
+ return nil, nil, err
+ }
+
+ key := new(ecdsa.PublicKey)
+
+ switch w.Curve {
+ case "nistp256":
+ key.Curve = elliptic.P256()
+ case "nistp384":
+ key.Curve = elliptic.P384()
+ case "nistp521":
+ key.Curve = elliptic.P521()
+ default:
+ return nil, nil, errors.New("ssh: unsupported curve")
+ }
+
+ key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes)
+ if key.X == nil || key.Y == nil {
+ return nil, nil, errors.New("ssh: invalid curve point")
+ }
+ return (*ecdsaPublicKey)(key), w.Rest, nil
+}
+
+func (key *ecdsaPublicKey) Marshal() []byte {
+ // See RFC 5656, section 3.1.
+ keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
+ // ECDSA publickey struct layout should match the struct used by
+ // parseECDSACert in the x/crypto/ssh/agent package.
+ w := struct {
+ Name string
+ ID string
+ Key []byte
+ }{
+ key.Type(),
+ key.nistID(),
+ keyBytes,
+ }
+
+ return Marshal(&w)
+}
+
+func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
+ if sig.Format != key.Type() {
+ return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
+ }
+
+ h := ecHash(key.Curve).New()
+ h.Write(data)
+ digest := h.Sum(nil)
+
+ // Per RFC 5656, section 3.1.2,
+ // The ecdsa_signature_blob value has the following specific encoding:
+ // mpint r
+ // mpint s
+ var ecSig struct {
+ R *big.Int
+ S *big.Int
+ }
+
+ if err := Unmarshal(sig.Blob, &ecSig); err != nil {
+ return err
+ }
+
+ if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) {
+ return nil
+ }
+ return errors.New("ssh: signature did not verify")
+}
+
+func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
+ return (*ecdsa.PublicKey)(k)
+}
+
+// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
+// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding
+// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
+func NewSignerFromKey(key interface{}) (Signer, error) {
+ switch key := key.(type) {
+ case crypto.Signer:
+ return NewSignerFromSigner(key)
+ case *dsa.PrivateKey:
+ return &dsaPrivateKey{key}, nil
+ default:
+ return nil, fmt.Errorf("ssh: unsupported key type %T", key)
+ }
+}
+
+type wrappedSigner struct {
+ signer crypto.Signer
+ pubKey PublicKey
+}
+
+// NewSignerFromSigner takes any crypto.Signer implementation and
+// returns a corresponding Signer interface. This can be used, for
+// example, with keys kept in hardware modules.
+func NewSignerFromSigner(signer crypto.Signer) (Signer, error) {
+ pubKey, err := NewPublicKey(signer.Public())
+ if err != nil {
+ return nil, err
+ }
+
+ return &wrappedSigner{signer, pubKey}, nil
+}
+
+func (s *wrappedSigner) PublicKey() PublicKey {
+ return s.pubKey
+}
+
+func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
+ var hashFunc crypto.Hash
+
+ switch key := s.pubKey.(type) {
+ case *rsaPublicKey, *dsaPublicKey:
+ hashFunc = crypto.SHA1
+ case *ecdsaPublicKey:
+ hashFunc = ecHash(key.Curve)
+ case ed25519PublicKey:
+ default:
+ return nil, fmt.Errorf("ssh: unsupported key type %T", key)
+ }
+
+ var digest []byte
+ if hashFunc != 0 {
+ h := hashFunc.New()
+ h.Write(data)
+ digest = h.Sum(nil)
+ } else {
+ digest = data
+ }
+
+ signature, err := s.signer.Sign(rand, digest, hashFunc)
+ if err != nil {
+ return nil, err
+ }
+
+ // crypto.Signer.Sign is expected to return an ASN.1-encoded signature
+ // for ECDSA and DSA, but that's not the encoding expected by SSH, so
+ // re-encode.
+ switch s.pubKey.(type) {
+ case *ecdsaPublicKey, *dsaPublicKey:
+ type asn1Signature struct {
+ R, S *big.Int
+ }
+ asn1Sig := new(asn1Signature)
+ _, err := asn1.Unmarshal(signature, asn1Sig)
+ if err != nil {
+ return nil, err
+ }
+
+ switch s.pubKey.(type) {
+ case *ecdsaPublicKey:
+ signature = Marshal(asn1Sig)
+
+ case *dsaPublicKey:
+ signature = make([]byte, 40)
+ r := asn1Sig.R.Bytes()
+ s := asn1Sig.S.Bytes()
+ copy(signature[20-len(r):20], r)
+ copy(signature[40-len(s):40], s)
+ }
+ }
+
+ return &Signature{
+ Format: s.pubKey.Type(),
+ Blob: signature,
+ }, nil
+}
+
+// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
+// or ed25519.PublicKey returns a corresponding PublicKey instance.
+// ECDSA keys must use P-256, P-384 or P-521.
+func NewPublicKey(key interface{}) (PublicKey, error) {
+ switch key := key.(type) {
+ case *rsa.PublicKey:
+ return (*rsaPublicKey)(key), nil
+ case *ecdsa.PublicKey:
+ if !supportedEllipticCurve(key.Curve) {
+ return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.")
+ }
+ return (*ecdsaPublicKey)(key), nil
+ case *dsa.PublicKey:
+ return (*dsaPublicKey)(key), nil
+ case ed25519.PublicKey:
+ return (ed25519PublicKey)(key), nil
+ default:
+ return nil, fmt.Errorf("ssh: unsupported key type %T", key)
+ }
+}
+
+// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports
+// the same keys as ParseRawPrivateKey.
+func ParsePrivateKey(pemBytes []byte) (Signer, error) {
+ key, err := ParseRawPrivateKey(pemBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewSignerFromKey(key)
+}
+
+// encryptedBlock tells whether a private key is
+// encrypted by examining its Proc-Type header
+// for a mention of ENCRYPTED
+// according to RFC 1421 Section 4.6.1.1.
+func encryptedBlock(block *pem.Block) bool {
+ return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED")
+}
+
+// ParseRawPrivateKey returns a private key from a PEM encoded private key. It
+// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
+func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
+ block, _ := pem.Decode(pemBytes)
+ if block == nil {
+ return nil, errors.New("ssh: no key found")
+ }
+
+ if encryptedBlock(block) {
+ return nil, errors.New("ssh: cannot decode encrypted private keys")
+ }
+
+ switch block.Type {
+ case "RSA PRIVATE KEY":
+ return x509.ParsePKCS1PrivateKey(block.Bytes)
+ case "EC PRIVATE KEY":
+ return x509.ParseECPrivateKey(block.Bytes)
+ case "DSA PRIVATE KEY":
+ return ParseDSAPrivateKey(block.Bytes)
+ case "OPENSSH PRIVATE KEY":
+ return parseOpenSSHPrivateKey(block.Bytes)
+ default:
+ return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
+ }
+}
+
+// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
+// specified by the OpenSSL DSA man page.
+func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
+ var k struct {
+ Version int
+ P *big.Int
+ Q *big.Int
+ G *big.Int
+ Pub *big.Int
+ Priv *big.Int
+ }
+ rest, err := asn1.Unmarshal(der, &k)
+ if err != nil {
+ return nil, errors.New("ssh: failed to parse DSA key: " + err.Error())
+ }
+ if len(rest) > 0 {
+ return nil, errors.New("ssh: garbage after DSA key")
+ }
+
+ return &dsa.PrivateKey{
+ PublicKey: dsa.PublicKey{
+ Parameters: dsa.Parameters{
+ P: k.P,
+ Q: k.Q,
+ G: k.G,
+ },
+ Y: k.Pub,
+ },
+ X: k.Priv,
+ }, nil
+}
+
+// Implemented based on the documentation at
+// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
+func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
+ magic := append([]byte("openssh-key-v1"), 0)
+ if !bytes.Equal(magic, key[0:len(magic)]) {
+ return nil, errors.New("ssh: invalid openssh private key format")
+ }
+ remaining := key[len(magic):]
+
+ var w struct {
+ CipherName string
+ KdfName string
+ KdfOpts string
+ NumKeys uint32
+ PubKey []byte
+ PrivKeyBlock []byte
+ }
+
+ if err := Unmarshal(remaining, &w); err != nil {
+ return nil, err
+ }
+
+ pk1 := struct {
+ Check1 uint32
+ Check2 uint32
+ Keytype string
+ Pub []byte
+ Priv []byte
+ Comment string
+ Pad []byte `ssh:"rest"`
+ }{}
+
+ if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
+ return nil, err
+ }
+
+ if pk1.Check1 != pk1.Check2 {
+ return nil, errors.New("ssh: checkint mismatch")
+ }
+
+ // we only handle ed25519 keys currently
+ if pk1.Keytype != KeyAlgoED25519 {
+ return nil, errors.New("ssh: unhandled key type")
+ }
+
+ for i, b := range pk1.Pad {
+ if int(b) != i+1 {
+ return nil, errors.New("ssh: padding not as expected")
+ }
+ }
+
+ if len(pk1.Priv) != ed25519.PrivateKeySize {
+ return nil, errors.New("ssh: private key unexpected length")
+ }
+
+ pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
+ copy(pk, pk1.Priv)
+ return &pk, nil
+}
+
+// FingerprintLegacyMD5 returns the user presentation of the key's
+// fingerprint as described by RFC 4716 section 4.
+func FingerprintLegacyMD5(pubKey PublicKey) string {
+ md5sum := md5.Sum(pubKey.Marshal())
+ hexarray := make([]string, len(md5sum))
+ for i, c := range md5sum {
+ hexarray[i] = hex.EncodeToString([]byte{c})
+ }
+ return strings.Join(hexarray, ":")
+}
+
+// FingerprintSHA256 returns the user presentation of the key's
+// fingerprint as unpadded base64 encoded sha256 hash.
+// This format was introduced from OpenSSH 6.8.
+// https://www.openssh.com/txt/release-6.8
+// https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding)
+func FingerprintSHA256(pubKey PublicKey) string {
+ sha256sum := sha256.Sum256(pubKey.Marshal())
+ hash := base64.RawStdEncoding.EncodeToString(sha256sum[:])
+ return "SHA256:" + hash
+}
diff --git a/vendor/golang.org/x/crypto/ssh/mac.go b/vendor/golang.org/x/crypto/ssh/mac.go
new file mode 100644
index 000000000..c07a06285
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/mac.go
@@ -0,0 +1,61 @@
+// Copyright 2012 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 ssh
+
+// Message authentication support
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "crypto/sha256"
+ "hash"
+)
+
+type macMode struct {
+ keySize int
+ etm bool
+ new func(key []byte) hash.Hash
+}
+
+// truncatingMAC wraps around a hash.Hash and truncates the output digest to
+// a given size.
+type truncatingMAC struct {
+ length int
+ hmac hash.Hash
+}
+
+func (t truncatingMAC) Write(data []byte) (int, error) {
+ return t.hmac.Write(data)
+}
+
+func (t truncatingMAC) Sum(in []byte) []byte {
+ out := t.hmac.Sum(in)
+ return out[:len(in)+t.length]
+}
+
+func (t truncatingMAC) Reset() {
+ t.hmac.Reset()
+}
+
+func (t truncatingMAC) Size() int {
+ return t.length
+}
+
+func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
+
+var macModes = map[string]*macMode{
+ "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
+ return hmac.New(sha256.New, key)
+ }},
+ "hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
+ return hmac.New(sha256.New, key)
+ }},
+ "hmac-sha1": {20, false, func(key []byte) hash.Hash {
+ return hmac.New(sha1.New, key)
+ }},
+ "hmac-sha1-96": {20, false, func(key []byte) hash.Hash {
+ return truncatingMAC{12, hmac.New(sha1.New, key)}
+ }},
+}
diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go
new file mode 100644
index 000000000..e6ecd3afa
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/messages.go
@@ -0,0 +1,758 @@
+// Copyright 2011 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 ssh
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// These are SSH message type numbers. They are scattered around several
+// documents but many were taken from [SSH-PARAMETERS].
+const (
+ msgIgnore = 2
+ msgUnimplemented = 3
+ msgDebug = 4
+ msgNewKeys = 21
+
+ // Standard authentication messages
+ msgUserAuthSuccess = 52
+ msgUserAuthBanner = 53
+)
+
+// SSH messages:
+//
+// These structures mirror the wire format of the corresponding SSH messages.
+// They are marshaled using reflection with the marshal and unmarshal functions
+// in this file. The only wrinkle is that a final member of type []byte with a
+// ssh tag of "rest" receives the remainder of a packet when unmarshaling.
+
+// See RFC 4253, section 11.1.
+const msgDisconnect = 1
+
+// disconnectMsg is the message that signals a disconnect. It is also
+// the error type returned from mux.Wait()
+type disconnectMsg struct {
+ Reason uint32 `sshtype:"1"`
+ Message string
+ Language string
+}
+
+func (d *disconnectMsg) Error() string {
+ return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
+}
+
+// See RFC 4253, section 7.1.
+const msgKexInit = 20
+
+type kexInitMsg struct {
+ Cookie [16]byte `sshtype:"20"`
+ KexAlgos []string
+ ServerHostKeyAlgos []string
+ CiphersClientServer []string
+ CiphersServerClient []string
+ MACsClientServer []string
+ MACsServerClient []string
+ CompressionClientServer []string
+ CompressionServerClient []string
+ LanguagesClientServer []string
+ LanguagesServerClient []string
+ FirstKexFollows bool
+ Reserved uint32
+}
+
+// See RFC 4253, section 8.
+
+// Diffie-Helman
+const msgKexDHInit = 30
+
+type kexDHInitMsg struct {
+ X *big.Int `sshtype:"30"`
+}
+
+const msgKexECDHInit = 30
+
+type kexECDHInitMsg struct {
+ ClientPubKey []byte `sshtype:"30"`
+}
+
+const msgKexECDHReply = 31
+
+type kexECDHReplyMsg struct {
+ HostKey []byte `sshtype:"31"`
+ EphemeralPubKey []byte
+ Signature []byte
+}
+
+const msgKexDHReply = 31
+
+type kexDHReplyMsg struct {
+ HostKey []byte `sshtype:"31"`
+ Y *big.Int
+ Signature []byte
+}
+
+// See RFC 4253, section 10.
+const msgServiceRequest = 5
+
+type serviceRequestMsg struct {
+ Service string `sshtype:"5"`
+}
+
+// See RFC 4253, section 10.
+const msgServiceAccept = 6
+
+type serviceAcceptMsg struct {
+ Service string `sshtype:"6"`
+}
+
+// See RFC 4252, section 5.
+const msgUserAuthRequest = 50
+
+type userAuthRequestMsg struct {
+ User string `sshtype:"50"`
+ Service string
+ Method string
+ Payload []byte `ssh:"rest"`
+}
+
+// Used for debug printouts of packets.
+type userAuthSuccessMsg struct {
+}
+
+// See RFC 4252, section 5.1
+const msgUserAuthFailure = 51
+
+type userAuthFailureMsg struct {
+ Methods []string `sshtype:"51"`
+ PartialSuccess bool
+}
+
+// See RFC 4256, section 3.2
+const msgUserAuthInfoRequest = 60
+const msgUserAuthInfoResponse = 61
+
+type userAuthInfoRequestMsg struct {
+ User string `sshtype:"60"`
+ Instruction string
+ DeprecatedLanguage string
+ NumPrompts uint32
+ Prompts []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.1.
+const msgChannelOpen = 90
+
+type channelOpenMsg struct {
+ ChanType string `sshtype:"90"`
+ PeersId uint32
+ PeersWindow uint32
+ MaxPacketSize uint32
+ TypeSpecificData []byte `ssh:"rest"`
+}
+
+const msgChannelExtendedData = 95
+const msgChannelData = 94
+
+// Used for debug print outs of packets.
+type channelDataMsg struct {
+ PeersId uint32 `sshtype:"94"`
+ Length uint32
+ Rest []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.1.
+const msgChannelOpenConfirm = 91
+
+type channelOpenConfirmMsg struct {
+ PeersId uint32 `sshtype:"91"`
+ MyId uint32
+ MyWindow uint32
+ MaxPacketSize uint32
+ TypeSpecificData []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.1.
+const msgChannelOpenFailure = 92
+
+type channelOpenFailureMsg struct {
+ PeersId uint32 `sshtype:"92"`
+ Reason RejectionReason
+ Message string
+ Language string
+}
+
+const msgChannelRequest = 98
+
+type channelRequestMsg struct {
+ PeersId uint32 `sshtype:"98"`
+ Request string
+ WantReply bool
+ RequestSpecificData []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.4.
+const msgChannelSuccess = 99
+
+type channelRequestSuccessMsg struct {
+ PeersId uint32 `sshtype:"99"`
+}
+
+// See RFC 4254, section 5.4.
+const msgChannelFailure = 100
+
+type channelRequestFailureMsg struct {
+ PeersId uint32 `sshtype:"100"`
+}
+
+// See RFC 4254, section 5.3
+const msgChannelClose = 97
+
+type channelCloseMsg struct {
+ PeersId uint32 `sshtype:"97"`
+}
+
+// See RFC 4254, section 5.3
+const msgChannelEOF = 96
+
+type channelEOFMsg struct {
+ PeersId uint32 `sshtype:"96"`
+}
+
+// See RFC 4254, section 4
+const msgGlobalRequest = 80
+
+type globalRequestMsg struct {
+ Type string `sshtype:"80"`
+ WantReply bool
+ Data []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 4
+const msgRequestSuccess = 81
+
+type globalRequestSuccessMsg struct {
+ Data []byte `ssh:"rest" sshtype:"81"`
+}
+
+// See RFC 4254, section 4
+const msgRequestFailure = 82
+
+type globalRequestFailureMsg struct {
+ Data []byte `ssh:"rest" sshtype:"82"`
+}
+
+// See RFC 4254, section 5.2
+const msgChannelWindowAdjust = 93
+
+type windowAdjustMsg struct {
+ PeersId uint32 `sshtype:"93"`
+ AdditionalBytes uint32
+}
+
+// See RFC 4252, section 7
+const msgUserAuthPubKeyOk = 60
+
+type userAuthPubKeyOkMsg struct {
+ Algo string `sshtype:"60"`
+ PubKey []byte
+}
+
+// typeTags returns the possible type bytes for the given reflect.Type, which
+// should be a struct. The possible values are separated by a '|' character.
+func typeTags(structType reflect.Type) (tags []byte) {
+ tagStr := structType.Field(0).Tag.Get("sshtype")
+
+ for _, tag := range strings.Split(tagStr, "|") {
+ i, err := strconv.Atoi(tag)
+ if err == nil {
+ tags = append(tags, byte(i))
+ }
+ }
+
+ return tags
+}
+
+func fieldError(t reflect.Type, field int, problem string) error {
+ if problem != "" {
+ problem = ": " + problem
+ }
+ return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem)
+}
+
+var errShortRead = errors.New("ssh: short read")
+
+// Unmarshal parses data in SSH wire format into a structure. The out
+// argument should be a pointer to struct. If the first member of the
+// struct has the "sshtype" tag set to a '|'-separated set of numbers
+// in decimal, the packet must start with one of those numbers. In
+// case of error, Unmarshal returns a ParseError or
+// UnexpectedMessageError.
+func Unmarshal(data []byte, out interface{}) error {
+ v := reflect.ValueOf(out).Elem()
+ structType := v.Type()
+ expectedTypes := typeTags(structType)
+
+ var expectedType byte
+ if len(expectedTypes) > 0 {
+ expectedType = expectedTypes[0]
+ }
+
+ if len(data) == 0 {
+ return parseError(expectedType)
+ }
+
+ if len(expectedTypes) > 0 {
+ goodType := false
+ for _, e := range expectedTypes {
+ if e > 0 && data[0] == e {
+ goodType = true
+ break
+ }
+ }
+ if !goodType {
+ return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes)
+ }
+ data = data[1:]
+ }
+
+ var ok bool
+ for i := 0; i < v.NumField(); i++ {
+ field := v.Field(i)
+ t := field.Type()
+ switch t.Kind() {
+ case reflect.Bool:
+ if len(data) < 1 {
+ return errShortRead
+ }
+ field.SetBool(data[0] != 0)
+ data = data[1:]
+ case reflect.Array:
+ if t.Elem().Kind() != reflect.Uint8 {
+ return fieldError(structType, i, "array of unsupported type")
+ }
+ if len(data) < t.Len() {
+ return errShortRead
+ }
+ for j, n := 0, t.Len(); j < n; j++ {
+ field.Index(j).Set(reflect.ValueOf(data[j]))
+ }
+ data = data[t.Len():]
+ case reflect.Uint64:
+ var u64 uint64
+ if u64, data, ok = parseUint64(data); !ok {
+ return errShortRead
+ }
+ field.SetUint(u64)
+ case reflect.Uint32:
+ var u32 uint32
+ if u32, data, ok = parseUint32(data); !ok {
+ return errShortRead
+ }
+ field.SetUint(uint64(u32))
+ case reflect.Uint8:
+ if len(data) < 1 {
+ return errShortRead
+ }
+ field.SetUint(uint64(data[0]))
+ data = data[1:]
+ case reflect.String:
+ var s []byte
+ if s, data, ok = parseString(data); !ok {
+ return fieldError(structType, i, "")
+ }
+ field.SetString(string(s))
+ case reflect.Slice:
+ switch t.Elem().Kind() {
+ case reflect.Uint8:
+ if structType.Field(i).Tag.Get("ssh") == "rest" {
+ field.Set(reflect.ValueOf(data))
+ data = nil
+ } else {
+ var s []byte
+ if s, data, ok = parseString(data); !ok {
+ return errShortRead
+ }
+ field.Set(reflect.ValueOf(s))
+ }
+ case reflect.String:
+ var nl []string
+ if nl, data, ok = parseNameList(data); !ok {
+ return errShortRead
+ }
+ field.Set(reflect.ValueOf(nl))
+ default:
+ return fieldError(structType, i, "slice of unsupported type")
+ }
+ case reflect.Ptr:
+ if t == bigIntType {
+ var n *big.Int
+ if n, data, ok = parseInt(data); !ok {
+ return errShortRead
+ }
+ field.Set(reflect.ValueOf(n))
+ } else {
+ return fieldError(structType, i, "pointer to unsupported type")
+ }
+ default:
+ return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t))
+ }
+ }
+
+ if len(data) != 0 {
+ return parseError(expectedType)
+ }
+
+ return nil
+}
+
+// Marshal serializes the message in msg to SSH wire format. The msg
+// argument should be a struct or pointer to struct. If the first
+// member has the "sshtype" tag set to a number in decimal, that
+// number is prepended to the result. If the last of member has the
+// "ssh" tag set to "rest", its contents are appended to the output.
+func Marshal(msg interface{}) []byte {
+ out := make([]byte, 0, 64)
+ return marshalStruct(out, msg)
+}
+
+func marshalStruct(out []byte, msg interface{}) []byte {
+ v := reflect.Indirect(reflect.ValueOf(msg))
+ msgTypes := typeTags(v.Type())
+ if len(msgTypes) > 0 {
+ out = append(out, msgTypes[0])
+ }
+
+ for i, n := 0, v.NumField(); i < n; i++ {
+ field := v.Field(i)
+ switch t := field.Type(); t.Kind() {
+ case reflect.Bool:
+ var v uint8
+ if field.Bool() {
+ v = 1
+ }
+ out = append(out, v)
+ case reflect.Array:
+ if t.Elem().Kind() != reflect.Uint8 {
+ panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface()))
+ }
+ for j, l := 0, t.Len(); j < l; j++ {
+ out = append(out, uint8(field.Index(j).Uint()))
+ }
+ case reflect.Uint32:
+ out = appendU32(out, uint32(field.Uint()))
+ case reflect.Uint64:
+ out = appendU64(out, uint64(field.Uint()))
+ case reflect.Uint8:
+ out = append(out, uint8(field.Uint()))
+ case reflect.String:
+ s := field.String()
+ out = appendInt(out, len(s))
+ out = append(out, s...)
+ case reflect.Slice:
+ switch t.Elem().Kind() {
+ case reflect.Uint8:
+ if v.Type().Field(i).Tag.Get("ssh") != "rest" {
+ out = appendInt(out, field.Len())
+ }
+ out = append(out, field.Bytes()...)
+ case reflect.String:
+ offset := len(out)
+ out = appendU32(out, 0)
+ if n := field.Len(); n > 0 {
+ for j := 0; j < n; j++ {
+ f := field.Index(j)
+ if j != 0 {
+ out = append(out, ',')
+ }
+ out = append(out, f.String()...)
+ }
+ // overwrite length value
+ binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4))
+ }
+ default:
+ panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface()))
+ }
+ case reflect.Ptr:
+ if t == bigIntType {
+ var n *big.Int
+ nValue := reflect.ValueOf(&n)
+ nValue.Elem().Set(field)
+ needed := intLength(n)
+ oldLength := len(out)
+
+ if cap(out)-len(out) < needed {
+ newOut := make([]byte, len(out), 2*(len(out)+needed))
+ copy(newOut, out)
+ out = newOut
+ }
+ out = out[:oldLength+needed]
+ marshalInt(out[oldLength:], n)
+ } else {
+ panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface()))
+ }
+ }
+ }
+
+ return out
+}
+
+var bigOne = big.NewInt(1)
+
+func parseString(in []byte) (out, rest []byte, ok bool) {
+ if len(in) < 4 {
+ return
+ }
+ length := binary.BigEndian.Uint32(in)
+ in = in[4:]
+ if uint32(len(in)) < length {
+ return
+ }
+ out = in[:length]
+ rest = in[length:]
+ ok = true
+ return
+}
+
+var (
+ comma = []byte{','}
+ emptyNameList = []string{}
+)
+
+func parseNameList(in []byte) (out []string, rest []byte, ok bool) {
+ contents, rest, ok := parseString(in)
+ if !ok {
+ return
+ }
+ if len(contents) == 0 {
+ out = emptyNameList
+ return
+ }
+ parts := bytes.Split(contents, comma)
+ out = make([]string, len(parts))
+ for i, part := range parts {
+ out[i] = string(part)
+ }
+ return
+}
+
+func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) {
+ contents, rest, ok := parseString(in)
+ if !ok {
+ return
+ }
+ out = new(big.Int)
+
+ if len(contents) > 0 && contents[0]&0x80 == 0x80 {
+ // This is a negative number
+ notBytes := make([]byte, len(contents))
+ for i := range notBytes {
+ notBytes[i] = ^contents[i]
+ }
+ out.SetBytes(notBytes)
+ out.Add(out, bigOne)
+ out.Neg(out)
+ } else {
+ // Positive number
+ out.SetBytes(contents)
+ }
+ ok = true
+ return
+}
+
+func parseUint32(in []byte) (uint32, []byte, bool) {
+ if len(in) < 4 {
+ return 0, nil, false
+ }
+ return binary.BigEndian.Uint32(in), in[4:], true
+}
+
+func parseUint64(in []byte) (uint64, []byte, bool) {
+ if len(in) < 8 {
+ return 0, nil, false
+ }
+ return binary.BigEndian.Uint64(in), in[8:], true
+}
+
+func intLength(n *big.Int) int {
+ length := 4 /* length bytes */
+ if n.Sign() < 0 {
+ nMinus1 := new(big.Int).Neg(n)
+ nMinus1.Sub(nMinus1, bigOne)
+ bitLen := nMinus1.BitLen()
+ if bitLen%8 == 0 {
+ // The number will need 0xff padding
+ length++
+ }
+ length += (bitLen + 7) / 8
+ } else if n.Sign() == 0 {
+ // A zero is the zero length string
+ } else {
+ bitLen := n.BitLen()
+ if bitLen%8 == 0 {
+ // The number will need 0x00 padding
+ length++
+ }
+ length += (bitLen + 7) / 8
+ }
+
+ return length
+}
+
+func marshalUint32(to []byte, n uint32) []byte {
+ binary.BigEndian.PutUint32(to, n)
+ return to[4:]
+}
+
+func marshalUint64(to []byte, n uint64) []byte {
+ binary.BigEndian.PutUint64(to, n)
+ return to[8:]
+}
+
+func marshalInt(to []byte, n *big.Int) []byte {
+ lengthBytes := to
+ to = to[4:]
+ length := 0
+
+ if n.Sign() < 0 {
+ // A negative number has to be converted to two's-complement
+ // form. So we'll subtract 1 and invert. If the
+ // most-significant-bit isn't set then we'll need to pad the
+ // beginning with 0xff in order to keep the number negative.
+ nMinus1 := new(big.Int).Neg(n)
+ nMinus1.Sub(nMinus1, bigOne)
+ bytes := nMinus1.Bytes()
+ for i := range bytes {
+ bytes[i] ^= 0xff
+ }
+ if len(bytes) == 0 || bytes[0]&0x80 == 0 {
+ to[0] = 0xff
+ to = to[1:]
+ length++
+ }
+ nBytes := copy(to, bytes)
+ to = to[nBytes:]
+ length += nBytes
+ } else if n.Sign() == 0 {
+ // A zero is the zero length string
+ } else {
+ bytes := n.Bytes()
+ if len(bytes) > 0 && bytes[0]&0x80 != 0 {
+ // We'll have to pad this with a 0x00 in order to
+ // stop it looking like a negative number.
+ to[0] = 0
+ to = to[1:]
+ length++
+ }
+ nBytes := copy(to, bytes)
+ to = to[nBytes:]
+ length += nBytes
+ }
+
+ lengthBytes[0] = byte(length >> 24)
+ lengthBytes[1] = byte(length >> 16)
+ lengthBytes[2] = byte(length >> 8)
+ lengthBytes[3] = byte(length)
+ return to
+}
+
+func writeInt(w io.Writer, n *big.Int) {
+ length := intLength(n)
+ buf := make([]byte, length)
+ marshalInt(buf, n)
+ w.Write(buf)
+}
+
+func writeString(w io.Writer, s []byte) {
+ var lengthBytes [4]byte
+ lengthBytes[0] = byte(len(s) >> 24)
+ lengthBytes[1] = byte(len(s) >> 16)
+ lengthBytes[2] = byte(len(s) >> 8)
+ lengthBytes[3] = byte(len(s))
+ w.Write(lengthBytes[:])
+ w.Write(s)
+}
+
+func stringLength(n int) int {
+ return 4 + n
+}
+
+func marshalString(to []byte, s []byte) []byte {
+ to[0] = byte(len(s) >> 24)
+ to[1] = byte(len(s) >> 16)
+ to[2] = byte(len(s) >> 8)
+ to[3] = byte(len(s))
+ to = to[4:]
+ copy(to, s)
+ return to[len(s):]
+}
+
+var bigIntType = reflect.TypeOf((*big.Int)(nil))
+
+// Decode a packet into its corresponding message.
+func decode(packet []byte) (interface{}, error) {
+ var msg interface{}
+ switch packet[0] {
+ case msgDisconnect:
+ msg = new(disconnectMsg)
+ case msgServiceRequest:
+ msg = new(serviceRequestMsg)
+ case msgServiceAccept:
+ msg = new(serviceAcceptMsg)
+ case msgKexInit:
+ msg = new(kexInitMsg)
+ case msgKexDHInit:
+ msg = new(kexDHInitMsg)
+ case msgKexDHReply:
+ msg = new(kexDHReplyMsg)
+ case msgUserAuthRequest:
+ msg = new(userAuthRequestMsg)
+ case msgUserAuthSuccess:
+ return new(userAuthSuccessMsg), nil
+ case msgUserAuthFailure:
+ msg = new(userAuthFailureMsg)
+ case msgUserAuthPubKeyOk:
+ msg = new(userAuthPubKeyOkMsg)
+ case msgGlobalRequest:
+ msg = new(globalRequestMsg)
+ case msgRequestSuccess:
+ msg = new(globalRequestSuccessMsg)
+ case msgRequestFailure:
+ msg = new(globalRequestFailureMsg)
+ case msgChannelOpen:
+ msg = new(channelOpenMsg)
+ case msgChannelData:
+ msg = new(channelDataMsg)
+ case msgChannelOpenConfirm:
+ msg = new(channelOpenConfirmMsg)
+ case msgChannelOpenFailure:
+ msg = new(channelOpenFailureMsg)
+ case msgChannelWindowAdjust:
+ msg = new(windowAdjustMsg)
+ case msgChannelEOF:
+ msg = new(channelEOFMsg)
+ case msgChannelClose:
+ msg = new(channelCloseMsg)
+ case msgChannelRequest:
+ msg = new(channelRequestMsg)
+ case msgChannelSuccess:
+ msg = new(channelRequestSuccessMsg)
+ case msgChannelFailure:
+ msg = new(channelRequestFailureMsg)
+ default:
+ return nil, unexpectedMessageError(0, packet[0])
+ }
+ if err := Unmarshal(packet, msg); err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go
new file mode 100644
index 000000000..27a527c10
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/mux.go
@@ -0,0 +1,330 @@
+// Copyright 2013 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 ssh
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "log"
+ "sync"
+ "sync/atomic"
+)
+
+// debugMux, if set, causes messages in the connection protocol to be
+// logged.
+const debugMux = false
+
+// chanList is a thread safe channel list.
+type chanList struct {
+ // protects concurrent access to chans
+ sync.Mutex
+
+ // chans are indexed by the local id of the channel, which the
+ // other side should send in the PeersId field.
+ chans []*channel
+
+ // This is a debugging aid: it offsets all IDs by this
+ // amount. This helps distinguish otherwise identical
+ // server/client muxes
+ offset uint32
+}
+
+// Assigns a channel ID to the given channel.
+func (c *chanList) add(ch *channel) uint32 {
+ c.Lock()
+ defer c.Unlock()
+ for i := range c.chans {
+ if c.chans[i] == nil {
+ c.chans[i] = ch
+ return uint32(i) + c.offset
+ }
+ }
+ c.chans = append(c.chans, ch)
+ return uint32(len(c.chans)-1) + c.offset
+}
+
+// getChan returns the channel for the given ID.
+func (c *chanList) getChan(id uint32) *channel {
+ id -= c.offset
+
+ c.Lock()
+ defer c.Unlock()
+ if id < uint32(len(c.chans)) {
+ return c.chans[id]
+ }
+ return nil
+}
+
+func (c *chanList) remove(id uint32) {
+ id -= c.offset
+ c.Lock()
+ if id < uint32(len(c.chans)) {
+ c.chans[id] = nil
+ }
+ c.Unlock()
+}
+
+// dropAll forgets all channels it knows, returning them in a slice.
+func (c *chanList) dropAll() []*channel {
+ c.Lock()
+ defer c.Unlock()
+ var r []*channel
+
+ for _, ch := range c.chans {
+ if ch == nil {
+ continue
+ }
+ r = append(r, ch)
+ }
+ c.chans = nil
+ return r
+}
+
+// mux represents the state for the SSH connection protocol, which
+// multiplexes many channels onto a single packet transport.
+type mux struct {
+ conn packetConn
+ chanList chanList
+
+ incomingChannels chan NewChannel
+
+ globalSentMu sync.Mutex
+ globalResponses chan interface{}
+ incomingRequests chan *Request
+
+ errCond *sync.Cond
+ err error
+}
+
+// When debugging, each new chanList instantiation has a different
+// offset.
+var globalOff uint32
+
+func (m *mux) Wait() error {
+ m.errCond.L.Lock()
+ defer m.errCond.L.Unlock()
+ for m.err == nil {
+ m.errCond.Wait()
+ }
+ return m.err
+}
+
+// newMux returns a mux that runs over the given connection.
+func newMux(p packetConn) *mux {
+ m := &mux{
+ conn: p,
+ incomingChannels: make(chan NewChannel, chanSize),
+ globalResponses: make(chan interface{}, 1),
+ incomingRequests: make(chan *Request, chanSize),
+ errCond: newCond(),
+ }
+ if debugMux {
+ m.chanList.offset = atomic.AddUint32(&globalOff, 1)
+ }
+
+ go m.loop()
+ return m
+}
+
+func (m *mux) sendMessage(msg interface{}) error {
+ p := Marshal(msg)
+ if debugMux {
+ log.Printf("send global(%d): %#v", m.chanList.offset, msg)
+ }
+ return m.conn.writePacket(p)
+}
+
+func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) {
+ if wantReply {
+ m.globalSentMu.Lock()
+ defer m.globalSentMu.Unlock()
+ }
+
+ if err := m.sendMessage(globalRequestMsg{
+ Type: name,
+ WantReply: wantReply,
+ Data: payload,
+ }); err != nil {
+ return false, nil, err
+ }
+
+ if !wantReply {
+ return false, nil, nil
+ }
+
+ msg, ok := <-m.globalResponses
+ if !ok {
+ return false, nil, io.EOF
+ }
+ switch msg := msg.(type) {
+ case *globalRequestFailureMsg:
+ return false, msg.Data, nil
+ case *globalRequestSuccessMsg:
+ return true, msg.Data, nil
+ default:
+ return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg)
+ }
+}
+
+// ackRequest must be called after processing a global request that
+// has WantReply set.
+func (m *mux) ackRequest(ok bool, data []byte) error {
+ if ok {
+ return m.sendMessage(globalRequestSuccessMsg{Data: data})
+ }
+ return m.sendMessage(globalRequestFailureMsg{Data: data})
+}
+
+func (m *mux) Close() error {
+ return m.conn.Close()
+}
+
+// loop runs the connection machine. It will process packets until an
+// error is encountered. To synchronize on loop exit, use mux.Wait.
+func (m *mux) loop() {
+ var err error
+ for err == nil {
+ err = m.onePacket()
+ }
+
+ for _, ch := range m.chanList.dropAll() {
+ ch.close()
+ }
+
+ close(m.incomingChannels)
+ close(m.incomingRequests)
+ close(m.globalResponses)
+
+ m.conn.Close()
+
+ m.errCond.L.Lock()
+ m.err = err
+ m.errCond.Broadcast()
+ m.errCond.L.Unlock()
+
+ if debugMux {
+ log.Println("loop exit", err)
+ }
+}
+
+// onePacket reads and processes one packet.
+func (m *mux) onePacket() error {
+ packet, err := m.conn.readPacket()
+ if err != nil {
+ return err
+ }
+
+ if debugMux {
+ if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData {
+ log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet))
+ } else {
+ p, _ := decode(packet)
+ log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet))
+ }
+ }
+
+ switch packet[0] {
+ case msgChannelOpen:
+ return m.handleChannelOpen(packet)
+ case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
+ return m.handleGlobalPacket(packet)
+ }
+
+ // assume a channel packet.
+ if len(packet) < 5 {
+ return parseError(packet[0])
+ }
+ id := binary.BigEndian.Uint32(packet[1:])
+ ch := m.chanList.getChan(id)
+ if ch == nil {
+ return fmt.Errorf("ssh: invalid channel %d", id)
+ }
+
+ return ch.handlePacket(packet)
+}
+
+func (m *mux) handleGlobalPacket(packet []byte) error {
+ msg, err := decode(packet)
+ if err != nil {
+ return err
+ }
+
+ switch msg := msg.(type) {
+ case *globalRequestMsg:
+ m.incomingRequests <- &Request{
+ Type: msg.Type,
+ WantReply: msg.WantReply,
+ Payload: msg.Data,
+ mux: m,
+ }
+ case *globalRequestSuccessMsg, *globalRequestFailureMsg:
+ m.globalResponses <- msg
+ default:
+ panic(fmt.Sprintf("not a global message %#v", msg))
+ }
+
+ return nil
+}
+
+// handleChannelOpen schedules a channel to be Accept()ed.
+func (m *mux) handleChannelOpen(packet []byte) error {
+ var msg channelOpenMsg
+ if err := Unmarshal(packet, &msg); err != nil {
+ return err
+ }
+
+ if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
+ failMsg := channelOpenFailureMsg{
+ PeersId: msg.PeersId,
+ Reason: ConnectionFailed,
+ Message: "invalid request",
+ Language: "en_US.UTF-8",
+ }
+ return m.sendMessage(failMsg)
+ }
+
+ c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData)
+ c.remoteId = msg.PeersId
+ c.maxRemotePayload = msg.MaxPacketSize
+ c.remoteWin.add(msg.PeersWindow)
+ m.incomingChannels <- c
+ return nil
+}
+
+func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) {
+ ch, err := m.openChannel(chanType, extra)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return ch, ch.incomingRequests, nil
+}
+
+func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) {
+ ch := m.newChannel(chanType, channelOutbound, extra)
+
+ ch.maxIncomingPayload = channelMaxPacket
+
+ open := channelOpenMsg{
+ ChanType: chanType,
+ PeersWindow: ch.myWindow,
+ MaxPacketSize: ch.maxIncomingPayload,
+ TypeSpecificData: extra,
+ PeersId: ch.localId,
+ }
+ if err := m.sendMessage(open); err != nil {
+ return nil, err
+ }
+
+ switch msg := (<-ch.msg).(type) {
+ case *channelOpenConfirmMsg:
+ return ch, nil
+ case *channelOpenFailureMsg:
+ return nil, &OpenChannelError{msg.Reason, msg.Message}
+ default:
+ return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg)
+ }
+}
diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go
new file mode 100644
index 000000000..77c84d165
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/server.go
@@ -0,0 +1,491 @@
+// Copyright 2011 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 ssh
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+)
+
+// The Permissions type holds fine-grained permissions that are
+// specific to a user or a specific authentication method for a
+// user. Permissions, except for "source-address", must be enforced in
+// the server application layer, after successful authentication. The
+// Permissions are passed on in ServerConn so a server implementation
+// can honor them.
+type Permissions struct {
+ // Critical options restrict default permissions. Common
+ // restrictions are "source-address" and "force-command". If
+ // the server cannot enforce the restriction, or does not
+ // recognize it, the user should not authenticate.
+ CriticalOptions map[string]string
+
+ // Extensions are extra functionality that the server may
+ // offer on authenticated connections. Common extensions are
+ // "permit-agent-forwarding", "permit-X11-forwarding". Lack of
+ // support for an extension does not preclude authenticating a
+ // user.
+ Extensions map[string]string
+}
+
+// ServerConfig holds server specific configuration data.
+type ServerConfig struct {
+ // Config contains configuration shared between client and server.
+ Config
+
+ hostKeys []Signer
+
+ // NoClientAuth is true if clients are allowed to connect without
+ // authenticating.
+ NoClientAuth bool
+
+ // PasswordCallback, if non-nil, is called when a user
+ // attempts to authenticate using a password.
+ PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
+
+ // PublicKeyCallback, if non-nil, is called when a client attempts public
+ // key authentication. It must return true if the given public key is
+ // valid for the given user. For example, see CertChecker.Authenticate.
+ PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
+
+ // KeyboardInteractiveCallback, if non-nil, is called when
+ // keyboard-interactive authentication is selected (RFC
+ // 4256). The client object's Challenge function should be
+ // used to query the user. The callback may offer multiple
+ // Challenge rounds. To avoid information leaks, the client
+ // should be presented a challenge even if the user is
+ // unknown.
+ KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
+
+ // AuthLogCallback, if non-nil, is called to log all authentication
+ // attempts.
+ AuthLogCallback func(conn ConnMetadata, method string, err error)
+
+ // ServerVersion is the version identification string to announce in
+ // the public handshake.
+ // If empty, a reasonable default is used.
+ // Note that RFC 4253 section 4.2 requires that this string start with
+ // "SSH-2.0-".
+ ServerVersion string
+}
+
+// AddHostKey adds a private key as a host key. If an existing host
+// key exists with the same algorithm, it is overwritten. Each server
+// config must have at least one host key.
+func (s *ServerConfig) AddHostKey(key Signer) {
+ for i, k := range s.hostKeys {
+ if k.PublicKey().Type() == key.PublicKey().Type() {
+ s.hostKeys[i] = key
+ return
+ }
+ }
+
+ s.hostKeys = append(s.hostKeys, key)
+}
+
+// cachedPubKey contains the results of querying whether a public key is
+// acceptable for a user.
+type cachedPubKey struct {
+ user string
+ pubKeyData []byte
+ result error
+ perms *Permissions
+}
+
+const maxCachedPubKeys = 16
+
+// pubKeyCache caches tests for public keys. Since SSH clients
+// will query whether a public key is acceptable before attempting to
+// authenticate with it, we end up with duplicate queries for public
+// key validity. The cache only applies to a single ServerConn.
+type pubKeyCache struct {
+ keys []cachedPubKey
+}
+
+// get returns the result for a given user/algo/key tuple.
+func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
+ for _, k := range c.keys {
+ if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
+ return k, true
+ }
+ }
+ return cachedPubKey{}, false
+}
+
+// add adds the given tuple to the cache.
+func (c *pubKeyCache) add(candidate cachedPubKey) {
+ if len(c.keys) < maxCachedPubKeys {
+ c.keys = append(c.keys, candidate)
+ }
+}
+
+// ServerConn is an authenticated SSH connection, as seen from the
+// server
+type ServerConn struct {
+ Conn
+
+ // If the succeeding authentication callback returned a
+ // non-nil Permissions pointer, it is stored here.
+ Permissions *Permissions
+}
+
+// NewServerConn starts a new SSH server with c as the underlying
+// transport. It starts with a handshake and, if the handshake is
+// unsuccessful, it closes the connection and returns an error. The
+// Request and NewChannel channels must be serviced, or the connection
+// will hang.
+func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
+ fullConf := *config
+ fullConf.SetDefaults()
+ s := &connection{
+ sshConn: sshConn{conn: c},
+ }
+ perms, err := s.serverHandshake(&fullConf)
+ if err != nil {
+ c.Close()
+ return nil, nil, nil, err
+ }
+ return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
+}
+
+// signAndMarshal signs the data with the appropriate algorithm,
+// and serializes the result in SSH wire format.
+func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
+ sig, err := k.Sign(rand, data)
+ if err != nil {
+ return nil, err
+ }
+
+ return Marshal(sig), nil
+}
+
+// handshake performs key exchange and user authentication.
+func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
+ if len(config.hostKeys) == 0 {
+ return nil, errors.New("ssh: server has no host keys")
+ }
+
+ if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil {
+ return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
+ }
+
+ if config.ServerVersion != "" {
+ s.serverVersion = []byte(config.ServerVersion)
+ } else {
+ s.serverVersion = []byte(packageVersion)
+ }
+ var err error
+ s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
+ if err != nil {
+ return nil, err
+ }
+
+ tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
+ s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
+
+ if err := s.transport.waitSession(); err != nil {
+ return nil, err
+ }
+
+ // We just did the key change, so the session ID is established.
+ s.sessionID = s.transport.getSessionID()
+
+ var packet []byte
+ if packet, err = s.transport.readPacket(); err != nil {
+ return nil, err
+ }
+
+ var serviceRequest serviceRequestMsg
+ if err = Unmarshal(packet, &serviceRequest); err != nil {
+ return nil, err
+ }
+ if serviceRequest.Service != serviceUserAuth {
+ return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
+ }
+ serviceAccept := serviceAcceptMsg{
+ Service: serviceUserAuth,
+ }
+ if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
+ return nil, err
+ }
+
+ perms, err := s.serverAuthenticate(config)
+ if err != nil {
+ return nil, err
+ }
+ s.mux = newMux(s.transport)
+ return perms, err
+}
+
+func isAcceptableAlgo(algo string) bool {
+ switch algo {
+ case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
+ CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
+ return true
+ }
+ return false
+}
+
+func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
+ if addr == nil {
+ return errors.New("ssh: no address known for client, but source-address match required")
+ }
+
+ tcpAddr, ok := addr.(*net.TCPAddr)
+ if !ok {
+ return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
+ }
+
+ for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
+ if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
+ if allowedIP.Equal(tcpAddr.IP) {
+ return nil
+ }
+ } else {
+ _, ipNet, err := net.ParseCIDR(sourceAddr)
+ if err != nil {
+ return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
+ }
+
+ if ipNet.Contains(tcpAddr.IP) {
+ return nil
+ }
+ }
+ }
+
+ return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
+}
+
+func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
+ sessionID := s.transport.getSessionID()
+ var cache pubKeyCache
+ var perms *Permissions
+
+userAuthLoop:
+ for {
+ var userAuthReq userAuthRequestMsg
+ if packet, err := s.transport.readPacket(); err != nil {
+ return nil, err
+ } else if err = Unmarshal(packet, &userAuthReq); err != nil {
+ return nil, err
+ }
+
+ if userAuthReq.Service != serviceSSH {
+ return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
+ }
+
+ s.user = userAuthReq.User
+ perms = nil
+ authErr := errors.New("no auth passed yet")
+
+ switch userAuthReq.Method {
+ case "none":
+ if config.NoClientAuth {
+ authErr = nil
+ }
+ case "password":
+ if config.PasswordCallback == nil {
+ authErr = errors.New("ssh: password auth not configured")
+ break
+ }
+ payload := userAuthReq.Payload
+ if len(payload) < 1 || payload[0] != 0 {
+ return nil, parseError(msgUserAuthRequest)
+ }
+ payload = payload[1:]
+ password, payload, ok := parseString(payload)
+ if !ok || len(payload) > 0 {
+ return nil, parseError(msgUserAuthRequest)
+ }
+
+ perms, authErr = config.PasswordCallback(s, password)
+ case "keyboard-interactive":
+ if config.KeyboardInteractiveCallback == nil {
+ authErr = errors.New("ssh: keyboard-interactive auth not configubred")
+ break
+ }
+
+ prompter := &sshClientKeyboardInteractive{s}
+ perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
+ case "publickey":
+ if config.PublicKeyCallback == nil {
+ authErr = errors.New("ssh: publickey auth not configured")
+ break
+ }
+ payload := userAuthReq.Payload
+ if len(payload) < 1 {
+ return nil, parseError(msgUserAuthRequest)
+ }
+ isQuery := payload[0] == 0
+ payload = payload[1:]
+ algoBytes, payload, ok := parseString(payload)
+ if !ok {
+ return nil, parseError(msgUserAuthRequest)
+ }
+ algo := string(algoBytes)
+ if !isAcceptableAlgo(algo) {
+ authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
+ break
+ }
+
+ pubKeyData, payload, ok := parseString(payload)
+ if !ok {
+ return nil, parseError(msgUserAuthRequest)
+ }
+
+ pubKey, err := ParsePublicKey(pubKeyData)
+ if err != nil {
+ return nil, err
+ }
+
+ candidate, ok := cache.get(s.user, pubKeyData)
+ if !ok {
+ candidate.user = s.user
+ candidate.pubKeyData = pubKeyData
+ candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
+ if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
+ candidate.result = checkSourceAddress(
+ s.RemoteAddr(),
+ candidate.perms.CriticalOptions[sourceAddressCriticalOption])
+ }
+ cache.add(candidate)
+ }
+
+ if isQuery {
+ // The client can query if the given public key
+ // would be okay.
+ if len(payload) > 0 {
+ return nil, parseError(msgUserAuthRequest)
+ }
+
+ if candidate.result == nil {
+ okMsg := userAuthPubKeyOkMsg{
+ Algo: algo,
+ PubKey: pubKeyData,
+ }
+ if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
+ return nil, err
+ }
+ continue userAuthLoop
+ }
+ authErr = candidate.result
+ } else {
+ sig, payload, ok := parseSignature(payload)
+ if !ok || len(payload) > 0 {
+ return nil, parseError(msgUserAuthRequest)
+ }
+ // Ensure the public key algo and signature algo
+ // are supported. Compare the private key
+ // algorithm name that corresponds to algo with
+ // sig.Format. This is usually the same, but
+ // for certs, the names differ.
+ if !isAcceptableAlgo(sig.Format) {
+ break
+ }
+ signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData)
+
+ if err := pubKey.Verify(signedData, sig); err != nil {
+ return nil, err
+ }
+
+ authErr = candidate.result
+ perms = candidate.perms
+ }
+ default:
+ authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
+ }
+
+ if config.AuthLogCallback != nil {
+ config.AuthLogCallback(s, userAuthReq.Method, authErr)
+ }
+
+ if authErr == nil {
+ break userAuthLoop
+ }
+
+ var failureMsg userAuthFailureMsg
+ if config.PasswordCallback != nil {
+ failureMsg.Methods = append(failureMsg.Methods, "password")
+ }
+ if config.PublicKeyCallback != nil {
+ failureMsg.Methods = append(failureMsg.Methods, "publickey")
+ }
+ if config.KeyboardInteractiveCallback != nil {
+ failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
+ }
+
+ if len(failureMsg.Methods) == 0 {
+ return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
+ }
+
+ if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
+ return nil, err
+ }
+ return perms, nil
+}
+
+// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
+// asking the client on the other side of a ServerConn.
+type sshClientKeyboardInteractive struct {
+ *connection
+}
+
+func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
+ if len(questions) != len(echos) {
+ return nil, errors.New("ssh: echos and questions must have equal length")
+ }
+
+ var prompts []byte
+ for i := range questions {
+ prompts = appendString(prompts, questions[i])
+ prompts = appendBool(prompts, echos[i])
+ }
+
+ if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
+ Instruction: instruction,
+ NumPrompts: uint32(len(questions)),
+ Prompts: prompts,
+ })); err != nil {
+ return nil, err
+ }
+
+ packet, err := c.transport.readPacket()
+ if err != nil {
+ return nil, err
+ }
+ if packet[0] != msgUserAuthInfoResponse {
+ return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
+ }
+ packet = packet[1:]
+
+ n, packet, ok := parseUint32(packet)
+ if !ok || int(n) != len(questions) {
+ return nil, parseError(msgUserAuthInfoResponse)
+ }
+
+ for i := uint32(0); i < n; i++ {
+ ans, rest, ok := parseString(packet)
+ if !ok {
+ return nil, parseError(msgUserAuthInfoResponse)
+ }
+
+ answers = append(answers, string(ans))
+ packet = rest
+ }
+ if len(packet) != 0 {
+ return nil, errors.New("ssh: junk at end of message")
+ }
+
+ return answers, nil
+}
diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go
new file mode 100644
index 000000000..17e2aa85c
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/session.go
@@ -0,0 +1,627 @@
+// Copyright 2011 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 ssh
+
+// Session implements an interactive session described in
+// "RFC 4254, section 6".
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "sync"
+)
+
+type Signal string
+
+// POSIX signals as listed in RFC 4254 Section 6.10.
+const (
+ SIGABRT Signal = "ABRT"
+ SIGALRM Signal = "ALRM"
+ SIGFPE Signal = "FPE"
+ SIGHUP Signal = "HUP"
+ SIGILL Signal = "ILL"
+ SIGINT Signal = "INT"
+ SIGKILL Signal = "KILL"
+ SIGPIPE Signal = "PIPE"
+ SIGQUIT Signal = "QUIT"
+ SIGSEGV Signal = "SEGV"
+ SIGTERM Signal = "TERM"
+ SIGUSR1 Signal = "USR1"
+ SIGUSR2 Signal = "USR2"
+)
+
+var signals = map[Signal]int{
+ SIGABRT: 6,
+ SIGALRM: 14,
+ SIGFPE: 8,
+ SIGHUP: 1,
+ SIGILL: 4,
+ SIGINT: 2,
+ SIGKILL: 9,
+ SIGPIPE: 13,
+ SIGQUIT: 3,
+ SIGSEGV: 11,
+ SIGTERM: 15,
+}
+
+type TerminalModes map[uint8]uint32
+
+// POSIX terminal mode flags as listed in RFC 4254 Section 8.
+const (
+ tty_OP_END = 0
+ VINTR = 1
+ VQUIT = 2
+ VERASE = 3
+ VKILL = 4
+ VEOF = 5
+ VEOL = 6
+ VEOL2 = 7
+ VSTART = 8
+ VSTOP = 9
+ VSUSP = 10
+ VDSUSP = 11
+ VREPRINT = 12
+ VWERASE = 13
+ VLNEXT = 14
+ VFLUSH = 15
+ VSWTCH = 16
+ VSTATUS = 17
+ VDISCARD = 18
+ IGNPAR = 30
+ PARMRK = 31
+ INPCK = 32
+ ISTRIP = 33
+ INLCR = 34
+ IGNCR = 35
+ ICRNL = 36
+ IUCLC = 37
+ IXON = 38
+ IXANY = 39
+ IXOFF = 40
+ IMAXBEL = 41
+ ISIG = 50
+ ICANON = 51
+ XCASE = 52
+ ECHO = 53
+ ECHOE = 54
+ ECHOK = 55
+ ECHONL = 56
+ NOFLSH = 57
+ TOSTOP = 58
+ IEXTEN = 59
+ ECHOCTL = 60
+ ECHOKE = 61
+ PENDIN = 62
+ OPOST = 70
+ OLCUC = 71
+ ONLCR = 72
+ OCRNL = 73
+ ONOCR = 74
+ ONLRET = 75
+ CS7 = 90
+ CS8 = 91
+ PARENB = 92
+ PARODD = 93
+ TTY_OP_ISPEED = 128
+ TTY_OP_OSPEED = 129
+)
+
+// A Session represents a connection to a remote command or shell.
+type Session struct {
+ // Stdin specifies the remote process's standard input.
+ // If Stdin is nil, the remote process reads from an empty
+ // bytes.Buffer.
+ Stdin io.Reader
+
+ // Stdout and Stderr specify the remote process's standard
+ // output and error.
+ //
+ // If either is nil, Run connects the corresponding file
+ // descriptor to an instance of ioutil.Discard. There is a
+ // fixed amount of buffering that is shared for the two streams.
+ // If either blocks it may eventually cause the remote
+ // command to block.
+ Stdout io.Writer
+ Stderr io.Writer
+
+ ch Channel // the channel backing this session
+ started bool // true once Start, Run or Shell is invoked.
+ copyFuncs []func() error
+ errors chan error // one send per copyFunc
+
+ // true if pipe method is active
+ stdinpipe, stdoutpipe, stderrpipe bool
+
+ // stdinPipeWriter is non-nil if StdinPipe has not been called
+ // and Stdin was specified by the user; it is the write end of
+ // a pipe connecting Session.Stdin to the stdin channel.
+ stdinPipeWriter io.WriteCloser
+
+ exitStatus chan error
+}
+
+// SendRequest sends an out-of-band channel request on the SSH channel
+// underlying the session.
+func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
+ return s.ch.SendRequest(name, wantReply, payload)
+}
+
+func (s *Session) Close() error {
+ return s.ch.Close()
+}
+
+// RFC 4254 Section 6.4.
+type setenvRequest struct {
+ Name string
+ Value string
+}
+
+// Setenv sets an environment variable that will be applied to any
+// command executed by Shell or Run.
+func (s *Session) Setenv(name, value string) error {
+ msg := setenvRequest{
+ Name: name,
+ Value: value,
+ }
+ ok, err := s.ch.SendRequest("env", true, Marshal(&msg))
+ if err == nil && !ok {
+ err = errors.New("ssh: setenv failed")
+ }
+ return err
+}
+
+// RFC 4254 Section 6.2.
+type ptyRequestMsg struct {
+ Term string
+ Columns uint32
+ Rows uint32
+ Width uint32
+ Height uint32
+ Modelist string
+}
+
+// RequestPty requests the association of a pty with the session on the remote host.
+func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error {
+ var tm []byte
+ for k, v := range termmodes {
+ kv := struct {
+ Key byte
+ Val uint32
+ }{k, v}
+
+ tm = append(tm, Marshal(&kv)...)
+ }
+ tm = append(tm, tty_OP_END)
+ req := ptyRequestMsg{
+ Term: term,
+ Columns: uint32(w),
+ Rows: uint32(h),
+ Width: uint32(w * 8),
+ Height: uint32(h * 8),
+ Modelist: string(tm),
+ }
+ ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req))
+ if err == nil && !ok {
+ err = errors.New("ssh: pty-req failed")
+ }
+ return err
+}
+
+// RFC 4254 Section 6.5.
+type subsystemRequestMsg struct {
+ Subsystem string
+}
+
+// RequestSubsystem requests the association of a subsystem with the session on the remote host.
+// A subsystem is a predefined command that runs in the background when the ssh session is initiated
+func (s *Session) RequestSubsystem(subsystem string) error {
+ msg := subsystemRequestMsg{
+ Subsystem: subsystem,
+ }
+ ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg))
+ if err == nil && !ok {
+ err = errors.New("ssh: subsystem request failed")
+ }
+ return err
+}
+
+// RFC 4254 Section 6.9.
+type signalMsg struct {
+ Signal string
+}
+
+// Signal sends the given signal to the remote process.
+// sig is one of the SIG* constants.
+func (s *Session) Signal(sig Signal) error {
+ msg := signalMsg{
+ Signal: string(sig),
+ }
+
+ _, err := s.ch.SendRequest("signal", false, Marshal(&msg))
+ return err
+}
+
+// RFC 4254 Section 6.5.
+type execMsg struct {
+ Command string
+}
+
+// Start runs cmd on the remote host. Typically, the remote
+// server passes cmd to the shell for interpretation.
+// A Session only accepts one call to Run, Start or Shell.
+func (s *Session) Start(cmd string) error {
+ if s.started {
+ return errors.New("ssh: session already started")
+ }
+ req := execMsg{
+ Command: cmd,
+ }
+
+ ok, err := s.ch.SendRequest("exec", true, Marshal(&req))
+ if err == nil && !ok {
+ err = fmt.Errorf("ssh: command %v failed", cmd)
+ }
+ if err != nil {
+ return err
+ }
+ return s.start()
+}
+
+// Run runs cmd on the remote host. Typically, the remote
+// server passes cmd to the shell for interpretation.
+// A Session only accepts one call to Run, Start, Shell, Output,
+// or CombinedOutput.
+//
+// The returned error is nil if the command runs, has no problems
+// copying stdin, stdout, and stderr, and exits with a zero exit
+// status.
+//
+// If the remote server does not send an exit status, an error of type
+// *ExitMissingError is returned. If the command completes
+// unsuccessfully or is interrupted by a signal, the error is of type
+// *ExitError. Other error types may be returned for I/O problems.
+func (s *Session) Run(cmd string) error {
+ err := s.Start(cmd)
+ if err != nil {
+ return err
+ }
+ return s.Wait()
+}
+
+// Output runs cmd on the remote host and returns its standard output.
+func (s *Session) Output(cmd string) ([]byte, error) {
+ if s.Stdout != nil {
+ return nil, errors.New("ssh: Stdout already set")
+ }
+ var b bytes.Buffer
+ s.Stdout = &b
+ err := s.Run(cmd)
+ return b.Bytes(), err
+}
+
+type singleWriter struct {
+ b bytes.Buffer
+ mu sync.Mutex
+}
+
+func (w *singleWriter) Write(p []byte) (int, error) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ return w.b.Write(p)
+}
+
+// CombinedOutput runs cmd on the remote host and returns its combined
+// standard output and standard error.
+func (s *Session) CombinedOutput(cmd string) ([]byte, error) {
+ if s.Stdout != nil {
+ return nil, errors.New("ssh: Stdout already set")
+ }
+ if s.Stderr != nil {
+ return nil, errors.New("ssh: Stderr already set")
+ }
+ var b singleWriter
+ s.Stdout = &b
+ s.Stderr = &b
+ err := s.Run(cmd)
+ return b.b.Bytes(), err
+}
+
+// Shell starts a login shell on the remote host. A Session only
+// accepts one call to Run, Start, Shell, Output, or CombinedOutput.
+func (s *Session) Shell() error {
+ if s.started {
+ return errors.New("ssh: session already started")
+ }
+
+ ok, err := s.ch.SendRequest("shell", true, nil)
+ if err == nil && !ok {
+ return errors.New("ssh: could not start shell")
+ }
+ if err != nil {
+ return err
+ }
+ return s.start()
+}
+
+func (s *Session) start() error {
+ s.started = true
+
+ type F func(*Session)
+ for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
+ setupFd(s)
+ }
+
+ s.errors = make(chan error, len(s.copyFuncs))
+ for _, fn := range s.copyFuncs {
+ go func(fn func() error) {
+ s.errors <- fn()
+ }(fn)
+ }
+ return nil
+}
+
+// Wait waits for the remote command to exit.
+//
+// The returned error is nil if the command runs, has no problems
+// copying stdin, stdout, and stderr, and exits with a zero exit
+// status.
+//
+// If the remote server does not send an exit status, an error of type
+// *ExitMissingError is returned. If the command completes
+// unsuccessfully or is interrupted by a signal, the error is of type
+// *ExitError. Other error types may be returned for I/O problems.
+func (s *Session) Wait() error {
+ if !s.started {
+ return errors.New("ssh: session not started")
+ }
+ waitErr := <-s.exitStatus
+
+ if s.stdinPipeWriter != nil {
+ s.stdinPipeWriter.Close()
+ }
+ var copyError error
+ for _ = range s.copyFuncs {
+ if err := <-s.errors; err != nil && copyError == nil {
+ copyError = err
+ }
+ }
+ if waitErr != nil {
+ return waitErr
+ }
+ return copyError
+}
+
+func (s *Session) wait(reqs <-chan *Request) error {
+ wm := Waitmsg{status: -1}
+ // Wait for msg channel to be closed before returning.
+ for msg := range reqs {
+ switch msg.Type {
+ case "exit-status":
+ wm.status = int(binary.BigEndian.Uint32(msg.Payload))
+ case "exit-signal":
+ var sigval struct {
+ Signal string
+ CoreDumped bool
+ Error string
+ Lang string
+ }
+ if err := Unmarshal(msg.Payload, &sigval); err != nil {
+ return err
+ }
+
+ // Must sanitize strings?
+ wm.signal = sigval.Signal
+ wm.msg = sigval.Error
+ wm.lang = sigval.Lang
+ default:
+ // This handles keepalives and matches
+ // OpenSSH's behaviour.
+ if msg.WantReply {
+ msg.Reply(false, nil)
+ }
+ }
+ }
+ if wm.status == 0 {
+ return nil
+ }
+ if wm.status == -1 {
+ // exit-status was never sent from server
+ if wm.signal == "" {
+ // signal was not sent either. RFC 4254
+ // section 6.10 recommends against this
+ // behavior, but it is allowed, so we let
+ // clients handle it.
+ return &ExitMissingError{}
+ }
+ wm.status = 128
+ if _, ok := signals[Signal(wm.signal)]; ok {
+ wm.status += signals[Signal(wm.signal)]
+ }
+ }
+
+ return &ExitError{wm}
+}
+
+// ExitMissingError is returned if a session is torn down cleanly, but
+// the server sends no confirmation of the exit status.
+type ExitMissingError struct{}
+
+func (e *ExitMissingError) Error() string {
+ return "wait: remote command exited without exit status or exit signal"
+}
+
+func (s *Session) stdin() {
+ if s.stdinpipe {
+ return
+ }
+ var stdin io.Reader
+ if s.Stdin == nil {
+ stdin = new(bytes.Buffer)
+ } else {
+ r, w := io.Pipe()
+ go func() {
+ _, err := io.Copy(w, s.Stdin)
+ w.CloseWithError(err)
+ }()
+ stdin, s.stdinPipeWriter = r, w
+ }
+ s.copyFuncs = append(s.copyFuncs, func() error {
+ _, err := io.Copy(s.ch, stdin)
+ if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF {
+ err = err1
+ }
+ return err
+ })
+}
+
+func (s *Session) stdout() {
+ if s.stdoutpipe {
+ return
+ }
+ if s.Stdout == nil {
+ s.Stdout = ioutil.Discard
+ }
+ s.copyFuncs = append(s.copyFuncs, func() error {
+ _, err := io.Copy(s.Stdout, s.ch)
+ return err
+ })
+}
+
+func (s *Session) stderr() {
+ if s.stderrpipe {
+ return
+ }
+ if s.Stderr == nil {
+ s.Stderr = ioutil.Discard
+ }
+ s.copyFuncs = append(s.copyFuncs, func() error {
+ _, err := io.Copy(s.Stderr, s.ch.Stderr())
+ return err
+ })
+}
+
+// sessionStdin reroutes Close to CloseWrite.
+type sessionStdin struct {
+ io.Writer
+ ch Channel
+}
+
+func (s *sessionStdin) Close() error {
+ return s.ch.CloseWrite()
+}
+
+// StdinPipe returns a pipe that will be connected to the
+// remote command's standard input when the command starts.
+func (s *Session) StdinPipe() (io.WriteCloser, error) {
+ if s.Stdin != nil {
+ return nil, errors.New("ssh: Stdin already set")
+ }
+ if s.started {
+ return nil, errors.New("ssh: StdinPipe after process started")
+ }
+ s.stdinpipe = true
+ return &sessionStdin{s.ch, s.ch}, nil
+}
+
+// StdoutPipe returns a pipe that will be connected to the
+// remote command's standard output when the command starts.
+// There is a fixed amount of buffering that is shared between
+// stdout and stderr streams. If the StdoutPipe reader is
+// not serviced fast enough it may eventually cause the
+// remote command to block.
+func (s *Session) StdoutPipe() (io.Reader, error) {
+ if s.Stdout != nil {
+ return nil, errors.New("ssh: Stdout already set")
+ }
+ if s.started {
+ return nil, errors.New("ssh: StdoutPipe after process started")
+ }
+ s.stdoutpipe = true
+ return s.ch, nil
+}
+
+// StderrPipe returns a pipe that will be connected to the
+// remote command's standard error when the command starts.
+// There is a fixed amount of buffering that is shared between
+// stdout and stderr streams. If the StderrPipe reader is
+// not serviced fast enough it may eventually cause the
+// remote command to block.
+func (s *Session) StderrPipe() (io.Reader, error) {
+ if s.Stderr != nil {
+ return nil, errors.New("ssh: Stderr already set")
+ }
+ if s.started {
+ return nil, errors.New("ssh: StderrPipe after process started")
+ }
+ s.stderrpipe = true
+ return s.ch.Stderr(), nil
+}
+
+// newSession returns a new interactive session on the remote host.
+func newSession(ch Channel, reqs <-chan *Request) (*Session, error) {
+ s := &Session{
+ ch: ch,
+ }
+ s.exitStatus = make(chan error, 1)
+ go func() {
+ s.exitStatus <- s.wait(reqs)
+ }()
+
+ return s, nil
+}
+
+// An ExitError reports unsuccessful completion of a remote command.
+type ExitError struct {
+ Waitmsg
+}
+
+func (e *ExitError) Error() string {
+ return e.Waitmsg.String()
+}
+
+// Waitmsg stores the information about an exited remote command
+// as reported by Wait.
+type Waitmsg struct {
+ status int
+ signal string
+ msg string
+ lang string
+}
+
+// ExitStatus returns the exit status of the remote command.
+func (w Waitmsg) ExitStatus() int {
+ return w.status
+}
+
+// Signal returns the exit signal of the remote command if
+// it was terminated violently.
+func (w Waitmsg) Signal() string {
+ return w.signal
+}
+
+// Msg returns the exit message given by the remote command
+func (w Waitmsg) Msg() string {
+ return w.msg
+}
+
+// Lang returns the language tag. See RFC 3066
+func (w Waitmsg) Lang() string {
+ return w.lang
+}
+
+func (w Waitmsg) String() string {
+ str := fmt.Sprintf("Process exited with status %v", w.status)
+ if w.signal != "" {
+ str += fmt.Sprintf(" from signal %v", w.signal)
+ }
+ if w.msg != "" {
+ str += fmt.Sprintf(". Reason was: %v", w.msg)
+ }
+ return str
+}
diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go
new file mode 100644
index 000000000..6151241ff
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/tcpip.go
@@ -0,0 +1,407 @@
+// Copyright 2011 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 ssh
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "math/rand"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// Listen requests the remote peer open a listening socket on
+// addr. Incoming connections will be available by calling Accept on
+// the returned net.Listener. The listener must be serviced, or the
+// SSH connection may hang.
+func (c *Client) Listen(n, addr string) (net.Listener, error) {
+ laddr, err := net.ResolveTCPAddr(n, addr)
+ if err != nil {
+ return nil, err
+ }
+ return c.ListenTCP(laddr)
+}
+
+// Automatic port allocation is broken with OpenSSH before 6.0. See
+// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In
+// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0,
+// rather than the actual port number. This means you can never open
+// two different listeners with auto allocated ports. We work around
+// this by trying explicit ports until we succeed.
+
+const openSSHPrefix = "OpenSSH_"
+
+var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+// isBrokenOpenSSHVersion returns true if the given version string
+// specifies a version of OpenSSH that is known to have a bug in port
+// forwarding.
+func isBrokenOpenSSHVersion(versionStr string) bool {
+ i := strings.Index(versionStr, openSSHPrefix)
+ if i < 0 {
+ return false
+ }
+ i += len(openSSHPrefix)
+ j := i
+ for ; j < len(versionStr); j++ {
+ if versionStr[j] < '0' || versionStr[j] > '9' {
+ break
+ }
+ }
+ version, _ := strconv.Atoi(versionStr[i:j])
+ return version < 6
+}
+
+// autoPortListenWorkaround simulates automatic port allocation by
+// trying random ports repeatedly.
+func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
+ var sshListener net.Listener
+ var err error
+ const tries = 10
+ for i := 0; i < tries; i++ {
+ addr := *laddr
+ addr.Port = 1024 + portRandomizer.Intn(60000)
+ sshListener, err = c.ListenTCP(&addr)
+ if err == nil {
+ laddr.Port = addr.Port
+ return sshListener, err
+ }
+ }
+ return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err)
+}
+
+// RFC 4254 7.1
+type channelForwardMsg struct {
+ addr string
+ rport uint32
+}
+
+// ListenTCP requests the remote peer open a listening socket
+// on laddr. Incoming connections will be available by calling
+// Accept on the returned net.Listener.
+func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
+ if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
+ return c.autoPortListenWorkaround(laddr)
+ }
+
+ m := channelForwardMsg{
+ laddr.IP.String(),
+ uint32(laddr.Port),
+ }
+ // send message
+ ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ return nil, errors.New("ssh: tcpip-forward request denied by peer")
+ }
+
+ // If the original port was 0, then the remote side will
+ // supply a real port number in the response.
+ if laddr.Port == 0 {
+ var p struct {
+ Port uint32
+ }
+ if err := Unmarshal(resp, &p); err != nil {
+ return nil, err
+ }
+ laddr.Port = int(p.Port)
+ }
+
+ // Register this forward, using the port number we obtained.
+ ch := c.forwards.add(*laddr)
+
+ return &tcpListener{laddr, c, ch}, nil
+}
+
+// forwardList stores a mapping between remote
+// forward requests and the tcpListeners.
+type forwardList struct {
+ sync.Mutex
+ entries []forwardEntry
+}
+
+// forwardEntry represents an established mapping of a laddr on a
+// remote ssh server to a channel connected to a tcpListener.
+type forwardEntry struct {
+ laddr net.TCPAddr
+ c chan forward
+}
+
+// forward represents an incoming forwarded tcpip connection. The
+// arguments to add/remove/lookup should be address as specified in
+// the original forward-request.
+type forward struct {
+ newCh NewChannel // the ssh client channel underlying this forward
+ raddr *net.TCPAddr // the raddr of the incoming connection
+}
+
+func (l *forwardList) add(addr net.TCPAddr) chan forward {
+ l.Lock()
+ defer l.Unlock()
+ f := forwardEntry{
+ addr,
+ make(chan forward, 1),
+ }
+ l.entries = append(l.entries, f)
+ return f.c
+}
+
+// See RFC 4254, section 7.2
+type forwardedTCPPayload struct {
+ Addr string
+ Port uint32
+ OriginAddr string
+ OriginPort uint32
+}
+
+// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr.
+func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
+ if port == 0 || port > 65535 {
+ return nil, fmt.Errorf("ssh: port number out of range: %d", port)
+ }
+ ip := net.ParseIP(string(addr))
+ if ip == nil {
+ return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
+ }
+ return &net.TCPAddr{IP: ip, Port: int(port)}, nil
+}
+
+func (l *forwardList) handleChannels(in <-chan NewChannel) {
+ for ch := range in {
+ var payload forwardedTCPPayload
+ if err := Unmarshal(ch.ExtraData(), &payload); err != nil {
+ ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
+ continue
+ }
+
+ // RFC 4254 section 7.2 specifies that incoming
+ // addresses should list the address, in string
+ // format. It is implied that this should be an IP
+ // address, as it would be impossible to connect to it
+ // otherwise.
+ laddr, err := parseTCPAddr(payload.Addr, payload.Port)
+ if err != nil {
+ ch.Reject(ConnectionFailed, err.Error())
+ continue
+ }
+ raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort)
+ if err != nil {
+ ch.Reject(ConnectionFailed, err.Error())
+ continue
+ }
+
+ if ok := l.forward(*laddr, *raddr, ch); !ok {
+ // Section 7.2, implementations MUST reject spurious incoming
+ // connections.
+ ch.Reject(Prohibited, "no forward for address")
+ continue
+ }
+ }
+}
+
+// remove removes the forward entry, and the channel feeding its
+// listener.
+func (l *forwardList) remove(addr net.TCPAddr) {
+ l.Lock()
+ defer l.Unlock()
+ for i, f := range l.entries {
+ if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port {
+ l.entries = append(l.entries[:i], l.entries[i+1:]...)
+ close(f.c)
+ return
+ }
+ }
+}
+
+// closeAll closes and clears all forwards.
+func (l *forwardList) closeAll() {
+ l.Lock()
+ defer l.Unlock()
+ for _, f := range l.entries {
+ close(f.c)
+ }
+ l.entries = nil
+}
+
+func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool {
+ l.Lock()
+ defer l.Unlock()
+ for _, f := range l.entries {
+ if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port {
+ f.c <- forward{ch, &raddr}
+ return true
+ }
+ }
+ return false
+}
+
+type tcpListener struct {
+ laddr *net.TCPAddr
+
+ conn *Client
+ in <-chan forward
+}
+
+// Accept waits for and returns the next connection to the listener.
+func (l *tcpListener) Accept() (net.Conn, error) {
+ s, ok := <-l.in
+ if !ok {
+ return nil, io.EOF
+ }
+ ch, incoming, err := s.newCh.Accept()
+ if err != nil {
+ return nil, err
+ }
+ go DiscardRequests(incoming)
+
+ return &tcpChanConn{
+ Channel: ch,
+ laddr: l.laddr,
+ raddr: s.raddr,
+ }, nil
+}
+
+// Close closes the listener.
+func (l *tcpListener) Close() error {
+ m := channelForwardMsg{
+ l.laddr.IP.String(),
+ uint32(l.laddr.Port),
+ }
+
+ // this also closes the listener.
+ l.conn.forwards.remove(*l.laddr)
+ ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
+ if err == nil && !ok {
+ err = errors.New("ssh: cancel-tcpip-forward failed")
+ }
+ return err
+}
+
+// Addr returns the listener's network address.
+func (l *tcpListener) Addr() net.Addr {
+ return l.laddr
+}
+
+// Dial initiates a connection to the addr from the remote host.
+// The resulting connection has a zero LocalAddr() and RemoteAddr().
+func (c *Client) Dial(n, addr string) (net.Conn, error) {
+ // Parse the address into host and numeric port.
+ host, portString, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+ port, err := strconv.ParseUint(portString, 10, 16)
+ if err != nil {
+ return nil, err
+ }
+ // Use a zero address for local and remote address.
+ zeroAddr := &net.TCPAddr{
+ IP: net.IPv4zero,
+ Port: 0,
+ }
+ ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port))
+ if err != nil {
+ return nil, err
+ }
+ return &tcpChanConn{
+ Channel: ch,
+ laddr: zeroAddr,
+ raddr: zeroAddr,
+ }, nil
+}
+
+// DialTCP connects to the remote address raddr on the network net,
+// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
+// as the local address for the connection.
+func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
+ if laddr == nil {
+ laddr = &net.TCPAddr{
+ IP: net.IPv4zero,
+ Port: 0,
+ }
+ }
+ ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
+ if err != nil {
+ return nil, err
+ }
+ return &tcpChanConn{
+ Channel: ch,
+ laddr: laddr,
+ raddr: raddr,
+ }, nil
+}
+
+// RFC 4254 7.2
+type channelOpenDirectMsg struct {
+ raddr string
+ rport uint32
+ laddr string
+ lport uint32
+}
+
+func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
+ msg := channelOpenDirectMsg{
+ raddr: raddr,
+ rport: uint32(rport),
+ laddr: laddr,
+ lport: uint32(lport),
+ }
+ ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
+ if err != nil {
+ return nil, err
+ }
+ go DiscardRequests(in)
+ return ch, err
+}
+
+type tcpChan struct {
+ Channel // the backing channel
+}
+
+// tcpChanConn fulfills the net.Conn interface without
+// the tcpChan having to hold laddr or raddr directly.
+type tcpChanConn struct {
+ Channel
+ laddr, raddr net.Addr
+}
+
+// LocalAddr returns the local network address.
+func (t *tcpChanConn) LocalAddr() net.Addr {
+ return t.laddr
+}
+
+// RemoteAddr returns the remote network address.
+func (t *tcpChanConn) RemoteAddr() net.Addr {
+ return t.raddr
+}
+
+// SetDeadline sets the read and write deadlines associated
+// with the connection.
+func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
+ if err := t.SetReadDeadline(deadline); err != nil {
+ return err
+ }
+ return t.SetWriteDeadline(deadline)
+}
+
+// SetReadDeadline sets the read deadline.
+// A zero value for t means Read will not time out.
+// After the deadline, the error from Read will implement net.Error
+// with Timeout() == true.
+func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error {
+ return errors.New("ssh: tcpChan: deadline not supported")
+}
+
+// SetWriteDeadline exists to satisfy the net.Conn interface
+// but is not implemented by this type. It always returns an error.
+func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error {
+ return errors.New("ssh: tcpChan: deadline not supported")
+}
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go
new file mode 100644
index 000000000..18379a935
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go
@@ -0,0 +1,951 @@
+// Copyright 2011 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 terminal
+
+import (
+ "bytes"
+ "io"
+ "sync"
+ "unicode/utf8"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes struct {
+ // Foreground colors
+ Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
+
+ // Reset all attributes
+ Reset []byte
+}
+
+var vt100EscapeCodes = EscapeCodes{
+ Black: []byte{keyEscape, '[', '3', '0', 'm'},
+ Red: []byte{keyEscape, '[', '3', '1', 'm'},
+ Green: []byte{keyEscape, '[', '3', '2', 'm'},
+ Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
+ Blue: []byte{keyEscape, '[', '3', '4', 'm'},
+ Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
+ Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
+ White: []byte{keyEscape, '[', '3', '7', 'm'},
+
+ Reset: []byte{keyEscape, '[', '0', 'm'},
+}
+
+// Terminal contains the state for running a VT100 terminal that is capable of
+// reading lines of input.
+type Terminal struct {
+ // AutoCompleteCallback, if non-null, is called for each keypress with
+ // the full input line and the current position of the cursor (in
+ // bytes, as an index into |line|). If it returns ok=false, the key
+ // press is processed normally. Otherwise it returns a replacement line
+ // and the new cursor position.
+ AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
+
+ // Escape contains a pointer to the escape codes for this terminal.
+ // It's always a valid pointer, although the escape codes themselves
+ // may be empty if the terminal doesn't support them.
+ Escape *EscapeCodes
+
+ // lock protects the terminal and the state in this object from
+ // concurrent processing of a key press and a Write() call.
+ lock sync.Mutex
+
+ c io.ReadWriter
+ prompt []rune
+
+ // line is the current line being entered.
+ line []rune
+ // pos is the logical position of the cursor in line
+ pos int
+ // echo is true if local echo is enabled
+ echo bool
+ // pasteActive is true iff there is a bracketed paste operation in
+ // progress.
+ pasteActive bool
+
+ // cursorX contains the current X value of the cursor where the left
+ // edge is 0. cursorY contains the row number where the first row of
+ // the current line is 0.
+ cursorX, cursorY int
+ // maxLine is the greatest value of cursorY so far.
+ maxLine int
+
+ termWidth, termHeight int
+
+ // outBuf contains the terminal data to be sent.
+ outBuf []byte
+ // remainder contains the remainder of any partial key sequences after
+ // a read. It aliases into inBuf.
+ remainder []byte
+ inBuf [256]byte
+
+ // history contains previously entered commands so that they can be
+ // accessed with the up and down keys.
+ history stRingBuffer
+ // historyIndex stores the currently accessed history entry, where zero
+ // means the immediately previous entry.
+ historyIndex int
+ // When navigating up and down the history it's possible to return to
+ // the incomplete, initial line. That value is stored in
+ // historyPending.
+ historyPending string
+}
+
+// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
+// a local terminal, that terminal must first have been put into raw mode.
+// prompt is a string that is written at the start of each input line (i.e.
+// "> ").
+func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
+ return &Terminal{
+ Escape: &vt100EscapeCodes,
+ c: c,
+ prompt: []rune(prompt),
+ termWidth: 80,
+ termHeight: 24,
+ echo: true,
+ historyIndex: -1,
+ }
+}
+
+const (
+ keyCtrlD = 4
+ keyCtrlU = 21
+ keyEnter = '\r'
+ keyEscape = 27
+ keyBackspace = 127
+ keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
+ keyUp
+ keyDown
+ keyLeft
+ keyRight
+ keyAltLeft
+ keyAltRight
+ keyHome
+ keyEnd
+ keyDeleteWord
+ keyDeleteLine
+ keyClearScreen
+ keyPasteStart
+ keyPasteEnd
+)
+
+var (
+ crlf = []byte{'\r', '\n'}
+ pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
+ pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
+)
+
+// bytesToKey tries to parse a key sequence from b. If successful, it returns
+// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
+func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
+ if len(b) == 0 {
+ return utf8.RuneError, nil
+ }
+
+ if !pasteActive {
+ switch b[0] {
+ case 1: // ^A
+ return keyHome, b[1:]
+ case 5: // ^E
+ return keyEnd, b[1:]
+ case 8: // ^H
+ return keyBackspace, b[1:]
+ case 11: // ^K
+ return keyDeleteLine, b[1:]
+ case 12: // ^L
+ return keyClearScreen, b[1:]
+ case 23: // ^W
+ return keyDeleteWord, b[1:]
+ }
+ }
+
+ if b[0] != keyEscape {
+ if !utf8.FullRune(b) {
+ return utf8.RuneError, b
+ }
+ r, l := utf8.DecodeRune(b)
+ return r, b[l:]
+ }
+
+ if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
+ switch b[2] {
+ case 'A':
+ return keyUp, b[3:]
+ case 'B':
+ return keyDown, b[3:]
+ case 'C':
+ return keyRight, b[3:]
+ case 'D':
+ return keyLeft, b[3:]
+ case 'H':
+ return keyHome, b[3:]
+ case 'F':
+ return keyEnd, b[3:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
+ switch b[5] {
+ case 'C':
+ return keyAltRight, b[6:]
+ case 'D':
+ return keyAltLeft, b[6:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
+ return keyPasteStart, b[6:]
+ }
+
+ if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
+ return keyPasteEnd, b[6:]
+ }
+
+ // If we get here then we have a key that we don't recognise, or a
+ // partial sequence. It's not clear how one should find the end of a
+ // sequence without knowing them all, but it seems that [a-zA-Z~] only
+ // appears at the end of a sequence.
+ for i, c := range b[0:] {
+ if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
+ return keyUnknown, b[i+1:]
+ }
+ }
+
+ return utf8.RuneError, b
+}
+
+// queue appends data to the end of t.outBuf
+func (t *Terminal) queue(data []rune) {
+ t.outBuf = append(t.outBuf, []byte(string(data))...)
+}
+
+var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
+var space = []rune{' '}
+
+func isPrintable(key rune) bool {
+ isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
+ return key >= 32 && !isInSurrogateArea
+}
+
+// moveCursorToPos appends data to t.outBuf which will move the cursor to the
+// given, logical position in the text.
+func (t *Terminal) moveCursorToPos(pos int) {
+ if !t.echo {
+ return
+ }
+
+ x := visualLength(t.prompt) + pos
+ y := x / t.termWidth
+ x = x % t.termWidth
+
+ up := 0
+ if y < t.cursorY {
+ up = t.cursorY - y
+ }
+
+ down := 0
+ if y > t.cursorY {
+ down = y - t.cursorY
+ }
+
+ left := 0
+ if x < t.cursorX {
+ left = t.cursorX - x
+ }
+
+ right := 0
+ if x > t.cursorX {
+ right = x - t.cursorX
+ }
+
+ t.cursorX = x
+ t.cursorY = y
+ t.move(up, down, left, right)
+}
+
+func (t *Terminal) move(up, down, left, right int) {
+ movement := make([]rune, 3*(up+down+left+right))
+ m := movement
+ for i := 0; i < up; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'A'
+ m = m[3:]
+ }
+ for i := 0; i < down; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'B'
+ m = m[3:]
+ }
+ for i := 0; i < left; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'D'
+ m = m[3:]
+ }
+ for i := 0; i < right; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'C'
+ m = m[3:]
+ }
+
+ t.queue(movement)
+}
+
+func (t *Terminal) clearLineToRight() {
+ op := []rune{keyEscape, '[', 'K'}
+ t.queue(op)
+}
+
+const maxLineLength = 4096
+
+func (t *Terminal) setLine(newLine []rune, newPos int) {
+ if t.echo {
+ t.moveCursorToPos(0)
+ t.writeLine(newLine)
+ for i := len(newLine); i < len(t.line); i++ {
+ t.writeLine(space)
+ }
+ t.moveCursorToPos(newPos)
+ }
+ t.line = newLine
+ t.pos = newPos
+}
+
+func (t *Terminal) advanceCursor(places int) {
+ t.cursorX += places
+ t.cursorY += t.cursorX / t.termWidth
+ if t.cursorY > t.maxLine {
+ t.maxLine = t.cursorY
+ }
+ t.cursorX = t.cursorX % t.termWidth
+
+ if places > 0 && t.cursorX == 0 {
+ // Normally terminals will advance the current position
+ // when writing a character. But that doesn't happen
+ // for the last character in a line. However, when
+ // writing a character (except a new line) that causes
+ // a line wrap, the position will be advanced two
+ // places.
+ //
+ // So, if we are stopping at the end of a line, we
+ // need to write a newline so that our cursor can be
+ // advanced to the next line.
+ t.outBuf = append(t.outBuf, '\r', '\n')
+ }
+}
+
+func (t *Terminal) eraseNPreviousChars(n int) {
+ if n == 0 {
+ return
+ }
+
+ if t.pos < n {
+ n = t.pos
+ }
+ t.pos -= n
+ t.moveCursorToPos(t.pos)
+
+ copy(t.line[t.pos:], t.line[n+t.pos:])
+ t.line = t.line[:len(t.line)-n]
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ for i := 0; i < n; i++ {
+ t.queue(space)
+ }
+ t.advanceCursor(n)
+ t.moveCursorToPos(t.pos)
+ }
+}
+
+// countToLeftWord returns then number of characters from the cursor to the
+// start of the previous word.
+func (t *Terminal) countToLeftWord() int {
+ if t.pos == 0 {
+ return 0
+ }
+
+ pos := t.pos - 1
+ for pos > 0 {
+ if t.line[pos] != ' ' {
+ break
+ }
+ pos--
+ }
+ for pos > 0 {
+ if t.line[pos] == ' ' {
+ pos++
+ break
+ }
+ pos--
+ }
+
+ return t.pos - pos
+}
+
+// countToRightWord returns then number of characters from the cursor to the
+// start of the next word.
+func (t *Terminal) countToRightWord() int {
+ pos := t.pos
+ for pos < len(t.line) {
+ if t.line[pos] == ' ' {
+ break
+ }
+ pos++
+ }
+ for pos < len(t.line) {
+ if t.line[pos] != ' ' {
+ break
+ }
+ pos++
+ }
+ return pos - t.pos
+}
+
+// visualLength returns the number of visible glyphs in s.
+func visualLength(runes []rune) int {
+ inEscapeSeq := false
+ length := 0
+
+ for _, r := range runes {
+ switch {
+ case inEscapeSeq:
+ if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
+ inEscapeSeq = false
+ }
+ case r == '\x1b':
+ inEscapeSeq = true
+ default:
+ length++
+ }
+ }
+
+ return length
+}
+
+// handleKey processes the given key and, optionally, returns a line of text
+// that the user has entered.
+func (t *Terminal) handleKey(key rune) (line string, ok bool) {
+ if t.pasteActive && key != keyEnter {
+ t.addKeyToLine(key)
+ return
+ }
+
+ switch key {
+ case keyBackspace:
+ if t.pos == 0 {
+ return
+ }
+ t.eraseNPreviousChars(1)
+ case keyAltLeft:
+ // move left by a word.
+ t.pos -= t.countToLeftWord()
+ t.moveCursorToPos(t.pos)
+ case keyAltRight:
+ // move right by a word.
+ t.pos += t.countToRightWord()
+ t.moveCursorToPos(t.pos)
+ case keyLeft:
+ if t.pos == 0 {
+ return
+ }
+ t.pos--
+ t.moveCursorToPos(t.pos)
+ case keyRight:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+ case keyHome:
+ if t.pos == 0 {
+ return
+ }
+ t.pos = 0
+ t.moveCursorToPos(t.pos)
+ case keyEnd:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos = len(t.line)
+ t.moveCursorToPos(t.pos)
+ case keyUp:
+ entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
+ if !ok {
+ return "", false
+ }
+ if t.historyIndex == -1 {
+ t.historyPending = string(t.line)
+ }
+ t.historyIndex++
+ runes := []rune(entry)
+ t.setLine(runes, len(runes))
+ case keyDown:
+ switch t.historyIndex {
+ case -1:
+ return
+ case 0:
+ runes := []rune(t.historyPending)
+ t.setLine(runes, len(runes))
+ t.historyIndex--
+ default:
+ entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
+ if ok {
+ t.historyIndex--
+ runes := []rune(entry)
+ t.setLine(runes, len(runes))
+ }
+ }
+ case keyEnter:
+ t.moveCursorToPos(len(t.line))
+ t.queue([]rune("\r\n"))
+ line = string(t.line)
+ ok = true
+ t.line = t.line[:0]
+ t.pos = 0
+ t.cursorX = 0
+ t.cursorY = 0
+ t.maxLine = 0
+ case keyDeleteWord:
+ // Delete zero or more spaces and then one or more characters.
+ t.eraseNPreviousChars(t.countToLeftWord())
+ case keyDeleteLine:
+ // Delete everything from the current cursor position to the
+ // end of line.
+ for i := t.pos; i < len(t.line); i++ {
+ t.queue(space)
+ t.advanceCursor(1)
+ }
+ t.line = t.line[:t.pos]
+ t.moveCursorToPos(t.pos)
+ case keyCtrlD:
+ // Erase the character under the current position.
+ // The EOF case when the line is empty is handled in
+ // readLine().
+ if t.pos < len(t.line) {
+ t.pos++
+ t.eraseNPreviousChars(1)
+ }
+ case keyCtrlU:
+ t.eraseNPreviousChars(t.pos)
+ case keyClearScreen:
+ // Erases the screen and moves the cursor to the home position.
+ t.queue([]rune("\x1b[2J\x1b[H"))
+ t.queue(t.prompt)
+ t.cursorX, t.cursorY = 0, 0
+ t.advanceCursor(visualLength(t.prompt))
+ t.setLine(t.line, t.pos)
+ default:
+ if t.AutoCompleteCallback != nil {
+ prefix := string(t.line[:t.pos])
+ suffix := string(t.line[t.pos:])
+
+ t.lock.Unlock()
+ newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
+ t.lock.Lock()
+
+ if completeOk {
+ t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
+ return
+ }
+ }
+ if !isPrintable(key) {
+ return
+ }
+ if len(t.line) == maxLineLength {
+ return
+ }
+ t.addKeyToLine(key)
+ }
+ return
+}
+
+// addKeyToLine inserts the given key at the current position in the current
+// line.
+func (t *Terminal) addKeyToLine(key rune) {
+ if len(t.line) == cap(t.line) {
+ newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
+ copy(newLine, t.line)
+ t.line = newLine
+ }
+ t.line = t.line[:len(t.line)+1]
+ copy(t.line[t.pos+1:], t.line[t.pos:])
+ t.line[t.pos] = key
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) writeLine(line []rune) {
+ for len(line) != 0 {
+ remainingOnLine := t.termWidth - t.cursorX
+ todo := len(line)
+ if todo > remainingOnLine {
+ todo = remainingOnLine
+ }
+ t.queue(line[:todo])
+ t.advanceCursor(visualLength(line[:todo]))
+ line = line[todo:]
+ }
+}
+
+// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
+func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
+ for len(buf) > 0 {
+ i := bytes.IndexByte(buf, '\n')
+ todo := len(buf)
+ if i >= 0 {
+ todo = i
+ }
+
+ var nn int
+ nn, err = w.Write(buf[:todo])
+ n += nn
+ if err != nil {
+ return n, err
+ }
+ buf = buf[todo:]
+
+ if i >= 0 {
+ if _, err = w.Write(crlf); err != nil {
+ return n, err
+ }
+ n += 1
+ buf = buf[1:]
+ }
+ }
+
+ return n, nil
+}
+
+func (t *Terminal) Write(buf []byte) (n int, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ // This is the easy case: there's nothing on the screen that we
+ // have to move out of the way.
+ return writeWithCRLF(t.c, buf)
+ }
+
+ // We have a prompt and possibly user input on the screen. We
+ // have to clear it first.
+ t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
+ t.cursorX = 0
+ t.clearLineToRight()
+
+ for t.cursorY > 0 {
+ t.move(1 /* up */, 0, 0, 0)
+ t.cursorY--
+ t.clearLineToRight()
+ }
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+
+ if n, err = writeWithCRLF(t.c, buf); err != nil {
+ return
+ }
+
+ t.writeLine(t.prompt)
+ if t.echo {
+ t.writeLine(t.line)
+ }
+
+ t.moveCursorToPos(t.pos)
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+ return
+}
+
+// ReadPassword temporarily changes the prompt and reads a password, without
+// echo, from the terminal.
+func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ oldPrompt := t.prompt
+ t.prompt = []rune(prompt)
+ t.echo = false
+
+ line, err = t.readLine()
+
+ t.prompt = oldPrompt
+ t.echo = true
+
+ return
+}
+
+// ReadLine returns a line of input from the terminal.
+func (t *Terminal) ReadLine() (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ return t.readLine()
+}
+
+func (t *Terminal) readLine() (line string, err error) {
+ // t.lock must be held at this point
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ t.writeLine(t.prompt)
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ }
+
+ lineIsPasted := t.pasteActive
+
+ for {
+ rest := t.remainder
+ lineOk := false
+ for !lineOk {
+ var key rune
+ key, rest = bytesToKey(rest, t.pasteActive)
+ if key == utf8.RuneError {
+ break
+ }
+ if !t.pasteActive {
+ if key == keyCtrlD {
+ if len(t.line) == 0 {
+ return "", io.EOF
+ }
+ }
+ if key == keyPasteStart {
+ t.pasteActive = true
+ if len(t.line) == 0 {
+ lineIsPasted = true
+ }
+ continue
+ }
+ } else if key == keyPasteEnd {
+ t.pasteActive = false
+ continue
+ }
+ if !t.pasteActive {
+ lineIsPasted = false
+ }
+ line, lineOk = t.handleKey(key)
+ }
+ if len(rest) > 0 {
+ n := copy(t.inBuf[:], rest)
+ t.remainder = t.inBuf[:n]
+ } else {
+ t.remainder = nil
+ }
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ if lineOk {
+ if t.echo {
+ t.historyIndex = -1
+ t.history.Add(line)
+ }
+ if lineIsPasted {
+ err = ErrPasteIndicator
+ }
+ return
+ }
+
+ // t.remainder is a slice at the beginning of t.inBuf
+ // containing a partial key sequence
+ readBuf := t.inBuf[len(t.remainder):]
+ var n int
+
+ t.lock.Unlock()
+ n, err = t.c.Read(readBuf)
+ t.lock.Lock()
+
+ if err != nil {
+ return
+ }
+
+ t.remainder = t.inBuf[:n+len(t.remainder)]
+ }
+}
+
+// SetPrompt sets the prompt to be used when reading subsequent lines.
+func (t *Terminal) SetPrompt(prompt string) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.prompt = []rune(prompt)
+}
+
+func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
+ // Move cursor to column zero at the start of the line.
+ t.move(t.cursorY, 0, t.cursorX, 0)
+ t.cursorX, t.cursorY = 0, 0
+ t.clearLineToRight()
+ for t.cursorY < numPrevLines {
+ // Move down a line
+ t.move(0, 1, 0, 0)
+ t.cursorY++
+ t.clearLineToRight()
+ }
+ // Move back to beginning.
+ t.move(t.cursorY, 0, 0, 0)
+ t.cursorX, t.cursorY = 0, 0
+
+ t.queue(t.prompt)
+ t.advanceCursor(visualLength(t.prompt))
+ t.writeLine(t.line)
+ t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) SetSize(width, height int) error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if width == 0 {
+ width = 1
+ }
+
+ oldWidth := t.termWidth
+ t.termWidth, t.termHeight = width, height
+
+ switch {
+ case width == oldWidth:
+ // If the width didn't change then nothing else needs to be
+ // done.
+ return nil
+ case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
+ // If there is nothing on current line and no prompt printed,
+ // just do nothing
+ return nil
+ case width < oldWidth:
+ // Some terminals (e.g. xterm) will truncate lines that were
+ // too long when shinking. Others, (e.g. gnome-terminal) will
+ // attempt to wrap them. For the former, repainting t.maxLine
+ // works great, but that behaviour goes badly wrong in the case
+ // of the latter because they have doubled every full line.
+
+ // We assume that we are working on a terminal that wraps lines
+ // and adjust the cursor position based on every previous line
+ // wrapping and turning into two. This causes the prompt on
+ // xterms to move upwards, which isn't great, but it avoids a
+ // huge mess with gnome-terminal.
+ if t.cursorX >= t.termWidth {
+ t.cursorX = t.termWidth - 1
+ }
+ t.cursorY *= 2
+ t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
+ case width > oldWidth:
+ // If the terminal expands then our position calculations will
+ // be wrong in the future because we think the cursor is
+ // |t.pos| chars into the string, but there will be a gap at
+ // the end of any wrapped line.
+ //
+ // But the position will actually be correct until we move, so
+ // we can move back to the beginning and repaint everything.
+ t.clearAndRepaintLinePlusNPrevious(t.maxLine)
+ }
+
+ _, err := t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ return err
+}
+
+type pasteIndicatorError struct{}
+
+func (pasteIndicatorError) Error() string {
+ return "terminal: ErrPasteIndicator not correctly handled"
+}
+
+// ErrPasteIndicator may be returned from ReadLine as the error, in addition
+// to valid line data. It indicates that bracketed paste mode is enabled and
+// that the returned line consists only of pasted data. Programs may wish to
+// interpret pasted data more literally than typed data.
+var ErrPasteIndicator = pasteIndicatorError{}
+
+// SetBracketedPasteMode requests that the terminal bracket paste operations
+// with markers. Not all terminals support this but, if it is supported, then
+// enabling this mode will stop any autocomplete callback from running due to
+// pastes. Additionally, any lines that are completely pasted will be returned
+// from ReadLine with the error set to ErrPasteIndicator.
+func (t *Terminal) SetBracketedPasteMode(on bool) {
+ if on {
+ io.WriteString(t.c, "\x1b[?2004h")
+ } else {
+ io.WriteString(t.c, "\x1b[?2004l")
+ }
+}
+
+// stRingBuffer is a ring buffer of strings.
+type stRingBuffer struct {
+ // entries contains max elements.
+ entries []string
+ max int
+ // head contains the index of the element most recently added to the ring.
+ head int
+ // size contains the number of elements in the ring.
+ size int
+}
+
+func (s *stRingBuffer) Add(a string) {
+ if s.entries == nil {
+ const defaultNumEntries = 100
+ s.entries = make([]string, defaultNumEntries)
+ s.max = defaultNumEntries
+ }
+
+ s.head = (s.head + 1) % s.max
+ s.entries[s.head] = a
+ if s.size < s.max {
+ s.size++
+ }
+}
+
+// NthPreviousEntry returns the value passed to the nth previous call to Add.
+// If n is zero then the immediately prior value is returned, if one, then the
+// next most recent, and so on. If such an element doesn't exist then ok is
+// false.
+func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
+ if n >= s.size {
+ return "", false
+ }
+ index := s.head - n
+ if index < 0 {
+ index += s.max
+ }
+ return s.entries[index], true
+}
+
+// readPasswordLine reads from reader until it finds \n or io.EOF.
+// The slice returned does not include the \n.
+// readPasswordLine also ignores any \r it finds.
+func readPasswordLine(reader io.Reader) ([]byte, error) {
+ var buf [1]byte
+ var ret []byte
+
+ for {
+ n, err := reader.Read(buf[:])
+ if n > 0 {
+ switch buf[0] {
+ case '\n':
+ return ret, nil
+ case '\r':
+ // remove \r from passwords on Windows
+ default:
+ ret = append(ret, buf[0])
+ }
+ continue
+ }
+ if err != nil {
+ if err == io.EOF && len(ret) > 0 {
+ return ret, nil
+ }
+ return ret, err
+ }
+ }
+}
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go
new file mode 100644
index 000000000..d01919614
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go
@@ -0,0 +1,119 @@
+// Copyright 2011 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.
+
+// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package terminal // import "golang.org/x/crypto/ssh/terminal"
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// State contains the state of a terminal.
+type State struct {
+ termios syscall.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ var oldState State
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
+ return nil, err
+ }
+
+ newState := oldState.termios
+ // This attempts to replicate the behaviour documented for cfmakeraw in
+ // the termios(3) manpage.
+ newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
+ newState.Oflag &^= syscall.OPOST
+ newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
+ newState.Cflag &^= syscall.CSIZE | syscall.PARENB
+ newState.Cflag |= syscall.CS8
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ var oldState State
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 {
+ return err
+ }
+ return nil
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ var dimensions [4]uint16
+
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 {
+ return -1, -1, err
+ }
+ return int(dimensions[1]), int(dimensions[0]), nil
+}
+
+// passwordReader is an io.Reader that reads from a specific file descriptor.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+ return syscall.Read(int(r), buf)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ var oldState syscall.Termios
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 {
+ return nil, err
+ }
+
+ newState := oldState
+ newState.Lflag &^= syscall.ECHO
+ newState.Lflag |= syscall.ICANON | syscall.ISIG
+ newState.Iflag |= syscall.ICRNL
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
+ return nil, err
+ }
+
+ defer func() {
+ syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0)
+ }()
+
+ return readPasswordLine(passwordReader(fd))
+}
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
new file mode 100644
index 000000000..9c1ffd145
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
@@ -0,0 +1,12 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package terminal
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TIOCGETA
+const ioctlWriteTermios = syscall.TIOCSETA
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
new file mode 100644
index 000000000..5883b22d7
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
@@ -0,0 +1,11 @@
+// Copyright 2013 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 terminal
+
+// These constants are declared here, rather than importing
+// them from the syscall package as some syscall packages, even
+// on linux, for example gccgo, do not declare them.
+const ioctlReadTermios = 0x5401 // syscall.TCGETS
+const ioctlWriteTermios = 0x5402 // syscall.TCSETS
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
new file mode 100644
index 000000000..799f049f0
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
@@ -0,0 +1,58 @@
+// Copyright 2016 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 terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+ "fmt"
+ "runtime"
+)
+
+type State struct{}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ return false
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
new file mode 100644
index 000000000..07eb5edd7
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
@@ -0,0 +1,73 @@
+// Copyright 2015 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.
+
+// +build solaris
+
+package terminal // import "golang.org/x/crypto/ssh/terminal"
+
+import (
+ "golang.org/x/sys/unix"
+ "io"
+ "syscall"
+)
+
+// State contains the state of a terminal.
+type State struct {
+ termios syscall.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ // see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
+ var termio unix.Termio
+ err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio)
+ return err == nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
+ val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+ oldState := *val
+
+ newState := oldState
+ newState.Lflag &^= syscall.ECHO
+ newState.Lflag |= syscall.ICANON | syscall.ISIG
+ newState.Iflag |= syscall.ICRNL
+ err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
+ if err != nil {
+ return nil, err
+ }
+
+ defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
+
+ var buf [16]byte
+ var ret []byte
+ for {
+ n, err := syscall.Read(fd, buf[:])
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ if len(ret) == 0 {
+ return nil, io.EOF
+ }
+ break
+ }
+ if buf[n-1] == '\n' {
+ n--
+ }
+ ret = append(ret, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ }
+
+ return ret, nil
+}
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
new file mode 100644
index 000000000..e0a1f36ce
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
@@ -0,0 +1,155 @@
+// Copyright 2011 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.
+
+// +build windows
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ enableLineInput = 2
+ enableEchoInput = 4
+ enableProcessedInput = 1
+ enableWindowInput = 8
+ enableMouseInput = 16
+ enableInsertMode = 32
+ enableQuickEditMode = 64
+ enableExtendedFlags = 128
+ enableAutoPosition = 256
+ enableProcessedOutput = 1
+ enableWrapAtEolOutput = 2
+)
+
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+var (
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+ procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
+ procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+)
+
+type (
+ short int16
+ word uint16
+
+ coord struct {
+ x short
+ y short
+ }
+ smallRect struct {
+ left short
+ top short
+ right short
+ bottom short
+ }
+ consoleScreenBufferInfo struct {
+ size coord
+ cursorPosition coord
+ attributes word
+ window smallRect
+ maximumWindowSize coord
+ }
+)
+
+type State struct {
+ mode uint32
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ var st uint32
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ return r != 0 && e == 0
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ var st uint32
+ _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
+ _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ return &State{st}, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ var st uint32
+ _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ return &State{st}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
+ return err
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ var info consoleScreenBufferInfo
+ _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
+ if e != 0 {
+ return 0, 0, error(e)
+ }
+ return int(info.size.x), int(info.size.y), nil
+}
+
+// passwordReader is an io.Reader that reads from a specific Windows HANDLE.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+ return syscall.Read(syscall.Handle(r), buf)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ var st uint32
+ _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+ old := st
+
+ st &^= (enableEchoInput)
+ st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
+ _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
+ if e != 0 {
+ return nil, error(e)
+ }
+
+ defer func() {
+ syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
+ }()
+
+ return readPasswordLine(passwordReader(fd))
+}
diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go
new file mode 100644
index 000000000..f9780e0ae
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/transport.go
@@ -0,0 +1,375 @@
+// Copyright 2011 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 ssh
+
+import (
+ "bufio"
+ "errors"
+ "io"
+ "log"
+)
+
+// debugTransport if set, will print packet types as they go over the
+// wire. No message decoding is done, to minimize the impact on timing.
+const debugTransport = false
+
+const (
+ gcmCipherID = "aes128-gcm@openssh.com"
+ aes128cbcID = "aes128-cbc"
+ tripledescbcID = "3des-cbc"
+)
+
+// packetConn represents a transport that implements packet based
+// operations.
+type packetConn interface {
+ // Encrypt and send a packet of data to the remote peer.
+ writePacket(packet []byte) error
+
+ // Read a packet from the connection. The read is blocking,
+ // i.e. if error is nil, then the returned byte slice is
+ // always non-empty.
+ readPacket() ([]byte, error)
+
+ // Close closes the write-side of the connection.
+ Close() error
+}
+
+// transport is the keyingTransport that implements the SSH packet
+// protocol.
+type transport struct {
+ reader connectionState
+ writer connectionState
+
+ bufReader *bufio.Reader
+ bufWriter *bufio.Writer
+ rand io.Reader
+ isClient bool
+ io.Closer
+}
+
+// packetCipher represents a combination of SSH encryption/MAC
+// protocol. A single instance should be used for one direction only.
+type packetCipher interface {
+ // writePacket encrypts the packet and writes it to w. The
+ // contents of the packet are generally scrambled.
+ writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
+
+ // readPacket reads and decrypts a packet of data. The
+ // returned packet may be overwritten by future calls of
+ // readPacket.
+ readPacket(seqnum uint32, r io.Reader) ([]byte, error)
+}
+
+// connectionState represents one side (read or write) of the
+// connection. This is necessary because each direction has its own
+// keys, and can even have its own algorithms
+type connectionState struct {
+ packetCipher
+ seqNum uint32
+ dir direction
+ pendingKeyChange chan packetCipher
+}
+
+// prepareKeyChange sets up key material for a keychange. The key changes in
+// both directions are triggered by reading and writing a msgNewKey packet
+// respectively.
+func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
+ if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
+ return err
+ } else {
+ t.reader.pendingKeyChange <- ciph
+ }
+
+ if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil {
+ return err
+ } else {
+ t.writer.pendingKeyChange <- ciph
+ }
+
+ return nil
+}
+
+func (t *transport) printPacket(p []byte, write bool) {
+ if len(p) == 0 {
+ return
+ }
+ who := "server"
+ if t.isClient {
+ who = "client"
+ }
+ what := "read"
+ if write {
+ what = "write"
+ }
+
+ log.Println(what, who, p[0])
+}
+
+// Read and decrypt next packet.
+func (t *transport) readPacket() (p []byte, err error) {
+ for {
+ p, err = t.reader.readPacket(t.bufReader)
+ if err != nil {
+ break
+ }
+ if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
+ break
+ }
+ }
+ if debugTransport {
+ t.printPacket(p, false)
+ }
+
+ return p, err
+}
+
+func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
+ packet, err := s.packetCipher.readPacket(s.seqNum, r)
+ s.seqNum++
+ if err == nil && len(packet) == 0 {
+ err = errors.New("ssh: zero length packet")
+ }
+
+ if len(packet) > 0 {
+ switch packet[0] {
+ case msgNewKeys:
+ select {
+ case cipher := <-s.pendingKeyChange:
+ s.packetCipher = cipher
+ default:
+ return nil, errors.New("ssh: got bogus newkeys message.")
+ }
+
+ case msgDisconnect:
+ // Transform a disconnect message into an
+ // error. Since this is lowest level at which
+ // we interpret message types, doing it here
+ // ensures that we don't have to handle it
+ // elsewhere.
+ var msg disconnectMsg
+ if err := Unmarshal(packet, &msg); err != nil {
+ return nil, err
+ }
+ return nil, &msg
+ }
+ }
+
+ // The packet may point to an internal buffer, so copy the
+ // packet out here.
+ fresh := make([]byte, len(packet))
+ copy(fresh, packet)
+
+ return fresh, err
+}
+
+func (t *transport) writePacket(packet []byte) error {
+ if debugTransport {
+ t.printPacket(packet, true)
+ }
+ return t.writer.writePacket(t.bufWriter, t.rand, packet)
+}
+
+func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
+ changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
+
+ err := s.packetCipher.writePacket(s.seqNum, w, rand, packet)
+ if err != nil {
+ return err
+ }
+ if err = w.Flush(); err != nil {
+ return err
+ }
+ s.seqNum++
+ if changeKeys {
+ select {
+ case cipher := <-s.pendingKeyChange:
+ s.packetCipher = cipher
+ default:
+ panic("ssh: no key material for msgNewKeys")
+ }
+ }
+ return err
+}
+
+func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
+ t := &transport{
+ bufReader: bufio.NewReader(rwc),
+ bufWriter: bufio.NewWriter(rwc),
+ rand: rand,
+ reader: connectionState{
+ packetCipher: &streamPacketCipher{cipher: noneCipher{}},
+ pendingKeyChange: make(chan packetCipher, 1),
+ },
+ writer: connectionState{
+ packetCipher: &streamPacketCipher{cipher: noneCipher{}},
+ pendingKeyChange: make(chan packetCipher, 1),
+ },
+ Closer: rwc,
+ }
+ t.isClient = isClient
+
+ if isClient {
+ t.reader.dir = serverKeys
+ t.writer.dir = clientKeys
+ } else {
+ t.reader.dir = clientKeys
+ t.writer.dir = serverKeys
+ }
+
+ return t
+}
+
+type direction struct {
+ ivTag []byte
+ keyTag []byte
+ macKeyTag []byte
+}
+
+var (
+ serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
+ clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
+)
+
+// generateKeys generates key material for IV, MAC and encryption.
+func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) {
+ cipherMode := cipherModes[algs.Cipher]
+ macMode := macModes[algs.MAC]
+
+ iv = make([]byte, cipherMode.ivSize)
+ key = make([]byte, cipherMode.keySize)
+ macKey = make([]byte, macMode.keySize)
+
+ generateKeyMaterial(iv, d.ivTag, kex)
+ generateKeyMaterial(key, d.keyTag, kex)
+ generateKeyMaterial(macKey, d.macKeyTag, kex)
+ return
+}
+
+// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
+// described in RFC 4253, section 6.4. direction should either be serverKeys
+// (to setup server->client keys) or clientKeys (for client->server keys).
+func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
+ iv, key, macKey := generateKeys(d, algs, kex)
+
+ if algs.Cipher == gcmCipherID {
+ return newGCMCipher(iv, key, macKey)
+ }
+
+ if algs.Cipher == aes128cbcID {
+ return newAESCBCCipher(iv, key, macKey, algs)
+ }
+
+ if algs.Cipher == tripledescbcID {
+ return newTripleDESCBCCipher(iv, key, macKey, algs)
+ }
+
+ c := &streamPacketCipher{
+ mac: macModes[algs.MAC].new(macKey),
+ etm: macModes[algs.MAC].etm,
+ }
+ c.macResult = make([]byte, c.mac.Size())
+
+ var err error
+ c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
+ if err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+// generateKeyMaterial fills out with key material generated from tag, K, H
+// and sessionId, as specified in RFC 4253, section 7.2.
+func generateKeyMaterial(out, tag []byte, r *kexResult) {
+ var digestsSoFar []byte
+
+ h := r.Hash.New()
+ for len(out) > 0 {
+ h.Reset()
+ h.Write(r.K)
+ h.Write(r.H)
+
+ if len(digestsSoFar) == 0 {
+ h.Write(tag)
+ h.Write(r.SessionID)
+ } else {
+ h.Write(digestsSoFar)
+ }
+
+ digest := h.Sum(nil)
+ n := copy(out, digest)
+ out = out[n:]
+ if len(out) > 0 {
+ digestsSoFar = append(digestsSoFar, digest...)
+ }
+ }
+}
+
+const packageVersion = "SSH-2.0-Go"
+
+// Sends and receives a version line. The versionLine string should
+// be US ASCII, start with "SSH-2.0-", and should not include a
+// newline. exchangeVersions returns the other side's version line.
+func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
+ // Contrary to the RFC, we do not ignore lines that don't
+ // start with "SSH-2.0-" to make the library usable with
+ // nonconforming servers.
+ for _, c := range versionLine {
+ // The spec disallows non US-ASCII chars, and
+ // specifically forbids null chars.
+ if c < 32 {
+ return nil, errors.New("ssh: junk character in version line")
+ }
+ }
+ if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
+ return
+ }
+
+ them, err = readVersion(rw)
+ return them, err
+}
+
+// maxVersionStringBytes is the maximum number of bytes that we'll
+// accept as a version string. RFC 4253 section 4.2 limits this at 255
+// chars
+const maxVersionStringBytes = 255
+
+// Read version string as specified by RFC 4253, section 4.2.
+func readVersion(r io.Reader) ([]byte, error) {
+ versionString := make([]byte, 0, 64)
+ var ok bool
+ var buf [1]byte
+
+ for len(versionString) < maxVersionStringBytes {
+ _, err := io.ReadFull(r, buf[:])
+ if err != nil {
+ return nil, err
+ }
+ // The RFC says that the version should be terminated with \r\n
+ // but several SSH servers actually only send a \n.
+ if buf[0] == '\n' {
+ ok = true
+ break
+ }
+
+ // non ASCII chars are disallowed, but we are lenient,
+ // since Go doesn't use null-terminated strings.
+
+ // The RFC allows a comment after a space, however,
+ // all of it (version and comments) goes into the
+ // session hash.
+ versionString = append(versionString, buf[0])
+ }
+
+ if !ok {
+ return nil, errors.New("ssh: overflow reading version string")
+ }
+
+ // There might be a '\r' on the end which we should remove.
+ if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
+ versionString = versionString[:len(versionString)-1]
+ }
+ return versionString, nil
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 72ce57231..d79c80a67 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -202,12 +202,30 @@
"revisionTime": "2015-03-14T17:03:34Z"
},
{
+ "checksumSHA1": "2gmvVTDCks8cPhpmyDlvm0sbrXE=",
+ "path": "github.com/naoina/toml",
+ "revision": "ac014c6b6502388d89a85552b7208b8da7cfe104",
+ "revisionTime": "2017-04-10T21:57:17Z"
+ },
+ {
+ "checksumSHA1": "xZBlSMT5o/A+EDOro6KbfHZwSNc=",
+ "path": "github.com/naoina/toml/ast",
+ "revision": "eb52202f758b98ac5b1a8eb26f36455205d688f0",
+ "revisionTime": "2017-04-03T15:03:10Z"
+ },
+ {
"checksumSHA1": "R1h9XHH3dTmLq7yKL9/uW0xFwfs=",
"path": "github.com/nsf/termbox-go",
"revision": "3540b76b9c77679aeffd0a47e00243fb0ce47133",
"revisionTime": "2017-02-11T01:27:00Z"
},
{
+ "checksumSHA1": "h+oCMj21PiQfIdBog0eyUtF1djs=",
+ "path": "github.com/olekukonko/tablewriter",
+ "revision": "febf2d34b54a69ce7530036c7503b1c9fbfdf0bb",
+ "revisionTime": "2017-01-28T05:05:32Z"
+ },
+ {
"checksumSHA1": "Se195FlZ160eaEk/uVx4KdTPSxU=",
"path": "github.com/pborman/uuid",
"revision": "1b00554d822231195d1babd97ff4a781231955c9",
@@ -370,6 +388,24 @@
"revisionTime": "2017-02-08T20:51:15Z"
},
{
+ "checksumSHA1": "C1KKOxFoW7/W/NFNpiXK+boguNo=",
+ "path": "golang.org/x/crypto/curve25519",
+ "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
+ "revisionTime": "2017-03-17T13:29:17Z"
+ },
+ {
+ "checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=",
+ "path": "golang.org/x/crypto/ed25519",
+ "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
+ "revisionTime": "2017-03-17T13:29:17Z"
+ },
+ {
+ "checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=",
+ "path": "golang.org/x/crypto/ed25519/internal/edwards25519",
+ "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
+ "revisionTime": "2017-03-17T13:29:17Z"
+ },
+ {
"checksumSHA1": "IIhFTrLlmlc6lEFSitqi4aw2lw0=",
"path": "golang.org/x/crypto/openpgp",
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8",
@@ -424,6 +460,18 @@
"revisionTime": "2017-02-08T20:51:15Z"
},
{
+ "checksumSHA1": "fsrFs762jlaILyqqQImS1GfvIvw=",
+ "path": "golang.org/x/crypto/ssh",
+ "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
+ "revisionTime": "2017-03-17T13:29:17Z"
+ },
+ {
+ "checksumSHA1": "xiderUuvye8Kpn7yX3niiJg32bE=",
+ "path": "golang.org/x/crypto/ssh/terminal",
+ "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
+ "revisionTime": "2017-03-17T13:29:17Z"
+ },
+ {
"checksumSHA1": "Y+HGqEkYM15ir+J93MEaHdyFy0c=",
"path": "golang.org/x/net/context",
"revision": "a6577fac2d73be281a500b310739095313165611",
diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go
index d705c622f..42a0671a3 100644
--- a/whisper/mailserver/mailserver.go
+++ b/whisper/mailserver/mailserver.go
@@ -1,18 +1,18 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of go-ethereum.
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
-// go-ethereum is distributed in the hope that it will be useful,
+// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Lesser General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package mailserver
diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go
index ffdff3191..15652ea4a 100644
--- a/whisper/mailserver/server_test.go
+++ b/whisper/mailserver/server_test.go
@@ -1,18 +1,18 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of go-ethereum.
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
-// go-ethereum is distributed in the hope that it will be useful,
+// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Lesser General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package mailserver