devtools::install_github("hrbrmstr/speedtest")
Downloading GitHub repo hrbrmstr/speedtest@master
from URL https://api.github.com/repos/hrbrmstr/speedtest/zipball/master
Installing speedtest
"C:/PROGRA~1/R/R-34~1.2/bin/x64/R" --no-site-file --no-environ --no-save \
--no-restore --quiet CMD INSTALL \
"C:/Users/rcleo/AppData/Local/Temp/RtmpwXfh7H/devtools36e05e812c57/hrbrmstr-speedtest-f635a99" \
--library="C:/Users/rcleo/Documents/R/win-library/3.4" --install-tests
* installing *source* package 'speedtest' ...
** R
** tests
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
*** arch - i386
*** arch - x64
* DONE (speedtest)
library(speedtest)
Warning message:
In readLines(input, encoding = "UTF-8") :
incomplete final line found on 'C:/Users/rcleo/Documents/R/win-library/3.4/raw/rmarkdown/templates/reserveReview/template.yaml'
library(stringi)
library(hrbrthemes)
library(ggbeeswarm)
Carregando pacotes exigidos: ggplot2
library(tidyverse)
[37m-- [1mAttaching packages[22m --------------------------------------- tidyverse 1.2.0 --[39m
[37m[32mv[37m [34mtibble [37m 1.3.4 [32mv[37m [34mpurrr [37m 0.2.4
[32mv[37m [34mtidyr [37m 0.7.2 [32mv[37m [34mdplyr [37m 0.7.4
[32mv[37m [34mreadr [37m 1.1.1 [32mv[37m [34mstringr[37m 1.2.0
[32mv[37m [34mtibble [37m 1.3.4 [32mv[37m [34mforcats[37m 0.2.0[39m
[37m-- [1mConflicts[22m ------------------------------------------ tidyverse_conflicts() --
[31mx[37m [34mdplyr[37m::[32mfilter()[37m masks [34mstats[37m::filter()
[31mx[37m [34mdplyr[37m::[32mlag()[37m masks [34mstats[37m::lag()[39m
config <- spd_config()
servers <- spd_servers(config=config)
closest_servers <- spd_closest_servers(servers, config=config)
only_the_best_severs <- spd_best_servers(closest_servers, config)
glimpse(spd_download_test(closest_servers[1,], config=config))
|=========== | 20% ~1 s remaining
|================ | 30% ~1 s remaining
|====================== | 40% ~1 s remaining
|=========================== | 50% ~1 s remaining
|================================= | 60% ~2 s remaining
|====================================== | 70% ~2 s remaining
|============================================ | 80% ~2 s remaining
|================================================= | 90% ~2 s remaining
|=======================================================|100% ~0 s remaining Observations: 1
Variables: 15
$ url <chr> "http://st1.zamix.com.br/speedtest/upload.php"
$ lat <dbl> -22.5228
$ lng <dbl> -44.1039
$ name <chr> "Volta Redonda"
$ country <chr> "Brazil"
$ cc <chr> "BR"
$ sponsor <chr> "SuperOnda/Zamix"
$ id <chr> "6536"
$ host <chr> "st1.zamix.com.br:8080"
$ url2 <chr> "http://st2.zamix.com.br/speedtest/upload.php"
$ min <dbl> 15.35344
$ mean <dbl> 25.90716
$ median <dbl> 24.74659
$ max <dbl> 40.07601
$ sd <dbl> 7.048748
## Observations: 1
## Variables: 15
## $ url "http://speed0.xcelx.net/speedtest/upload.php"
## $ lat 42.3875
## $ lng -71.1
## $ name "Somerville, MA"
## $ country "United States"
## $ cc "US"
## $ sponsor "Axcelx Technologies LLC"
## $ id "5960"
## $ host "speed0.xcelx.net:8080"
## $ url2 "http://speed1.xcelx.net/speedtest/upload.php"
## $ min 14.40439
## $ mean 60.06834
## $ median 55.28457
## $ max 127.9436
## $ sd 34.20695
glimpse(spd_upload_test(only_the_best_severs[1,], config=config))
|================== | 33% ~1 s remaining
|=========================== | 50% ~1 s remaining
|==================================== | 67% ~1 s remaining
|============================================= | 83% ~1 s remaining
|=======================================================|100% ~0 s remaining Observations: 1
Variables: 17
$ total_time <dbl> 0.219
$ retrieval_time <dbl> 0
$ url <chr> "http://189.50.144.249/speedtest/speedtest/upload.php"
$ lat <dbl> -22.1124
$ lng <dbl> -45.028
$ name <chr> "Sao Lourenco"
$ country <chr> "Brazil"
$ cc <chr> "BR"
$ sponsor <chr> "StarWeb"
$ id <chr> "14377"
$ host <chr> "189.50.144.249:8080"
$ url2 <chr> NA
$ min <dbl> 4.096
$ mean <dbl> 6.273526
$ median <dbl> 6.250487
$ max <dbl> 8.445378
$ sd <dbl> 1.749677
## Observations: 1
## Variables: 18
## $ ping_time 0.02712567
## $ total_time 0.059917
## $ retrieval_time 2.3e-05
## $ url "http://speed0.xcelx.net/speedtest/upload.php"
## $ lat 42.3875
## $ lng -71.1
## $ name "Somerville, MA"
## $ country "United States"
## $ cc "US"
## $ sponsor "Axcelx Technologies LLC"
## $ id "5960"
## $ host "speed0.xcelx.net:8080"
## $ url2 "http://speed1.xcelx.net/speedtest/upload.php"
## $ min 6.240858
## $ mean 9.527599
## $ median 9.303148
## $ max 12.56686
## $ sd 2.451778
Welcome!
Here you will find daily news and tutorials about R, contributed by over 750 bloggers.
There are many ways to follow us -
By e-mail:
On Facebook:
If you are an R blogger yourself you are invited to add your own R content feed to this site (Non-English R bloggers should add themselves- here)
RSS Jobs for R-users
Rays Research & Development Analyst
PROGRAMMER/SOFTWARE DEVELOPMENT ENGINEER/COMPUTATIONAL AND MACHINE LEARNING SPECIALIST
Quantitative Econometrician @ San Francisco, California, U.S.
Postdoctoral Research Fellow in Healthcare Systems Engineering @ Maryland, U.S.
R Programmer & Statistician for Academic Research
Popular Searches
Recent Posts
Measuring & Monitoring Internet Speed with R
Creating integer64 and nanotime vectors in C++
Stan Roundup, 10 November 2017
.rprofile: Mara Averick
Gold-Mining – Week 10 (2017)
Recap: EARL Boston 2017
Announcing “Introduction to the Tidyverse”, my new DataCamp course
R live class | R with Database and Big Data | Nov 21-22 Milan
How Happy is Your Country? — Happy Planet Index Visualized
Formal ways to compare forecasting models: Rolling windows
Introduction to Visualizing Asset Returns
bridgesampling [R package]
Calculating the house edge of a slot machine, with R
Creating Reporting Template with Glue in R
R / Finance 2018 Call for Papers
Other sites
Jobs for R-users
SAS blogs
Measuring & Monitoring Internet Speed with R
November 11, 2017
By hrbrmstr
inShare4
(This article was first published on R – rud.is, and kindly contributed to R-bloggers)
72
SHARES
Share
Tweet
Working remotely has many benefits, but if you work remotely in an area like, say, rural Maine, one of those benefits is not massively speedy internet connections. Being able to go fast and furious on the internet is one of the many things I miss about our time in Seattle and it is unlikely that we’ll be seeing Google Fiber in my small town any time soon. One other issue is that residential plans from evil giants like Comcast come with things like “bandwidth caps”. I suspect many WFH-ers can live within those limits, but I work with internet-scale data and often shunt extracts or whole datasets to the DatCave™ server farm for local processing. As such, I pay an extra penalty as a Comcast “Business-class” user that has little benefit besides getting slightly higher QoS and some faster service response times when there are issues.
Why go into all that? Well, I recently decided to bump up the connection from 100 Mb/s to 150 Mb/s (and managed to do so w/o increasing the overall monthly bill at all) but wanted to have a way to regularly verify I am getting what I’m paying for without having to go to an interactive “speed test” site.
There’s a handy speedtest-cli攼㹤愼㸰戼㹤攼㹤戼㸴㤼㸷, which is a python-based module with a command-line interface that can perform speed tests against Ookla’s legacy server. (You’ll notice that link forwards you to their new socket-based test service; neither speedtest-cli nor the code in the package being shown here uses that yet)
I run plenty of ruby, python, node and go (et al) programs on the command-line, but I wanted a way to measure this in R-proper (storing individual test results) on a regular basis as well as run something from the command-line whenever I wanted an interactive test. Thus begat speedtest攼㹤愼㸰戼㹤攼㹤戼㸴㤼㸷.
Testing the Need for Speed
After you devtools::install_github("hrbrmstr/speedtest") the package, you can either type speedtest::spd_test() at an R console or Rscript --quiet -e 'speedtest::spd_test()' on the command-line to see something like the following:
What you see there is a short-hand version of what’s available in the package:
spd_best_servers: Find “best” servers (latency-wise) from master server list
spd_closest_servers: Find “closest” servers (geography-wise) from master server list
spd_compute_bandwidth: Compute bandwidth from bytes transferred and time taken
spd_config: Retrieve client configuration information for the speedtest
spd_download_test: Perform a download speed/bandwidth test
spd_servers: Retrieve a list of SpeedTest servers
spd_upload_test: Perform an upload speed/bandwidth test
spd_test: Test your internet speed/bandwidth
The general idiom is to grab the test configuration file, collect the master list of servers, figure out which servers you’re going to test against and perform upload/download tests + collect the resultant statistics:
library(speedtest)
library(stringi)
library(hrbrthemes)
library(ggbeeswarm)
library(tidyverse)
config <- spd_config()
servers <- spd_servers(config=config)
closest_servers <- spd_closest_servers(servers, config=config)
only_the_best_severs <- spd_best_servers(closest_servers, config)
glimpse(spd_download_test(closest_servers[1,], config=config))
## Observations: 1
## Variables: 15
## $ url "http://speed0.xcelx.net/speedtest/upload.php"
## $ lat 42.3875
## $ lng -71.1
## $ name "Somerville, MA"
## $ country "United States"
## $ cc "US"
## $ sponsor "Axcelx Technologies LLC"
## $ id "5960"
## $ host "speed0.xcelx.net:8080"
## $ url2 "http://speed1.xcelx.net/speedtest/upload.php"
## $ min 14.40439
## $ mean 60.06834
## $ median 55.28457
## $ max 127.9436
## $ sd 34.20695
glimpse(spd_upload_test(only_the_best_severs[1,], config=config))
## Observations: 1
## Variables: 18
## $ ping_time 0.02712567
## $ total_time 0.059917
## $ retrieval_time 2.3e-05
## $ url "http://speed0.xcelx.net/speedtest/upload.php"
## $ lat 42.3875
## $ lng -71.1
## $ name "Somerville, MA"
## $ country "United States"
## $ cc "US"
## $ sponsor "Axcelx Technologies LLC"
## $ id "5960"
## $ host "speed0.xcelx.net:8080"
## $ url2 "http://speed1.xcelx.net/speedtest/upload.php"
## $ min 6.240858
## $ mean 9.527599
## $ median 9.303148
## $ max 12.56686
## $ sd 2.451778
set.seed(8675309)
bind_rows(
closest_servers[1:3,] %>%
mutate(type="closest"),
only_the_best_severs[1:3,] %>%
mutate(type="best"),
filter(servers, !(id %in% c(closest_servers[1:3,]$id, only_the_best_severs[1:3,]$id))) %>%
sample_n(3) %>%
mutate(type="random")
) %>%
group_by(type) %>%
ungroup() -> to_compare
select(to_compare, sponsor, name, country, host, type)
map_df(1:nrow(to_compare), ~{
spd_download_test(to_compare[.x,], config=config, summarise=FALSE, timeout=30)
}) -> dl_results_full
|========== | 20% ~3 s remaining
|================ | 30% ~6 s remaining
|===================== | 40% ~7 s remaining
|=========================== | 50% ~7 s remaining
|================================ | 60% ~7 s remaining
|===================================== | 70% ~6 s remaining
|=========================================== | 80% ~9 s remaining
|================================================ | 90% ~7 s remaining
|======================================================|100% ~0 s remaining
|========== | 20% ~4 s remaining
|================ | 30% ~4 s remaining
|===================== | 40% ~4 s remaining
|=========================== | 50% ~5 s remaining
|================================ | 60% ~8 s remaining
|===================================== | 70% ~12 s remaining
|=========================================== | 80% ~12 s remaining
|================================================ | 90% ~8 s remaining
|======================================================|100% ~0 s remaining
|========== | 20% ~3 s remaining
|================ | 30% ~13 s remaining
|===================== | 40% ~22 s remaining
|=========================== | 50% ~22 s remaining
|================================ | 60% ~26 s remaining
|===================================== | 70% ~29 s remaining
|=========================================== | 80% ~25 s remaining
|================================================ | 90% ~14 s remaining
|======================================================|100% ~0 s remaining
|========== | 20% ~3 s remaining
|================ | 30% ~9 s remaining
|===================== | 40% ~31 s remaining
|=========================== | 50% ~50 s remaining
|================================ | 60% ~54 s remaining
|===================================== | 70% ~47 s remaining
|=========================================== | 80% ~35 s remaining
|================================================ | 90% ~19 s remaining
|======================================================|100% ~0 s remaining
|========== | 20% ~5 s remaining
|================ | 30% ~5 s remaining
|===================== | 40% ~8 s remaining
|=========================== | 50% ~13 s remaining
|================================ | 60% ~16 s remaining
|===================================== | 70% ~17 s remaining
|=========================================== | 80% ~16 s remaining
|================================================ | 90% ~9 s remaining
|======================================================|100% ~0 s remaining
|========== | 20% ~4 s remaining
|================ | 30% ~5 s remaining
|===================== | 40% ~12 s remaining
|=========================== | 50% ~14 s remaining
|================================ | 60% ~11 s remaining
|===================================== | 70% ~20 s remaining
|=========================================== | 80% ~14 s remaining
|================================================ | 90% ~10 s remaining
|======================================================|100% ~0 s remaining
|============ | 20% ~16 s remaining
|================== | 30% ~11 s remaining
|======================== | 40% ~9 s remaining
|=============================== | 50% ~8 s remaining
|===================================== | 60% ~9 s remaining
|=========================================== | 70% ~19 s remaining
|================================================= | 80% ~18 s remaining
|======================================================= | 90% ~11 s remaining
|==============================================================|100% ~0 s remaining
|============ | 20% ~21 s remaining
|================== | 30% ~28 s remaining
|======================== | 40% ~38 s remaining
|=============================== | 50% ~55 s remaining
|===================================== | 60% ~57 s remaining
|=========================================== | 70% ~50 s remaining
|================================================= | 80% ~36 s remaining
|======================================================= | 90% ~20 s remaining
|==============================================================|100% ~0 s remaining
|============ | 20% ~29 s remaining
|================== | 30% ~1 m remaining
|======================== | 40% ~1 m remaining
|=============================== | 50% ~1 m remaining
|===================================== | 60% ~1 m remaining
|=========================================== | 70% ~58 s remaining
|================================================= | 80% ~42 s remaining
|======================================================= | 90% ~22 s remaining
|==============================================================|100% ~0 s remaining
mutate(dl_results_full, type=stri_trans_totitle(type)) %>%
ggplot(aes(type, bw, fill=type)) +
geom_quasirandom(aes(size=size, color=type), width=0.15, shape=21, stroke=0.25) +
scale_y_continuous(expand=c(0,5), labels=c(sprintf("%s", seq(0,150,50)), "200 Mb/s"), limits=c(0,200)) +
scale_size(range=c(2,6)) +
scale_color_manual(values=c(Random="#b2b2b2", Best="#2b2b2b", Closest="#2b2b2b")) +
scale_fill_ipsum() +
labs(x=NULL, y=NULL, title="Download bandwidth test by selected server type",
subtitle="Circle size scaled by size of file used in that speed test") +
theme_ipsum_rc(grid="Y") +
theme(legend.position="none")

bind_rows(
closest_servers[1:3,] %>% mutate(type="closest"),
only_the_best_severs[1:3,] %>% mutate(type="best")
) %>%
distinct(.keep_all=TRUE) -> to_compare
select(to_compare, sponsor, name, country, host, type)
map_df(1:nrow(to_compare), ~{
spd_upload_test(to_compare[.x,], config=config, summarise=FALSE, timeout=30)
}) -> ul_results_full
|=========================== | 33% ~17 s remaining
|======================================== | 50% ~10 s remaining
|====================================================== | 67% ~6 s remaining
|=================================================================== | 83% ~3 s remaining
|=================================================================================|100% ~0 s remaining
|=========================== | 33% ~1 s remaining
|======================================== | 50% ~1 s remaining
|====================================================== | 67% ~1 s remaining
|=================================================================== | 83% ~1 s remaining
|=================================================================================|100% ~0 s remaining
|=========================== | 33% ~8 s remaining
|======================================== | 50% ~5 s remaining
|====================================================== | 67% ~3 s remaining
|=================================================================== | 83% ~1 s remaining
|=================================================================================|100% ~0 s remaining
|=========================== | 33% ~1 s remaining
|======================================== | 50% ~1 s remaining
|====================================================== | 67% ~1 s remaining
|=================================================================== | 83% ~1 s remaining
|=================================================================================|100% ~0 s remaining
|=========================== | 33% ~3 s remaining
|======================================== | 50% ~2 s remaining
|====================================================== | 67% ~1 s remaining
|=================================================================== | 83% ~1 s remaining
|=================================================================================|100% ~0 s remaining
|=========================== | 33% ~1 s remaining
|======================================== | 50% ~1 s remaining
|====================================================== | 67% ~1 s remaining
|=================================================================== | 83% ~1 s remaining
|=================================================================================|100% ~0 s remaining
ggplot(ul_results_full, aes(x="Upload Test", y=bw)) +
geom_quasirandom(aes(size=size, fill="col"), width=0.1, shape=21, stroke=0.25, color="#2b2b2b") +
scale_y_continuous(expand=c(0,0.5), breaks=seq(0,16,4),
labels=c(sprintf("%s", seq(0,12,4)), "16 Mb/s"), limits=c(0,16)) +
scale_size(range=c(2,6)) +
scale_fill_ipsum() +
labs(x=NULL, y=NULL, title="Upload bandwidth test by selected server type",
subtitle="Circle size scaled by size of file used in that speed test") +
theme_ipsum_rc(grid="Y") +
theme(legend.position="none")

LS0tDQp0aXRsZTogIk1lYXN1cmluZyAmIE1vbml0b3JpbmcgSW50ZXJuZXQgU3BlZWQgd2l0aCBSIg0KYXV0aG9yOiAiTGVvbmksIFIuIEMuIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaHJicm1zdHIvc3BlZWR0ZXN0IikgDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHNwZWVkdGVzdCkNCmxpYnJhcnkoc3RyaW5naSkNCmxpYnJhcnkoaHJicnRoZW1lcykNCmxpYnJhcnkoZ2diZWVzd2FybSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpjb25maWcgPC0gc3BkX2NvbmZpZygpDQoNCnNlcnZlcnMgPC0gc3BkX3NlcnZlcnMoY29uZmlnPWNvbmZpZykNCmNsb3Nlc3Rfc2VydmVycyA8LSBzcGRfY2xvc2VzdF9zZXJ2ZXJzKHNlcnZlcnMsIGNvbmZpZz1jb25maWcpDQpvbmx5X3RoZV9iZXN0X3NldmVycyA8LSBzcGRfYmVzdF9zZXJ2ZXJzKGNsb3Nlc3Rfc2VydmVycywgY29uZmlnKQ0KDQpnbGltcHNlKHNwZF9kb3dubG9hZF90ZXN0KGNsb3Nlc3Rfc2VydmVyc1sxLF0sIGNvbmZpZz1jb25maWcpKQ0KIyMgT2JzZXJ2YXRpb25zOiAxDQojIyBWYXJpYWJsZXM6IDE1DQojIyAkIHVybCAgICAgICJodHRwOi8vc3BlZWQwLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbGF0ICAgICAgNDIuMzg3NQ0KIyMgJCBsbmcgICAgICAtNzEuMQ0KIyMgJCBuYW1lICAgICAiU29tZXJ2aWxsZSwgTUEiDQojIyAkIGNvdW50cnkgICJVbml0ZWQgU3RhdGVzIg0KIyMgJCBjYyAgICAgICAiVVMiDQojIyAkIHNwb25zb3IgICJBeGNlbHggVGVjaG5vbG9naWVzIExMQyINCiMjICQgaWQgICAgICAgIjU5NjAiDQojIyAkIGhvc3QgICAgICJzcGVlZDAueGNlbHgubmV0OjgwODAiDQojIyAkIHVybDIgICAgICJodHRwOi8vc3BlZWQxLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbWluICAgICAgMTQuNDA0MzkNCiMjICQgbWVhbiAgICAgNjAuMDY4MzQNCiMjICQgbWVkaWFuICAgNTUuMjg0NTcNCiMjICQgbWF4ICAgICAgMTI3Ljk0MzYNCiMjICQgc2QgICAgICAgMzQuMjA2OTUNCg0KZ2xpbXBzZShzcGRfdXBsb2FkX3Rlc3Qob25seV90aGVfYmVzdF9zZXZlcnNbMSxdLCBjb25maWc9Y29uZmlnKSkNCiMjIE9ic2VydmF0aW9uczogMQ0KIyMgVmFyaWFibGVzOiAxOA0KIyMgJCBwaW5nX3RpbWUgICAgICAgMC4wMjcxMjU2Nw0KIyMgJCB0b3RhbF90aW1lICAgICAgMC4wNTk5MTcNCiMjICQgcmV0cmlldmFsX3RpbWUgIDIuM2UtMDUNCiMjICQgdXJsICAgICAgICAgICAgICJodHRwOi8vc3BlZWQwLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbGF0ICAgICAgICAgICAgIDQyLjM4NzUNCiMjICQgbG5nICAgICAgICAgICAgIC03MS4xDQojIyAkIG5hbWUgICAgICAgICAgICAiU29tZXJ2aWxsZSwgTUEiDQojIyAkIGNvdW50cnkgICAgICAgICAiVW5pdGVkIFN0YXRlcyINCiMjICQgY2MgICAgICAgICAgICAgICJVUyINCiMjICQgc3BvbnNvciAgICAgICAgICJBeGNlbHggVGVjaG5vbG9naWVzIExMQyINCiMjICQgaWQgICAgICAgICAgICAgICI1OTYwIg0KIyMgJCBob3N0ICAgICAgICAgICAgInNwZWVkMC54Y2VseC5uZXQ6ODA4MCINCiMjICQgdXJsMiAgICAgICAgICAgICJodHRwOi8vc3BlZWQxLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbWluICAgICAgICAgICAgIDYuMjQwODU4DQojIyAkIG1lYW4gICAgICAgICAgICA5LjUyNzU5OQ0KIyMgJCBtZWRpYW4gICAgICAgICAgOS4zMDMxNDgNCiMjICQgbWF4ICAgICAgICAgICAgIDEyLjU2Njg2DQojIyAkIHNkICAgICAgICAgICAgICAyLjQ1MTc3OA0KYGBgDQpgYGB7cn0NCg0KV2VsY29tZSENCkhlcmUgeW91IHdpbGwgZmluZCBkYWlseSBuZXdzIGFuZCB0dXRvcmlhbHMgYWJvdXQgUiwgY29udHJpYnV0ZWQgYnkgb3ZlciA3NTAgYmxvZ2dlcnMuDQpUaGVyZSBhcmUgbWFueSB3YXlzIHRvIGZvbGxvdyB1cyAtDQpCeSBlLW1haWw6DQpPbiBGYWNlYm9vazoNCklmIHlvdSBhcmUgYW4gUiBibG9nZ2VyIHlvdXJzZWxmIHlvdSBhcmUgaW52aXRlZCB0byBhZGQgeW91ciBvd24gUiBjb250ZW50IGZlZWQgdG8gdGhpcyBzaXRlIChOb24tRW5nbGlzaCBSIGJsb2dnZXJzIHNob3VsZCBhZGQgdGhlbXNlbHZlcy0gaGVyZSkNClJTUyBKb2JzIGZvciBSLXVzZXJzDQoNCiAgICBSYXlzIFJlc2VhcmNoICYgRGV2ZWxvcG1lbnQgQW5hbHlzdA0KICAgIFBST0dSQU1NRVIvU09GVFdBUkUgREVWRUxPUE1FTlQgRU5HSU5FRVIvQ09NUFVUQVRJT05BTCBBTkQgTUFDSElORSBMRUFSTklORyBTUEVDSUFMSVNUDQogICAgUXVhbnRpdGF0aXZlIEVjb25vbWV0cmljaWFuIEAgU2FuIEZyYW5jaXNjbywgQ2FsaWZvcm5pYSwgVS5TLg0KICAgIFBvc3Rkb2N0b3JhbCBSZXNlYXJjaCBGZWxsb3cgaW4gSGVhbHRoY2FyZSBTeXN0ZW1zIEVuZ2luZWVyaW5nIEAgTWFyeWxhbmQsIFUuUy4NCiAgICBSIFByb2dyYW1tZXIgJiBTdGF0aXN0aWNpYW4gZm9yIEFjYWRlbWljIFJlc2VhcmNoDQoNClBvcHVsYXIgU2VhcmNoZXMNClJlY2VudCBQb3N0cw0KDQogICAgTWVhc3VyaW5nICYgTW9uaXRvcmluZyBJbnRlcm5ldCBTcGVlZCB3aXRoIFINCiAgICBDcmVhdGluZyBpbnRlZ2VyNjQgYW5kIG5hbm90aW1lIHZlY3RvcnMgaW4gQysrDQogICAgU3RhbiBSb3VuZHVwLCAxMCBOb3ZlbWJlciAyMDE3DQogICAgLnJwcm9maWxlOiBNYXJhIEF2ZXJpY2sNCiAgICBHb2xkLU1pbmluZyDigJMgV2VlayAxMCAoMjAxNykNCiAgICBSZWNhcDogRUFSTCBCb3N0b24gMjAxNw0KICAgIEFubm91bmNpbmcg4oCcSW50cm9kdWN0aW9uIHRvIHRoZSBUaWR5dmVyc2XigJ0sIG15IG5ldyBEYXRhQ2FtcCBjb3Vyc2UNCiAgICBSIGxpdmUgY2xhc3MgfCBSIHdpdGggRGF0YWJhc2UgYW5kIEJpZyBEYXRhIHwgTm92IDIxLTIyIE1pbGFuDQogICAgSG93IEhhcHB5IGlzIFlvdXIgQ291bnRyeT/igIrigJTigIpIYXBweSBQbGFuZXQgSW5kZXggVmlzdWFsaXplZA0KICAgIEZvcm1hbCB3YXlzIHRvIGNvbXBhcmUgZm9yZWNhc3RpbmcgbW9kZWxzOiBSb2xsaW5nIHdpbmRvd3MNCiAgICBJbnRyb2R1Y3Rpb24gdG8gVmlzdWFsaXppbmcgQXNzZXQgUmV0dXJucw0KICAgIGJyaWRnZXNhbXBsaW5nIFtSIHBhY2thZ2VdDQogICAgQ2FsY3VsYXRpbmcgdGhlIGhvdXNlIGVkZ2Ugb2YgYSBzbG90IG1hY2hpbmUsIHdpdGggUg0KICAgIENyZWF0aW5nIFJlcG9ydGluZyBUZW1wbGF0ZSB3aXRoIEdsdWUgaW4gUg0KICAgIFIgLyBGaW5hbmNlIDIwMTggQ2FsbCBmb3IgUGFwZXJzDQoNCk90aGVyIHNpdGVzDQoNCiAgICBKb2JzIGZvciBSLXVzZXJzDQogICAgU0FTIGJsb2dzDQoNCk1lYXN1cmluZyAmIE1vbml0b3JpbmcgSW50ZXJuZXQgU3BlZWQgd2l0aCBSDQpOb3ZlbWJlciAxMSwgMjAxNw0KQnkgaHJicm1zdHINCg0KaW5TaGFyZTQNCihUaGlzIGFydGljbGUgd2FzIGZpcnN0IHB1Ymxpc2hlZCBvbiBSIOKAkyBydWQuaXMsIGFuZCBraW5kbHkgY29udHJpYnV0ZWQgdG8gUi1ibG9nZ2VycykNCg0KNzINClNIQVJFUw0KU2hhcmUNClR3ZWV0DQoNCldvcmtpbmcgcmVtb3RlbHkgaGFzIG1hbnkgYmVuZWZpdHMsIGJ1dCBpZiB5b3Ugd29yayByZW1vdGVseSBpbiBhbiBhcmVhIGxpa2UsIHNheSwgcnVyYWwgTWFpbmUsIG9uZSBvZiB0aG9zZSBiZW5lZml0cyBpcyBub3QgbWFzc2l2ZWx5IHNwZWVkeSBpbnRlcm5ldCBjb25uZWN0aW9ucy4gQmVpbmcgYWJsZSB0byBnbyBmYXN0IGFuZCBmdXJpb3VzIG9uIHRoZSBpbnRlcm5ldCBpcyBvbmUgb2YgdGhlIG1hbnkgdGhpbmdzIEkgbWlzcyBhYm91dCBvdXIgdGltZSBpbiBTZWF0dGxlIGFuZCBpdCBpcyB1bmxpa2VseSB0aGF0IHdl4oCZbGwgYmUgc2VlaW5nIEdvb2dsZSBGaWJlciBpbiBteSBzbWFsbCB0b3duIGFueSB0aW1lIHNvb24uIE9uZSBvdGhlciBpc3N1ZSBpcyB0aGF0IHJlc2lkZW50aWFsIHBsYW5zIGZyb20gZXZpbCBnaWFudHMgbGlrZSBDb21jYXN0IGNvbWUgd2l0aCB0aGluZ3MgbGlrZSDigJxiYW5kd2lkdGggY2Fwc+KAnS4gSSBzdXNwZWN0IG1hbnkgV0ZILWVycyBjYW4gbGl2ZSB3aXRoaW4gdGhvc2UgbGltaXRzLCBidXQgSSB3b3JrIHdpdGggaW50ZXJuZXQtc2NhbGUgZGF0YSBhbmQgb2Z0ZW4gc2h1bnQgZXh0cmFjdHMgb3Igd2hvbGUgZGF0YXNldHMgdG8gdGhlIERhdENhdmXihKIgc2VydmVyIGZhcm0gZm9yIGxvY2FsIHByb2Nlc3NpbmcuIEFzIHN1Y2gsIEkgcGF5IGFuIGV4dHJhIHBlbmFsdHkgYXMgYSBDb21jYXN0IOKAnEJ1c2luZXNzLWNsYXNz4oCdIHVzZXIgdGhhdCBoYXMgbGl0dGxlIGJlbmVmaXQgYmVzaWRlcyBnZXR0aW5nIHNsaWdodGx5IGhpZ2hlciBRb1MgYW5kIHNvbWUgZmFzdGVyIHNlcnZpY2UgcmVzcG9uc2UgdGltZXMgd2hlbiB0aGVyZSBhcmUgaXNzdWVzLg0KDQpXaHkgZ28gaW50byBhbGwgdGhhdD8gV2VsbCwgSSByZWNlbnRseSBkZWNpZGVkIHRvIGJ1bXAgdXAgdGhlIGNvbm5lY3Rpb24gZnJvbSAxMDAgTWIvcyB0byAxNTAgTWIvcyAoYW5kIG1hbmFnZWQgdG8gZG8gc28gdy9vIGluY3JlYXNpbmcgdGhlIG92ZXJhbGwgbW9udGhseSBiaWxsIGF0IGFsbCkgYnV0IHdhbnRlZCB0byBoYXZlIGEgd2F5IHRvIHJlZ3VsYXJseSB2ZXJpZnkgSSBhbSBnZXR0aW5nIHdoYXQgSeKAmW0gcGF5aW5nIGZvciB3aXRob3V0IGhhdmluZyB0byBnbyB0byBhbiBpbnRlcmFjdGl2ZSDigJxzcGVlZCB0ZXN04oCdIHNpdGUuDQoNClRoZXJl4oCZcyBhIGhhbmR5IHNwZWVkdGVzdC1jbGnwn5SXLCB3aGljaCBpcyBhIHB5dGhvbi1iYXNlZCBtb2R1bGUgd2l0aCBhIGNvbW1hbmQtbGluZSBpbnRlcmZhY2UgdGhhdCBjYW4gcGVyZm9ybSBzcGVlZCB0ZXN0cyBhZ2FpbnN0IE9va2xh4oCZcyBsZWdhY3kgc2VydmVyLiAoWW914oCZbGwgbm90aWNlIHRoYXQgbGluayBmb3J3YXJkcyB5b3UgdG8gdGhlaXIgbmV3IHNvY2tldC1iYXNlZCB0ZXN0IHNlcnZpY2U7IG5laXRoZXIgc3BlZWR0ZXN0LWNsaSBub3IgdGhlIGNvZGUgaW4gdGhlIHBhY2thZ2UgYmVpbmcgc2hvd24gaGVyZSB1c2VzIHRoYXQgeWV0KQ0KDQpJIHJ1biBwbGVudHkgb2YgcnVieSwgcHl0aG9uLCBub2RlIGFuZCBnbyAoZXQgYWwpIHByb2dyYW1zIG9uIHRoZSBjb21tYW5kLWxpbmUsIGJ1dCBJIHdhbnRlZCBhIHdheSB0byBtZWFzdXJlIHRoaXMgaW4gUi1wcm9wZXIgKHN0b3JpbmcgaW5kaXZpZHVhbCB0ZXN0IHJlc3VsdHMpIG9uIGEgcmVndWxhciBiYXNpcyBhcyB3ZWxsIGFzIHJ1biBzb21ldGhpbmcgZnJvbSB0aGUgY29tbWFuZC1saW5lIHdoZW5ldmVyIEkgd2FudGVkIGFuIGludGVyYWN0aXZlIHRlc3QuIFRodXMgYmVnYXQgc3BlZWR0ZXN08J+Uly4NClRlc3RpbmcgdGhlIE5lZWQgZm9yIFNwZWVkDQoNCkFmdGVyIHlvdSBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImhyYnJtc3RyL3NwZWVkdGVzdCIpIHRoZSBwYWNrYWdlLCB5b3UgY2FuIGVpdGhlciB0eXBlIHNwZWVkdGVzdDo6c3BkX3Rlc3QoKSBhdCBhbiBSIGNvbnNvbGUgb3IgUnNjcmlwdCAtLXF1aWV0IC1lICdzcGVlZHRlc3Q6OnNwZF90ZXN0KCknIG9uIHRoZSBjb21tYW5kLWxpbmUgdG8gc2VlIHNvbWV0aGluZyBsaWtlIHRoZSBmb2xsb3dpbmc6DQoNCldoYXQgeW91IHNlZSB0aGVyZSBpcyBhIHNob3J0LWhhbmQgdmVyc2lvbiBvZiB3aGF04oCZcyBhdmFpbGFibGUgaW4gdGhlIHBhY2thZ2U6DQoNCiAgICBzcGRfYmVzdF9zZXJ2ZXJzOiBGaW5kIOKAnGJlc3TigJ0gc2VydmVycyAobGF0ZW5jeS13aXNlKSBmcm9tIG1hc3RlciBzZXJ2ZXIgbGlzdA0KICAgIHNwZF9jbG9zZXN0X3NlcnZlcnM6IEZpbmQg4oCcY2xvc2VzdOKAnSBzZXJ2ZXJzIChnZW9ncmFwaHktd2lzZSkgZnJvbSBtYXN0ZXIgc2VydmVyIGxpc3QNCiAgICBzcGRfY29tcHV0ZV9iYW5kd2lkdGg6IENvbXB1dGUgYmFuZHdpZHRoIGZyb20gYnl0ZXMgdHJhbnNmZXJyZWQgYW5kIHRpbWUgdGFrZW4NCiAgICBzcGRfY29uZmlnOiBSZXRyaWV2ZSBjbGllbnQgY29uZmlndXJhdGlvbiBpbmZvcm1hdGlvbiBmb3IgdGhlIHNwZWVkdGVzdA0KICAgIHNwZF9kb3dubG9hZF90ZXN0OiBQZXJmb3JtIGEgZG93bmxvYWQgc3BlZWQvYmFuZHdpZHRoIHRlc3QNCiAgICBzcGRfc2VydmVyczogUmV0cmlldmUgYSBsaXN0IG9mIFNwZWVkVGVzdCBzZXJ2ZXJzDQogICAgc3BkX3VwbG9hZF90ZXN0OiBQZXJmb3JtIGFuIHVwbG9hZCBzcGVlZC9iYW5kd2lkdGggdGVzdA0KICAgIHNwZF90ZXN0OiBUZXN0IHlvdXIgaW50ZXJuZXQgc3BlZWQvYmFuZHdpZHRoDQoNClRoZSBnZW5lcmFsIGlkaW9tIGlzIHRvIGdyYWIgdGhlIHRlc3QgY29uZmlndXJhdGlvbiBmaWxlLCBjb2xsZWN0IHRoZSBtYXN0ZXIgbGlzdCBvZiBzZXJ2ZXJzLCBmaWd1cmUgb3V0IHdoaWNoIHNlcnZlcnMgeW914oCZcmUgZ29pbmcgdG8gdGVzdCBhZ2FpbnN0IGFuZCBwZXJmb3JtIHVwbG9hZC9kb3dubG9hZCB0ZXN0cyArIGNvbGxlY3QgdGhlIHJlc3VsdGFudCBzdGF0aXN0aWNzOg0KDQpsaWJyYXJ5KHNwZWVkdGVzdCkNCmxpYnJhcnkoc3RyaW5naSkNCmxpYnJhcnkoaHJicnRoZW1lcykNCmxpYnJhcnkoZ2diZWVzd2FybSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpjb25maWcgPC0gc3BkX2NvbmZpZygpDQoNCnNlcnZlcnMgPC0gc3BkX3NlcnZlcnMoY29uZmlnPWNvbmZpZykNCmNsb3Nlc3Rfc2VydmVycyA8LSBzcGRfY2xvc2VzdF9zZXJ2ZXJzKHNlcnZlcnMsIGNvbmZpZz1jb25maWcpDQpvbmx5X3RoZV9iZXN0X3NldmVycyA8LSBzcGRfYmVzdF9zZXJ2ZXJzKGNsb3Nlc3Rfc2VydmVycywgY29uZmlnKQ0KDQpnbGltcHNlKHNwZF9kb3dubG9hZF90ZXN0KGNsb3Nlc3Rfc2VydmVyc1sxLF0sIGNvbmZpZz1jb25maWcpKQ0KIyMgT2JzZXJ2YXRpb25zOiAxDQojIyBWYXJpYWJsZXM6IDE1DQojIyAkIHVybCAgICAgICJodHRwOi8vc3BlZWQwLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbGF0ICAgICAgNDIuMzg3NQ0KIyMgJCBsbmcgICAgICAtNzEuMQ0KIyMgJCBuYW1lICAgICAiU29tZXJ2aWxsZSwgTUEiDQojIyAkIGNvdW50cnkgICJVbml0ZWQgU3RhdGVzIg0KIyMgJCBjYyAgICAgICAiVVMiDQojIyAkIHNwb25zb3IgICJBeGNlbHggVGVjaG5vbG9naWVzIExMQyINCiMjICQgaWQgICAgICAgIjU5NjAiDQojIyAkIGhvc3QgICAgICJzcGVlZDAueGNlbHgubmV0OjgwODAiDQojIyAkIHVybDIgICAgICJodHRwOi8vc3BlZWQxLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbWluICAgICAgMTQuNDA0MzkNCiMjICQgbWVhbiAgICAgNjAuMDY4MzQNCiMjICQgbWVkaWFuICAgNTUuMjg0NTcNCiMjICQgbWF4ICAgICAgMTI3Ljk0MzYNCiMjICQgc2QgICAgICAgMzQuMjA2OTUNCg0KZ2xpbXBzZShzcGRfdXBsb2FkX3Rlc3Qob25seV90aGVfYmVzdF9zZXZlcnNbMSxdLCBjb25maWc9Y29uZmlnKSkNCiMjIE9ic2VydmF0aW9uczogMQ0KIyMgVmFyaWFibGVzOiAxOA0KIyMgJCBwaW5nX3RpbWUgICAgICAgMC4wMjcxMjU2Nw0KIyMgJCB0b3RhbF90aW1lICAgICAgMC4wNTk5MTcNCiMjICQgcmV0cmlldmFsX3RpbWUgIDIuM2UtMDUNCiMjICQgdXJsICAgICAgICAgICAgICJodHRwOi8vc3BlZWQwLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbGF0ICAgICAgICAgICAgIDQyLjM4NzUNCiMjICQgbG5nICAgICAgICAgICAgIC03MS4xDQojIyAkIG5hbWUgICAgICAgICAgICAiU29tZXJ2aWxsZSwgTUEiDQojIyAkIGNvdW50cnkgICAgICAgICAiVW5pdGVkIFN0YXRlcyINCiMjICQgY2MgICAgICAgICAgICAgICJVUyINCiMjICQgc3BvbnNvciAgICAgICAgICJBeGNlbHggVGVjaG5vbG9naWVzIExMQyINCiMjICQgaWQgICAgICAgICAgICAgICI1OTYwIg0KIyMgJCBob3N0ICAgICAgICAgICAgInNwZWVkMC54Y2VseC5uZXQ6ODA4MCINCiMjICQgdXJsMiAgICAgICAgICAgICJodHRwOi8vc3BlZWQxLnhjZWx4Lm5ldC9zcGVlZHRlc3QvdXBsb2FkLnBocCINCiMjICQgbWluICAgICAgICAgICAgIDYuMjQwODU4DQojIyAkIG1lYW4gICAgICAgICAgICA5LjUyNzU5OQ0KIyMgJCBtZWRpYW4gICAgICAgICAgOS4zMDMxNDgNCiMjICQgbWF4ICAgICAgICAgICAgIDEyLjU2Njg2DQojIyAkIHNkICAgICAgICAgICAgICAyLjQ1MTc3OA0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoODY3NTMwOSkNCg0KYmluZF9yb3dzKA0KDQogIGNsb3Nlc3Rfc2VydmVyc1sxOjMsXSAlPiUNCiAgICBtdXRhdGUodHlwZT0iY2xvc2VzdCIpLA0KDQogIG9ubHlfdGhlX2Jlc3Rfc2V2ZXJzWzE6MyxdICU+JQ0KICAgIG11dGF0ZSh0eXBlPSJiZXN0IiksDQoNCiAgZmlsdGVyKHNlcnZlcnMsICEoaWQgJWluJSBjKGNsb3Nlc3Rfc2VydmVyc1sxOjMsXSRpZCwgb25seV90aGVfYmVzdF9zZXZlcnNbMTozLF0kaWQpKSkgJT4lDQogICAgc2FtcGxlX24oMykgJT4lDQogICAgbXV0YXRlKHR5cGU9InJhbmRvbSIpDQoNCikgJT4lDQogIGdyb3VwX2J5KHR5cGUpICU+JQ0KICB1bmdyb3VwKCkgLT4gdG9fY29tcGFyZQ0KDQpzZWxlY3QodG9fY29tcGFyZSwgc3BvbnNvciwgbmFtZSwgY291bnRyeSwgaG9zdCwgdHlwZSkNCmBgYA0KDQoNCmBgYHtyfQ0KbWFwX2RmKDE6bnJvdyh0b19jb21wYXJlKSwgfnsNCiAgc3BkX2Rvd25sb2FkX3Rlc3QodG9fY29tcGFyZVsueCxdLCBjb25maWc9Y29uZmlnLCBzdW1tYXJpc2U9RkFMU0UsIHRpbWVvdXQ9MzApDQp9KSAtPiBkbF9yZXN1bHRzX2Z1bGwNCg0KbXV0YXRlKGRsX3Jlc3VsdHNfZnVsbCwgdHlwZT1zdHJpX3RyYW5zX3RvdGl0bGUodHlwZSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHR5cGUsIGJ3LCBmaWxsPXR5cGUpKSArDQogIGdlb21fcXVhc2lyYW5kb20oYWVzKHNpemU9c2l6ZSwgY29sb3I9dHlwZSksIHdpZHRoPTAuMTUsIHNoYXBlPTIxLCBzdHJva2U9MC4yNSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCw1KSwgbGFiZWxzPWMoc3ByaW50ZigiJXMiLCBzZXEoMCwxNTAsNTApKSwgIjIwMCBNYi9zIiksIGxpbWl0cz1jKDAsMjAwKSkgKw0KICBzY2FsZV9zaXplKHJhbmdlPWMoMiw2KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoUmFuZG9tPSIjYjJiMmIyIiwgQmVzdD0iIzJiMmIyYiIsIENsb3Nlc3Q9IiMyYjJiMmIiKSkgKw0KICBzY2FsZV9maWxsX2lwc3VtKCkgKw0KICBsYWJzKHg9TlVMTCwgeT1OVUxMLCB0aXRsZT0iRG93bmxvYWQgYmFuZHdpZHRoIHRlc3QgYnkgc2VsZWN0ZWQgc2VydmVyIHR5cGUiLA0KICAgICAgIHN1YnRpdGxlPSJDaXJjbGUgc2l6ZSBzY2FsZWQgYnkgc2l6ZSBvZiBmaWxlIHVzZWQgaW4gdGhhdCBzcGVlZCB0ZXN0IikgKw0KICB0aGVtZV9pcHN1bV9yYyhncmlkPSJZIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQ0KYGBgDQoNCmBgYHtyfQ0KYmluZF9yb3dzKA0KICBjbG9zZXN0X3NlcnZlcnNbMTozLF0gJT4lIG11dGF0ZSh0eXBlPSJjbG9zZXN0IiksDQogIG9ubHlfdGhlX2Jlc3Rfc2V2ZXJzWzE6MyxdICU+JSBtdXRhdGUodHlwZT0iYmVzdCIpDQopICU+JQ0KICBkaXN0aW5jdCgua2VlcF9hbGw9VFJVRSkgLT4gdG9fY29tcGFyZQ0KDQpzZWxlY3QodG9fY29tcGFyZSwgc3BvbnNvciwgbmFtZSwgY291bnRyeSwgaG9zdCwgdHlwZSkNCmBgYA0KDQpgYGB7cn0NCm1hcF9kZigxOm5yb3codG9fY29tcGFyZSksIH57DQogIHNwZF91cGxvYWRfdGVzdCh0b19jb21wYXJlWy54LF0sIGNvbmZpZz1jb25maWcsIHN1bW1hcmlzZT1GQUxTRSwgdGltZW91dD0zMCkNCn0pIC0+IHVsX3Jlc3VsdHNfZnVsbA0KDQpnZ3Bsb3QodWxfcmVzdWx0c19mdWxsLCBhZXMoeD0iVXBsb2FkIFRlc3QiLCB5PWJ3KSkgKw0KICBnZW9tX3F1YXNpcmFuZG9tKGFlcyhzaXplPXNpemUsIGZpbGw9ImNvbCIpLCB3aWR0aD0wLjEsIHNoYXBlPTIxLCBzdHJva2U9MC4yNSwgY29sb3I9IiMyYjJiMmIiKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLDAuNSksIGJyZWFrcz1zZXEoMCwxNiw0KSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKHNwcmludGYoIiVzIiwgc2VxKDAsMTIsNCkpLCAiMTYgTWIvcyIpLCBsaW1pdHM9YygwLDE2KSkgKw0KICBzY2FsZV9zaXplKHJhbmdlPWMoMiw2KSkgKw0KICBzY2FsZV9maWxsX2lwc3VtKCkgKw0KICBsYWJzKHg9TlVMTCwgeT1OVUxMLCB0aXRsZT0iVXBsb2FkIGJhbmR3aWR0aCB0ZXN0IGJ5IHNlbGVjdGVkIHNlcnZlciB0eXBlIiwNCiAgICAgICBzdWJ0aXRsZT0iQ2lyY2xlIHNpemUgc2NhbGVkIGJ5IHNpemUgb2YgZmlsZSB1c2VkIGluIHRoYXQgc3BlZWQgdGVzdCIpICsNCiAgdGhlbWVfaXBzdW1fcmMoZ3JpZD0iWSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikNCmBgYA0KDQo=