Report timestamp: 2020-04-28 00:08:04
Reference Phabricator Ticket: T248308
Notebook: Optimizing WDQS Response Time From SPARQL Queries Analysis
Feedback should be send to goran.milovanovic_ext@wikimedia.de
.
Summary
WMDE together with php.cc is trying to find a way to optimize the Wikidata Query Service. This Report provides (a) and Exploratory Data Analysis (EDA) approach to WDQS response times (Part A) and (2) describes a Machine Learning (ML) approach (Part B) to analyze the SPARQL queries that were received by the endpoint in the first three weeks of April 2020, with the goal of developing a model able to predict whether a query would be critical for WDQS/Blazegraph or not. The motivation for the ML approach to the problem is the following one: by singling out the most important features that are present in the SPARQL queries under analysis we hope to be able to understand better what types of queries typically cause problems.
This, Part A, of the Report, provides the Exploratory Data Analytics(EDA) on the WDQS query response times. We analyze the query response times across a range of variables obtained directly from the event.wdqs_external_sparql_query
schema in the WMF Data Lake.
In the following sections we describe (1) the characteristics of the sample of SPARQL queries used in this study, (2) the overview of the number of queries run per (a) day of week, (b) hour of day, (c) WMF Datacenter/Host, (d) HTTP method of request, (e) server HTTP response code, and (f) the desired output format, (3) the mean and median WDQS query processing times across the mentioned (a) - (f) variables, and (4) the distributions of WDQS processing times across WMF Datacenter/Hosts and output format.
Summary
- The
eqiad
data center is receiving tons of queries in comparison to codfw
.
- The
XML
output format seems to take much more to process in comparison to JSON
and text/plain
(except for we really have only few observations of text/plain
in the sample).
- The distributions of the WDQS processing time across the crucial variables (WMF Datacenter/Host, Output format) are highly skewed towards short processing times - so we really need to focus on the outliers seriously.
0. Sample of SPARQL queries observed at the WDQS SPARQL endpoint
The SPARQL queries that hit the endpoint are registered in the event.wdqs_external_sparql_query
table in the WMF Data Lake (our Hadoop Big Data storage). A bucketing approach to sample data from this Hive table was taken, sampling approximately 1%
of queries that were observed in the period between 2020/04/01
and 2020/04/21
. The sample is approximately uniformly distributed across days and hours of the mentioned period of time. The sample size is slightly higher than 1M
SPARQL queries - including duplicates.
The following fields were derived from the event.wdqs_external_sparql_query
table for each collected SPARQL query:
ds_sparql
- the SPARQL query
ds_dt
- the query timestamp, later parsed into year
, month
, day
, hour
, and minute
ds_status_code
- the server http status response for this query
ds_query_time
- how many seconds did it took for the WDQS to process the query
ds_datacenter_host
- a combination of the (a) WMF data center and (b) a back-end host in the respective data center
ds_format
- content type (e.g. JSON, text/tsv, xml, etc.)
ds_method
- HTTP method (e.g. GET, POST)
ds_n_conc_queries
- for each SPARQL query, the number of SPARQL whose processing started in (1) the same minute, on (2) the same data center, and on (3) the same back-end host.
The following R script was used to obtain a sample of SPARQL queries from event.wdqs_external_sparql_query
.
#!/usr/bin/env Rscript
### ---------------------------------------------------------------------------
### --- WDQS_Event_S1_ETL.R v 0.0.1
### --- Script: WDQS_Event_S1_ETL.R, v. Beta 0.1
### --- Description: WDQS_Event_S1_ETL.R collects a sample of
### --- SPARQL queries from event.wdqs_external_sparql_query in the WMF Data Lake.
### --- Author: Goran S. Milovanovic, Data Scientist, WMDE
### --- Developed under the contract between Goran Milovanovic PR Data Kolektiv
### --- and WMDE.
### --- Contact: goran.milovanovic_ext@wikimedia.de
### ---------------------------------------------------------------------------
### --- RUN FROM: stat1005
### --- Date: 2020/04/22
### ---------------------------------------------------------------------------
### ---------------------------------------------------------------------------
### --- LICENSE:
### ---------------------------------------------------------------------------
### --- GPL v2
### --- This file is part of the Wikidata SPARQL Endpoint Analytics Project
### ---
### --- Wikidata SPARQL Endpoint Analytics Project 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 2 of the License, or
### --- (at your option) any later version.
### ---
### --- Wikidata SPARQL Endpoint Analytics Project 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 Wikidata SPARQL Endpoint Analytics Project.
### --- If not, see <http://www.gnu.org/licenses/>.
### ---------------------------------------------------------------------------
### ---------------------------------------------------------------------------
### --- Section 1. Collect raw data set event.wdqs_external_sparql_query
### ---------------------------------------------------------------------------
### --- setup
library(data.table)
library(tidyverse)
### --- directories
### --- Read WDCM paramereters
# - fPath: where the scripts is run from?
fPath <- as.character(commandArgs(trailingOnly = F)[4])
fPath <- gsub("--file=", "", fPath, fixed = T)
fPath <- unlist(strsplit(fPath, split = "/", fixed = T))
fPath <- paste(
paste(fPath[1:length(fPath) - 1], collapse = "/"),
"/",
sep = "")
fPath <- '/home/goransm/Analytics/Wikidata/WD_misc/WD_SPARQL_Endpoint_Analytics/'
dataDir <- paste0(fPath, '_event/_data/')
analyticsDir <- paste0(fPath, '_event/_analytics/')
### --- grab a sample of SPARQL queries event.wdqs_external_sparql_query
### --- w. HiveQL from Beeline
# - query filename
queryFile <- "wdqs_CollectData.hql"
# - Kerberos Init
kerberosPrefix <- 'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata '
# - dateRange
startDate <- '2020/04/01'
endDate <- '2020/04/21'
dateRange <- seq(as.Date(startDate), as.Date(endDate), by = "day")
# - query over dateRange
for (i in 1:length(dateRange)) {
print(paste0("Started: ", dateRange[i]))
# - year, month, date
year = substr(dateRange[i], 1, 4)
month = substr(dateRange[i], 6, 7)
if (substr(month, 1, 1) == '0') {month = substr(month, 2, 2)}
day = substr(dateRange[i], 9, 10)
if (substr(day, 1, 1) == '0') {day = substr(day, 2, 2)}
# - compose query
hiveQLquery <- paste0("SELECT meta.dt as dt,
regexp_replace(
regexp_replace(
regexp_replace(query, '\\n|\\r\\n|\\r|\\n\\r', ' __NEWLINE__ '),
'\\t', ' '),
'\\000', ' ') as sparql,
format, meta.domain as domain,
http.request_headers['X-BIGDATA-MAX-QUERY-MILLIS'] as max_query_millis, http.method as method,
http.status_code as status_code,
backend_host, datacenter, query_time
FROM event.wdqs_external_sparql_query TABLESAMPLE (BUCKET 1 OUT OF 100 ON rand()) w
WHERE isnotnull(http.status_code)
AND (meta.domain = 'query.wikidata.org')
AND year=", year,
" AND month=", month,
" AND day=", day, ';')
filename <- "wdqs_data_sample_TEST_"
filename <- paste0(filename, dateRange[i], ".tsv")
# - write hql
write(hiveQLquery, paste0(fPath, queryFile))
# - to Report
print("Fetching sparql_testData from wmf.webrequest now.")
# - Run query
query <- system(command = paste(kerberosPrefix,
'/usr/local/bin/beeline --incremental=true --silent -f "',
paste0(fPath, queryFile),
'" > ', dataDir, filename,
sep = ""),
wait = TRUE)
print(paste0("Completed: ", dateRange[i]))
print("--------------------------------------------")
}
# - to Report
print("DONE w. ETL from Hadoop: wmf.webrequest.")
1. Exploratory Data Analytics
All of the following analytics were derived from the datasets produced by the following R script.
#!/usr/bin/env Rscript
### ---------------------------------------------------------------------------
### --- WDQS_Event_S5_ReportingDatasets.R, v. 0.0.1
### --- Script: WDQS_Event_S5_ReportingDatasets.R, v. 0.0.1
### --- Description: WDQS_Event_S5_ReportingDatasets.R is used
### --- only to produce the reporting datasets for the
### --- Wikidata SPARQL Endpoint Analytics Project.
### --- All data are obtained from the event.wdqs_external_sparql_query
### --- table in the WMF Data Lake.
### --- Author: Goran S. Milovanovic, Data Scientist, WMDE
### --- Developed under the contract between Goran Milovanovic PR Data Kolektiv
### --- and WMDE.
### --- Contact: goran.milovanovic_ext@wikimedia.de
### ---------------------------------------------------------------------------
### --- RUN FROM: stat1005
### --- Date: 2020/04/27
### ---------------------------------------------------------------------------
### ---------------------------------------------------------------------------
### --- LICENSE:
### ---------------------------------------------------------------------------
### --- GPL v2
### --- This file is part of the Wikidata SPARQL Endpoint Analytics Project
### ---
### --- Wikidata SPARQL Endpoint Analytics Project 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 2 of the License, or
### --- (at your option) any later version.
### ---
### --- Wikidata SPARQL Endpoint Analytics Project 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 Wikidata SPARQL Endpoint Analytics Project.
### --- If not, see <http://www.gnu.org/licenses/>.
### ---------------------------------------------------------------------------
### --- setup
library(data.table)
library(stringr)
library(dplyr)
library(tidyr)
library(httr)
library(pROC)
library(xgboost)
library(caret)
library(fastDummies)
### --- directories
### --- Read WDCM paramereters
# - fPath: where the scripts is run from?
fPath <- as.character(commandArgs(trailingOnly = FALSE)[4])
fPath <- gsub("--file=", "", fPath, fixed = T)
fPath <- unlist(strsplit(fPath, split = "/", fixed = T))
fPath <- paste(
paste(fPath[1:length(fPath) - 1], collapse = "/"),
"/",
sep = "")
fPath <- '/home/goransm/Analytics/Wikidata/WD_misc/WD_SPARQL_Endpoint_Analytics/'
# - NOTE: Hetzner local dir
# - fPath <- '/home/goran/WMDE/WD_SPARQL_Endpoint_Analytics/'
dataDir <- paste0(fPath, '_event/_data/')
analyticsDir <- paste0(fPath, '_event/_analytics/')
reportingDir <- paste0(fPath, '_event/_reporting/')
### ---------------------------------------------------------------------------
### --- Section 1. Produce reporting dataset
### ---------------------------------------------------------------------------
### --- feature frequencies: vocabulary
filename <- 'queries_vocabulary.csv'
vocabulary <- fread(paste0(analyticsDir, filename))
vocabulary$V1 <- NULL
### --- dataset
filename <- 'wdqs_data_sample_2020-04-22.csv'
dataSet <- fread(paste0(analyticsDir, filename))
dataSet$V1 <- NULL
# - derive: n_conc_queries per datacenter x back_endhost
dataSet$datacenter_host <- paste(dataSet$datacenter,
dataSet$backend_host,
sep = "_")
dataSet$datacenter <- NULL
dataSet$backend_host <- NULL
# - the number of queries started in the same minute
# - as the target query
# - per datacenter x back_endhost
dataSet$datacenter_host_ymdhm <- paste(dataSet$datacenter_host,
dataSet$ymdhm,
sep = "_")
dataSet$ymdhm <- NULL
n_conc_queries <- dataSet %>%
dplyr::select(datacenter_host_ymdhm) %>%
dplyr::group_by(datacenter_host_ymdhm) %>%
dplyr::summarise(n_conc_queries = n())
dataSet <- dataSet %>%
dplyr::left_join(n_conc_queries, by = "datacenter_host_ymdhm")
dataSet$datacenter_host_ymdhm <- NULL
rm(n_conc_queries)
# - derive: mild and extreme outliers on query_time
# - query_time >= q3 + 3*iq
# - q3 = 75 percentile, q1 = 25 percentile
q3 <- quantile(dataSet$query_time, .75)
q1 <- quantile(dataSet$query_time, .25)
# - iq = q3 - q1; Interquartile range
iq <- unname(q3 - q1)
upper_outer_fence <- q3 + 3*iq
upper_inner_fence <- q3 + 1.5*iq
dataSet$query_time_extreme <-
ifelse(dataSet$query_time >= upper_outer_fence, 1, 0)
dataSet$query_time_mild <-
ifelse(dataSet$query_time >= upper_inner_fence, 1, 0)
# - derive multiclass DV: 0 - mild, mild - extreme, extreme query_time
dataSet$query_time_class <- 0
dataSet$query_time_class[dataSet$query_time_extreme == 0 & dataSet$query_time_mild == 1] <- 1
dataSet$query_time_class[dataSet$query_time_extreme == 1] <- 2
# - derive: below and above median query_time
dataSet$query_time_median <- ifelse(dataSet$query_time >= median(dataSet$query_time), 1, 0)
### --- vocabulary: top_f most frequently used features
top_f <- 300
ob_features <- c('nchar', '__vars__', '__vars_usage__', '__literal__',
'__vars_label__', '__vars_label_usage__', '__iri_ref__')
vocabulary <- vocabulary$feature[1:top_f]
vocabulary <- unique(c(vocabulary, ob_features))
### --- join vocabulary to dataSet
queries_processed <- fread(paste0(analyticsDir, 'queries_processed.csv'))
queries_processed$V1 <- NULL
dim(queries_processed)
queries_processed <- queries_processed %>%
dplyr::filter(feature %in% vocabulary)
rm(vocabulary)
dim(queries_processed)
featureFrame <- data.table::dcast(as.data.table(queries_processed),
uniqueSparqlId ~ feature,
value.var = "freq")
rm(queries_processed); gc()
# - mark features in dataSet: ds
colnames(dataSet) <- paste0("ds_", colnames(dataSet))
# - join:
dataSet <- dataSet %>%
dplyr::left_join(featureFrame,
by = c('ds_uniqueSparqlId' = 'uniqueSparqlId'))
rm(featureFrame); gc()
colnames(dataSet)
# - store reporting data.frame: reportingFrame.csv
filename <- paste0('reportingFrame', '_', top_f, '.csv')
write.csv(dataSet,
paste0(reportingDir, filename))
# - clear
rm(list = setdiff(ls(), c('reportingDir', 'analyticsDir', 'dataDir', 'fPath')))
gc()
### ---------------------------------------------------------------------------
### --- Section 2. Exploratory Data Analysis
### ---------------------------------------------------------------------------
### --- load reporting data.frame
top_f <- 300
filename <- paste0('reportingFrame', '_', top_f, '.csv')
reportFrame <- fread(paste0(reportingDir, filename))
reportFrame$V1 <- NULL
### --- reportFrame$recode ds_day to day of week
sampleDays <- ifelse(nchar(reportFrame$ds_day) == 1,
paste0('0', reportFrame$ds_day),
reportFrame$ds_day)
reportFrame$ds_day <- paste0("2020-04-", sampleDays)
reportFrame$ds_day <- weekdays(as.Date(reportFrame$ds_day))
rm(sampleDays)
### --- Sample characteristics
# - external (non-SPARQL parsing based) fatures in the dataset
ds_features <-
colnames(reportFrame)[which(grepl("^ds_", colnames(reportFrame)))]
# - days
sampleDays <- as.data.frame(table(reportFrame$ds_day))
colnames(sampleDays) <- c('Day', 'Num.Queries')
sampleDays$Percent <- round(
sampleDays$Num.Queries/sum(sampleDays$Num.Queries)*100, 2)
sampleDays <-dplyr::arrange(sampleDays, desc(Percent))
write.csv(sampleDays, paste0(reportingDir, 'sampleChar_Days.csv'))
# - hours
sampleHours <- as.data.frame(table(reportFrame$ds_hour))
colnames(sampleHours) <- c('Hour', 'Num.Queries')
sampleHours$Percent <- round(
sampleHours$Num.Queries/sum(sampleHours$Num.Queries)*100, 2)
sampleHours <-dplyr::arrange(sampleHours, desc(Percent))
write.csv(sampleHours, paste0(reportingDir, 'sampleChar_Hours.csv'))
# - HTTP method
sampleMethod <- as.data.frame(table(reportFrame$ds_method))
colnames(sampleMethod) <- c('HTTP.method', 'Num.Queries')
sampleMethod$Percent <- round(
sampleMethod$Num.Queries/sum(sampleMethod$Num.Queries)*100, 2)
sampleMethod <-dplyr::arrange(sampleMethod, desc(Percent))
write.csv(sampleMethod, paste0(reportingDir, 'sampleChar_HTTPmethod.csv'))
# - datacenter_host
sample_datacenter_host <- as.data.frame(table(reportFrame$ds_datacenter_host))
colnames(sample_datacenter_host) <- c('Datacenter.Host', 'Num.Queries')
sample_datacenter_host$Percent <- round(
sample_datacenter_host$Num.Queries/sum(sample_datacenter_host$Num.Queries)*100, 2)
sample_datacenter_host <-dplyr::arrange(sample_datacenter_host, desc(Percent))
write.csv(sample_datacenter_host, paste0(reportingDir, 'sampleChar_DatacenterHost.csv'))
# - Status Code
sample_StatusCode <- as.data.frame(table(reportFrame$ds_status_code))
colnames(sample_StatusCode) <- c('Status', 'Num.Queries')
sample_StatusCode$Percent <- round(
sample_StatusCode$Num.Queries/sum(sample_StatusCode$Num.Queries)*100, 2)
sample_StatusCode <-dplyr::arrange(sample_StatusCode, desc(Percent))
write.csv(sample_StatusCode, paste0(reportingDir, 'sampleChar_StatusCode.csv'))
# - Format
sample_Format <- as.data.frame(table(reportFrame$ds_format))
colnames(sample_Format) <- c('Format', 'Num.Queries')
sample_Format$Percent <- round(
sample_Format$Num.Queries/sum(sample_Format$Num.Queries)*100, 2)
sample_Format <-dplyr::arrange(sample_Format, desc(Percent))
write.csv(sample_Format, paste0(reportingDir, 'sample_Format.csv'))
# - Distribution of the number of queries
# - concurrently started in the same minute
reportFrame$ds_day_hour_minute <- paste(reportFrame$ds_day,
reportFrame$ds_hour,
reportFrame$ds_minute,
sep = "-")
sample_datacenter_host_qrqueries <- reportFrame %>%
dplyr::select(ds_datacenter_host, ds_day_hour_minute) %>%
dplyr::group_by(ds_datacenter_host, ds_day_hour_minute) %>%
dplyr::summarise(query_count = n()) %>%
dplyr::select(ds_datacenter_host, query_count) %>%
dplyr::group_by(ds_datacenter_host) %>%
dplyr::summarise(mean_conc_queries = mean(query_count),
median_conc_queries = median(query_count),
stdev_conc_queries = sd(query_count)) %>%
dplyr::arrange(desc(mean_conc_queries))
write.csv(sample_datacenter_host_qrqueries,
paste0(reportingDir, 'sample_datacenter_host_qrqueries.csv'))
reportFrame$ds_day_hour_minute <- NULL
### --- Produce repeated queries dataset
repeatedQueries <- reportFrame %>%
dplyr::select(ds_uniqueSparqlId,
ds_sparql,
ds_query_time)
colnames(repeatedQueries)
repeatedQueries <- repeatedQueries %>%
dplyr::group_by(ds_uniqueSparqlId, ds_sparql) %>%
dplyr::summarise(num = n(),
mean_query_time = mean(ds_query_time),
median_query_time = median(ds_query_time),
stdev_query_time = sd(ds_query_time))
repeatedQueries$Percent <- round(
repeatedQueries$num/sum(repeatedQueries$num)*100, 6)
repeatedQueries <- dplyr::arrange(repeatedQueries, desc(num))
write.csv(repeatedQueries,
paste0(reportingDir, 'repeatedQueries.csv'))
rm(repeatedQueries); gc()
### --- Cross-Tabulation: ds_* features x query_time
# - ds_format
crossTabs_format <- reportFrame %>%
dplyr::select(ds_format, ds_query_time) %>%
dplyr::group_by(ds_format) %>%
dplyr::summarise(mean_query_time = mean(ds_query_time),
median_query_time = median(ds_query_time),
stdev_query_time = sd(ds_query_time)) %>%
dplyr::arrange(desc(mean_query_time))
write.csv(crossTabs_format,
paste0(reportingDir, 'crossTabs_format.csv'))
# - ds_day
crossTabs_day <- reportFrame %>%
dplyr::select(ds_day, ds_query_time) %>%
dplyr::group_by(ds_day) %>%
dplyr::summarise(mean_query_time = mean(ds_query_time),
median_query_time = median(ds_query_time),
stdev_query_time = sd(ds_query_time)) %>%
dplyr::arrange(desc(mean_query_time))
write.csv(crossTabs_day,
paste0(reportingDir, 'crossTabs_day.csv'))
# - ds_method
crossTabs_method <- reportFrame %>%
dplyr::select(ds_method, ds_query_time) %>%
dplyr::group_by(ds_method) %>%
dplyr::summarise(mean_query_time = mean(ds_query_time),
median_query_time = median(ds_query_time),
stdev_query_time = sd(ds_query_time)) %>%
dplyr::arrange(desc(mean_query_time))
write.csv(crossTabs_method,
paste0(reportingDir, 'crossTabs_method.csv'))
# - ds_datacenter_host
crossTabs_datacenter_host <- reportFrame %>%
dplyr::select(ds_datacenter_host, ds_query_time) %>%
dplyr::group_by(ds_datacenter_host) %>%
dplyr::summarise(mean_query_time = mean(ds_query_time),
median_query_time = median(ds_query_time),
stdev_query_time = sd(ds_query_time)) %>%
dplyr::arrange(desc(mean_query_time))
write.csv(crossTabs_datacenter_host,
paste0(reportingDir, 'crossTabs_datacenter_host.csv'))
# - ds_status_code
crossTabs_status_code <- reportFrame %>%
dplyr::select(ds_status_code, ds_query_time) %>%
dplyr::group_by(ds_status_code) %>%
dplyr::summarise(mean_query_time = mean(ds_query_time),
median_query_time = median(ds_query_time),
stdev_query_time = sd(ds_query_time)) %>%
dplyr::arrange(desc(mean_query_time))
write.csv(crossTabs_status_code,
paste0(reportingDir, 'crossTabs_status_code.csv'))
1.1 Sample Description
library(ggplot2)
library(ggrepel)
library(scales)
First we provide descriptive statistics for the most important variables in the data set.
1.1.1 The distribution of queries per day of week
pFrame <- read.csv('sampleChar_Days.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$Day <- factor(pFrame$Day,
levels = c('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))
ggplot(data = pFrame,
aes(x = Day,
y = Num.Queries,
label = paste0(Percent, "%"))) +
geom_bar(width = .15, color = 'darkred', fill = 'red', stat = 'identity') +
geom_text_repel(size = 3) +
theme_bw()
1.1.2 The distribution of queries per hour of day
pFrame <- read.csv('sampleChar_Hours.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$Hour <- factor(pFrame$Hour,
levels = as.character(0:23))
ggplot(data = pFrame,
aes(x = Hour,
y = Num.Queries,
label = paste0(Percent, "%"))) +
geom_bar(width = .15, color = 'darkred', fill = 'red', stat = 'identity') +
geom_text_repel(size = 3) +
theme_bw()
1.1.3A The distribution of queries per WMF Datacenter/Host
pFrame <- read.csv('sampleChar_DatacenterHost.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$Datacenter.Host <- factor(pFrame$Datacenter.Host,
levels = unique(pFrame$Datacenter.Host))
ggplot(data = pFrame,
aes(x = Datacenter.Host,
y = Num.Queries,
label = paste0(Percent, "%"))) +
geom_bar(width = .15, color = 'darkgreen', fill = 'green', stat = 'identity') +
scale_y_continuous(labels = comma) +
geom_text_repel(size = 3) +
theme_bw() +
theme(axis.text.x = element_text(angle = 90))
1.1.4 The distribution of queries per HTTP method of request
pFrame <- read.csv('sampleChar_HTTPmethod.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$HTTP.method <- factor(pFrame$HTTP.method,
levels = unique(pFrame$HTTP.method))
ggplot(data = pFrame,
aes(x = HTTP.method,
y = Num.Queries,
label = paste0(Percent, "%"))) +
geom_bar(width = .15, color = 'darkblue', fill = 'blue', stat = 'identity') +
geom_text_repel(size = 3) +
theme_bw()
1.1.5 The distribution of queries per server response code
pFrame <- read.csv('sampleChar_StatusCode.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$Status<- as.character(pFrame$Status)
ggplot(data = pFrame,
aes(x = Status,
y = Num.Queries,
label = paste0(Percent, "%"))) +
scale_y_continuous(labels = comma) +
geom_bar(width = .15, color = 'darkorange', fill = 'orange', stat = 'identity') +
geom_text_repel(size = 3) +
theme_bw()
1.2 WDQS processing times
1.2.1 Day of week vs. WDQS response time
pFrame <- read.csv('crossTabs_day.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$ds_day <- factor(pFrame$ds_day,
levels = c('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))
ggplot(data = pFrame,
aes(x = ds_day,
y = mean_query_time,
label = paste0("Med: ", median_query_time))) +
geom_bar(width = .15, color = 'darkred', fill = 'red', stat = 'identity') +
scale_y_continuous(labels = comma) +
geom_text_repel(size = 3) +
xlab('Day') + ylab('Mean Query Time (secs)') +
theme_bw()
1.2.2 Hour of day vs. WDQS response time
pFrame <- read.csv('crossTabs_hour.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$ds_hour <- factor(pFrame$ds_hour,
levels = as.character(0:23))
ggplot(data = pFrame,
aes(x = ds_hour,
y = mean_query_time,
label = paste0("Med: ", median_query_time))) +
geom_bar(width = .15, color = 'darkred', fill = 'red', stat = 'identity') +
scale_y_continuous(labels = comma) +
geom_text_repel(size = 3) +
xlab('Hour') + ylab('Mean Query Time (secs)') +
theme_bw()
1.2.3 WMF Datacenter/Host vs. WDQS response time
pFrame <- read.csv('crossTabs_datacenter_host.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$ds_datacenter_host <- factor(pFrame$ds_datacenter_host,
levels = unique(pFrame$ds_datacenter_host))
ggplot(data = pFrame,
aes(x = ds_datacenter_host,
y = mean_query_time,
label = paste0("Med: ", median_query_time))) +
geom_bar(width = .15, color = 'darkgreen', fill = 'green', stat = 'identity') +
scale_y_continuous(labels = comma) +
geom_text_repel(size = 3) +
xlab('WMF/Datacenter/Host') + ylab('Mean Query Time (secs)') +
theme_bw() +
theme(axis.text.x = element_text(angle = 90))
1.2.4 HTTP method of request vs. WDQS response time
pFrame <- read.csv('crossTabs_method.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$ds_method <- factor(pFrame$ds_method,
levels = unique(pFrame$ds_method))
ggplot(data = pFrame,
aes(x = ds_method,
y = mean_query_time,
label = paste0("Med: ", median_query_time))) +
geom_bar(width = .15, color = 'darkblue', fill = 'blue', stat = 'identity') +
scale_y_continuous(labels = comma) +
geom_text_repel(size = 3) +
xlab('HTTP method of request') + ylab('Mean Query Time (secs)') +
theme_bw() +
theme(axis.text.x = element_text(angle = 90))
1.2.5 Server response code vs. WDQS response time
pFrame <- read.csv('crossTabs_status_code.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
pFrame$ds_status_code <- factor(pFrame$ds_status_code,
levels = unique(pFrame$ds_status_code))
ggplot(data = pFrame,
aes(x = ds_status_code,
y = mean_query_time,
label = paste0("Med: ", median_query_time))) +
geom_bar(width = .15, color = 'darkorange', fill = 'orange', stat = 'identity') +
scale_y_continuous(labels = comma) +
geom_text_repel(size = 3) +
xlab('Status Code') + ylab('Mean Query Time (secs)') +
theme_bw() +
theme(axis.text.x = element_text(angle = 90))
1.3 The distribution of WDQS processing times
1.3.1 The distribution of WDQS processing times across WMF Datacenter/Hosts
NOTE. Please mind that we use a logarithmic scale for the WDQS query processing times. The distributions are highly skewed on their natural scales.
pFrame <- read.csv('qtDistribution_DatacenterHost.csv',
header = T,
row.names = 1,
check.names = F,
stringsAsFactors = F)
ggplot(data = pFrame,
aes(x = log(ds_query_time))) +
geom_density(size = .25, color = "darkgreen", fill="green", alpha = .2) +
facet_wrap(~ds_datacenter_host) +
xlab('log(WDQS processing time)') + ylab('Density') +
ylim(c(0, 1)) +
theme_bw()+
theme(axis.text.x = element_text(angle = 90))
LS0tCnRpdGxlOiBXRFFTIEVuZHBvaW50IEFuYWx5dGljcyAtIE9wdGltaXppbmcgU2VydmVyIFJlc3BvbnNlIFRpbWUgLSBQYXJ0IEEgKEVEQSkKYXV0aG9yOgotIG5hbWU6IEdvcmFuIFMuIE1pbG92YW5vdmnEhwogIGFmZmlsaWF0aW9uOiBXaWtpbWVkaWEgRGV1dHNjaGxhbmQsIERhdGEgU2NpZW50aXN0LCBEYXRhS29sZWt0aXYsIE93bmVyCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKLS0tCioqKgoKKipSZXBvcnQgdGltZXN0YW1wOioqIGByIHtTeXMudGltZSgpfWAKCioqKgoKKipSZWZlcmVuY2UgUGhhYnJpY2F0b3IgVGlja2V0OioqIFtUMjQ4MzA4XShodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI0ODMwOCkKCioqKgojIyMgTm90ZWJvb2s6IE9wdGltaXppbmcgV0RRUyBSZXNwb25zZSBUaW1lIEZyb20gU1BBUlFMIFF1ZXJpZXMgQW5hbHlzaXMKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgojIyAqKlN1bW1hcnkqKiAKCltXTURFXShodHRwczovL3d3dy53aWtpbWVkaWEuZGUvKSB0b2dldGhlciB3aXRoIFtwaHAuY2NdKGh0dHBzOi8vdGhlcGhwLmNjL3dlbGNvbWUpIGlzIHRyeWluZyB0byBmaW5kIGEgd2F5IHRvIG9wdGltaXplIHRoZSBbV2lraWRhdGEgUXVlcnkgU2VydmljZV0oaHR0cHM6Ly93d3cubWVkaWF3aWtpLm9yZy93aWtpL1dpa2lkYXRhX1F1ZXJ5X1NlcnZpY2UpLiBUaGlzIFJlcG9ydCBwcm92aWRlcyAoYSkgYW5kIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgYXBwcm9hY2ggdG8gV0RRUyByZXNwb25zZSB0aW1lcyAoUGFydCBBKSBhbmQgKDIpIGRlc2NyaWJlcyBhIE1hY2hpbmUgTGVhcm5pbmcgKE1MKSBhcHByb2FjaCAoUGFydCBCKSB0byBhbmFseXplIHRoZSBTUEFSUUwgcXVlcmllcyB0aGF0IHdlcmUgcmVjZWl2ZWQgYnkgdGhlIGVuZHBvaW50IGluIHRoZSBmaXJzdCB0aHJlZSB3ZWVrcyBvZiBBcHJpbCAyMDIwLCB3aXRoIHRoZSBnb2FsIG9mIGRldmVsb3BpbmcgYSBtb2RlbCBhYmxlIHRvIHByZWRpY3Qgd2hldGhlciBhIHF1ZXJ5IHdvdWxkIGJlICoqY3JpdGljYWwqKiBmb3IgV0RRUy9CbGF6ZWdyYXBoIG9yIG5vdC4gVGhlIG1vdGl2YXRpb24gZm9yIHRoZSBNTCBhcHByb2FjaCB0byB0aGUgcHJvYmxlbSBpcyB0aGUgZm9sbG93aW5nIG9uZTogYnkgc2luZ2xpbmcgb3V0IHRoZSBtb3N0IGltcG9ydGFudCBmZWF0dXJlcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBTUEFSUUwgcXVlcmllcyB1bmRlciBhbmFseXNpcyB3ZSBob3BlIHRvIGJlIGFibGUgdG8gdW5kZXJzdGFuZCBiZXR0ZXIgd2hhdCB0eXBlcyBvZiBxdWVyaWVzIHR5cGljYWxseSBjYXVzZSBwcm9ibGVtcy4KCioqVGhpcywgUGFydCBBLCBvZiB0aGUgUmVwb3J0LCBwcm92aWRlcyB0aGUgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXRpY3MoRURBKSBvbiB0aGUgV0RRUyBxdWVyeSByZXNwb25zZSB0aW1lcy4gV2UgYW5hbHl6ZSB0aGUgcXVlcnkgcmVzcG9uc2UgdGltZXMgYWNyb3NzIGEgcmFuZ2Ugb2YgdmFyaWFibGVzIG9idGFpbmVkIGRpcmVjdGx5IGZyb20gdGhlIFtgZXZlbnQud2Rxc19leHRlcm5hbF9zcGFycWxfcXVlcnlgXShodHRwczovL2dlcnJpdC53aWtpbWVkaWEub3JnL3IvcGx1Z2lucy9naXRpbGVzL21lZGlhd2lraS9ldmVudC1zY2hlbWFzLysvbWFzdGVyL2pzb25zY2hlbWEvc3BhcnFsL3F1ZXJ5LzEuMC4wLnlhbWwpIHNjaGVtYSBpbiB0aGUgW1dNRiBEYXRhIExha2VdKGh0dHBzOi8vd2lraXRlY2gud2lraW1lZGlhLm9yZy93aWtpL0FuYWx5dGljcy9EYXRhX0xha2UpLioqCgpJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zIHdlIGRlc2NyaWJlICoqKDEpKiogdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgc2FtcGxlIG9mIFNQQVJRTCBxdWVyaWVzIHVzZWQgaW4gdGhpcyBzdHVkeSwgKiooMikqKiB0aGUgb3ZlcnZpZXcgb2YgdGhlIG51bWJlciBvZiBxdWVyaWVzIHJ1biBwZXIgKGEpIGRheSBvZiB3ZWVrLCAoYikgaG91ciBvZiBkYXksIChjKSBXTUYgRGF0YWNlbnRlci9Ib3N0LCAoZCkgSFRUUCBtZXRob2Qgb2YgcmVxdWVzdCwgKGUpIHNlcnZlciBIVFRQIHJlc3BvbnNlIGNvZGUsIGFuZCAoZikgdGhlIGRlc2lyZWQgb3V0cHV0IGZvcm1hdCwgKiooMykqKiB0aGUgbWVhbiBhbmQgbWVkaWFuIFdEUVMgcXVlcnkgcHJvY2Vzc2luZyB0aW1lcyBhY3Jvc3MgdGhlIG1lbnRpb25lZCAoYSkgLSAoZikgdmFyaWFibGVzLCBhbmQgKiooNCkqKiB0aGUgZGlzdHJpYnV0aW9ucyBvZiBXRFFTIHByb2Nlc3NpbmcgdGltZXMgYWNyb3NzIFdNRiBEYXRhY2VudGVyL0hvc3RzIGFuZCBvdXRwdXQgZm9ybWF0LgoKKipTdW1tYXJ5KiogCgoqIFRoZSBgZXFpYWRgIGRhdGEgY2VudGVyIGlzIHJlY2VpdmluZyB0b25zIG9mIHF1ZXJpZXMgaW4gY29tcGFyaXNvbiB0byBgY29kZndgLgoqIFRoZSBgWE1MYCBvdXRwdXQgZm9ybWF0IHNlZW1zIHRvIHRha2UgbXVjaCBtb3JlIHRvIHByb2Nlc3MgaW4gY29tcGFyaXNvbiB0byBgSlNPTmAgYW5kIGB0ZXh0L3BsYWluYCAoZXhjZXB0IGZvciB3ZSByZWFsbHkgaGF2ZSBvbmx5IGZldyBvYnNlcnZhdGlvbnMgb2YgYHRleHQvcGxhaW5gIGluIHRoZSBzYW1wbGUpLgoqIFRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBXRFFTIHByb2Nlc3NpbmcgdGltZSBhY3Jvc3MgdGhlIGNydWNpYWwgdmFyaWFibGVzIChXTUYgRGF0YWNlbnRlci9Ib3N0LCBPdXRwdXQgZm9ybWF0KSBhcmUgaGlnaGx5IHNrZXdlZCB0b3dhcmRzIHNob3J0IHByb2Nlc3NpbmcgdGltZXMgLSBzbyB3ZSByZWFsbHkgbmVlZCB0byBmb2N1cyBvbiB0aGUgb3V0bGllcnMgc2VyaW91c2x5LgoKCioqKgoKIyMgMC4gU2FtcGxlIG9mIFNQQVJRTCBxdWVyaWVzIG9ic2VydmVkIGF0IHRoZSBXRFFTIFNQQVJRTCBlbmRwb2ludAoKVGhlIFNQQVJRTCBxdWVyaWVzIHRoYXQgaGl0IHRoZSBlbmRwb2ludCBhcmUgcmVnaXN0ZXJlZCBpbiB0aGUgYGV2ZW50LndkcXNfZXh0ZXJuYWxfc3BhcnFsX3F1ZXJ5YCB0YWJsZSBpbiB0aGUgV01GIERhdGEgTGFrZSAob3VyIEhhZG9vcCBCaWcgRGF0YSBzdG9yYWdlKS4gQSBbYnVja2V0aW5nIGFwcHJvYWNoXShodHRwczovL2N3aWtpLmFwYWNoZS5vcmcvY29uZmx1ZW5jZS9kaXNwbGF5L0hpdmUvTGFuZ3VhZ2VNYW51YWwrU2FtcGxpbmcpIHRvIHNhbXBsZSBkYXRhIGZyb20gdGhpcyBIaXZlIHRhYmxlIHdhcyB0YWtlbiwgc2FtcGxpbmcgYXBwcm94aW1hdGVseSBgMSVgIG9mIHF1ZXJpZXMgdGhhdCB3ZXJlIG9ic2VydmVkIGluIHRoZSBwZXJpb2QgYmV0d2VlbiBgMjAyMC8wNC8wMWAgYW5kIGAyMDIwLzA0LzIxYC4gVGhlIHNhbXBsZSBpcyBhcHByb3hpbWF0ZWx5IHVuaWZvcm1seSBkaXN0cmlidXRlZCBhY3Jvc3MgZGF5cyBhbmQgaG91cnMgb2YgdGhlIG1lbnRpb25lZCBwZXJpb2Qgb2YgdGltZS4gVGhlIHNhbXBsZSBzaXplIGlzIHNsaWdodGx5IGhpZ2hlciB0aGFuIGAxTWAgU1BBUlFMIHF1ZXJpZXMgLSBpbmNsdWRpbmcgZHVwbGljYXRlcy4KClRoZSBmb2xsb3dpbmcgZmllbGRzIHdlcmUgZGVyaXZlZCBmcm9tIHRoZSBgZXZlbnQud2Rxc19leHRlcm5hbF9zcGFycWxfcXVlcnlgIHRhYmxlIGZvciBlYWNoIGNvbGxlY3RlZCBTUEFSUUwgcXVlcnk6CgotIGBkc19zcGFycWxgIC0gdGhlIFNQQVJRTCBxdWVyeQotIGBkc19kdGAgLSB0aGUgcXVlcnkgdGltZXN0YW1wLCBsYXRlciBwYXJzZWQgaW50byBgeWVhcmAsIGBtb250aGAsIGBkYXlgLCBgaG91cmAsIGFuZCBgbWludXRlYAotIGBkc19zdGF0dXNfY29kZWAgLSB0aGUgc2VydmVyIGh0dHAgc3RhdHVzIHJlc3BvbnNlIGZvciB0aGlzIHF1ZXJ5Ci0gYGRzX3F1ZXJ5X3RpbWVgIC0gaG93IG1hbnkgc2Vjb25kcyBkaWQgaXQgdG9vayBmb3IgdGhlIFdEUVMgdG8gcHJvY2VzcyB0aGUgcXVlcnkKLSBgZHNfZGF0YWNlbnRlcl9ob3N0YCAtIGEgY29tYmluYXRpb24gb2YgdGhlIChhKSBXTUYgZGF0YSBjZW50ZXIgYW5kIChiKSBhIGJhY2stZW5kIGhvc3QgaW4gdGhlIHJlc3BlY3RpdmUgZGF0YSBjZW50ZXIKLSBgZHNfZm9ybWF0YCAtIGNvbnRlbnQgdHlwZSAoZS5nLiBKU09OLCB0ZXh0L3RzdiwgeG1sLCBldGMuKQotIGBkc19tZXRob2RgIC0gSFRUUCBtZXRob2QgKGUuZy4gR0VULCBQT1NUKQotIGBkc19uX2NvbmNfcXVlcmllc2AgLSBmb3IgZWFjaCBTUEFSUUwgcXVlcnksIHRoZSBudW1iZXIgb2YgU1BBUlFMIHdob3NlIHByb2Nlc3Npbmcgc3RhcnRlZCBpbiAoMSkgdGhlIHNhbWUgbWludXRlLCBvbiAoMikgdGhlIHNhbWUgZGF0YSBjZW50ZXIsIGFuZCBvbiAoMykgdGhlIHNhbWUgYmFjay1lbmQgaG9zdC4KClRoZSBmb2xsb3dpbmcgUiBzY3JpcHQgd2FzIHVzZWQgdG8gb2J0YWluIGEgc2FtcGxlIG9mIFNQQVJRTCBxdWVyaWVzIGZyb20gYGV2ZW50LndkcXNfZXh0ZXJuYWxfc3BhcnFsX3F1ZXJ5YC4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gRiwgbWVzc2FnZSA9IEZ9CiMhL3Vzci9iaW4vZW52IFJzY3JpcHQKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBXRFFTX0V2ZW50X1MxX0VUTC5SIHYgMC4wLjEKIyMjIC0tLSBTY3JpcHQ6IFdEUVNfRXZlbnRfUzFfRVRMLlIsIHYuIEJldGEgMC4xCiMjIyAtLS0gRGVzY3JpcHRpb246IFdEUVNfRXZlbnRfUzFfRVRMLlIgY29sbGVjdHMgYSBzYW1wbGUgb2YgCiMjIyAtLS0gU1BBUlFMIHF1ZXJpZXMgZnJvbSBldmVudC53ZHFzX2V4dGVybmFsX3NwYXJxbF9xdWVyeSBpbiB0aGUgV01GIERhdGEgTGFrZS4KIyMjIC0tLSBBdXRob3I6IEdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERQojIyMgLS0tIERldmVsb3BlZCB1bmRlciB0aGUgY29udHJhY3QgYmV0d2VlbiBHb3JhbiBNaWxvdmFub3ZpYyBQUiBEYXRhIEtvbGVrdGl2CiMjIyAtLS0gYW5kIFdNREUuCiMjIyAtLS0gQ29udGFjdDogZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUlVOIEZST006IHN0YXQxMDA1CiMjIyAtLS0gRGF0ZTogMjAyMC8wNC8yMgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gTElDRU5TRToKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEdQTCB2MgojIyMgLS0tIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBXaWtpZGF0YSBTUEFSUUwgRW5kcG9pbnQgQW5hbHl0aWNzIFByb2plY3QKIyMjIC0tLQojIyMgLS0tIFdpa2lkYXRhIFNQQVJRTCBFbmRwb2ludCBBbmFseXRpY3MgUHJvamVjdCBpcyBmcmVlIHNvZnR3YXJlOiAKIyMjIC0tLSB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5CiMjIyAtLS0gaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnkKIyMjIC0tLSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSBMaWNlbnNlLCBvcgojIyMgLS0tIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uCiMjIyAtLS0KIyMjIC0tLSBXaWtpZGF0YSBTUEFSUUwgRW5kcG9pbnQgQW5hbHl0aWNzIFByb2plY3QgaXMgZGlzdHJpYnV0ZWQgCiMjIyAtLS0gaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyMjIC0tLSBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIyMgLS0tIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyMjIC0tLSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIyMgLS0tCiMjIyAtLS0gWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyMjIC0tLSBhbG9uZyB3aXRoIFdpa2lkYXRhIFNQQVJRTCBFbmRwb2ludCBBbmFseXRpY3MgUHJvamVjdC4KIyMjIC0tLSBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTZWN0aW9uIDEuIENvbGxlY3QgcmF3IGRhdGEgc2V0IGV2ZW50LndkcXNfZXh0ZXJuYWxfc3BhcnFsX3F1ZXJ5CiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyAtLS0gc2V0dXAKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMjIyAtLS0gZGlyZWN0b3JpZXMKIyMjIC0tLSBSZWFkIFdEQ00gcGFyYW1lcmV0ZXJzCiMgLSBmUGF0aDogd2hlcmUgdGhlIHNjcmlwdHMgaXMgcnVuIGZyb20/CmZQYXRoIDwtIGFzLmNoYXJhY3Rlcihjb21tYW5kQXJncyh0cmFpbGluZ09ubHkgPSBGKVs0XSkKZlBhdGggPC0gZ3N1YigiLS1maWxlPSIsICIiLCBmUGF0aCwgZml4ZWQgPSBUKQpmUGF0aCA8LSB1bmxpc3Qoc3Ryc3BsaXQoZlBhdGgsIHNwbGl0ID0gIi8iLCBmaXhlZCA9IFQpKQpmUGF0aCA8LSBwYXN0ZSgKICBwYXN0ZShmUGF0aFsxOmxlbmd0aChmUGF0aCkgLSAxXSwgY29sbGFwc2UgPSAiLyIpLAogICIvIiwKICBzZXAgPSAiIikKZlBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL1dpa2lkYXRhL1dEX21pc2MvV0RfU1BBUlFMX0VuZHBvaW50X0FuYWx5dGljcy8nCmRhdGFEaXIgPC0gcGFzdGUwKGZQYXRoLCAnX2V2ZW50L19kYXRhLycpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoZlBhdGgsICdfZXZlbnQvX2FuYWx5dGljcy8nKQoKIyMjIC0tLSBncmFiIGEgc2FtcGxlIG9mIFNQQVJRTCBxdWVyaWVzIGV2ZW50LndkcXNfZXh0ZXJuYWxfc3BhcnFsX3F1ZXJ5CiMjIyAtLS0gdy4gSGl2ZVFMIGZyb20gQmVlbGluZQojIC0gcXVlcnkgZmlsZW5hbWUKcXVlcnlGaWxlIDwtICJ3ZHFzX0NvbGxlY3REYXRhLmhxbCIKIyAtIEtlcmJlcm9zIEluaXQKa2VyYmVyb3NQcmVmaXggPC0gJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiMgLSBkYXRlUmFuZ2UKc3RhcnREYXRlIDwtICcyMDIwLzA0LzAxJwplbmREYXRlIDwtICcyMDIwLzA0LzIxJwpkYXRlUmFuZ2UgPC0gc2VxKGFzLkRhdGUoc3RhcnREYXRlKSwgYXMuRGF0ZShlbmREYXRlKSwgYnkgPSAiZGF5IikKIyAtIHF1ZXJ5IG92ZXIgZGF0ZVJhbmdlCmZvciAoaSBpbiAxOmxlbmd0aChkYXRlUmFuZ2UpKSB7CiAgcHJpbnQocGFzdGUwKCJTdGFydGVkOiAiLCBkYXRlUmFuZ2VbaV0pKQogICMgLSB5ZWFyLCBtb250aCwgZGF0ZQogIHllYXIgPSBzdWJzdHIoZGF0ZVJhbmdlW2ldLCAxLCA0KQogIG1vbnRoID0gc3Vic3RyKGRhdGVSYW5nZVtpXSwgNiwgNykKICBpZiAoc3Vic3RyKG1vbnRoLCAxLCAxKSA9PSAnMCcpIHttb250aCA9IHN1YnN0cihtb250aCwgMiwgMil9CiAgZGF5ID0gc3Vic3RyKGRhdGVSYW5nZVtpXSwgOSwgMTApCiAgaWYgKHN1YnN0cihkYXksIDEsIDEpID09ICcwJykge2RheSA9IHN1YnN0cihkYXksIDIsIDIpfQogICMgLSBjb21wb3NlIHF1ZXJ5CiAgaGl2ZVFMcXVlcnkgPC0gcGFzdGUwKCJTRUxFQ1QgbWV0YS5kdCBhcyBkdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnZXhwX3JlcGxhY2UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdleHBfcmVwbGFjZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnZXhwX3JlcGxhY2UocXVlcnksICdcXG58XFxyXFxufFxccnxcXG5cXHInLCAnIF9fTkVXTElORV9fICcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1xcdCcsICcgJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ1xcMDAwJywgJyAnKSBhcyBzcGFycWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCwgbWV0YS5kb21haW4gYXMgZG9tYWluLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBodHRwLnJlcXVlc3RfaGVhZGVyc1snWC1CSUdEQVRBLU1BWC1RVUVSWS1NSUxMSVMnXSBhcyBtYXhfcXVlcnlfbWlsbGlzLCBodHRwLm1ldGhvZCBhcyBtZXRob2QsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGh0dHAuc3RhdHVzX2NvZGUgYXMgc3RhdHVzX2NvZGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tlbmRfaG9zdCwgZGF0YWNlbnRlciwgcXVlcnlfdGltZSAKICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGV2ZW50LndkcXNfZXh0ZXJuYWxfc3BhcnFsX3F1ZXJ5IFRBQkxFU0FNUExFIChCVUNLRVQgMSBPVVQgT0YgMTAwIE9OIHJhbmQoKSkgdwogICAgICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIGlzbm90bnVsbChodHRwLnN0YXR1c19jb2RlKSAKICAgICAgICAgICAgICAgICAgICAgICAgICBBTkQgKG1ldGEuZG9tYWluID0gJ3F1ZXJ5Lndpa2lkYXRhLm9yZycpIAogICAgICAgICAgICAgICAgICAgICAgICAgIEFORCB5ZWFyPSIsIHllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICIgQU5EIG1vbnRoPSIsIG1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgICAiIEFORCBkYXk9IiwgZGF5LCAnOycpCiAgZmlsZW5hbWUgPC0gIndkcXNfZGF0YV9zYW1wbGVfVEVTVF8iCiAgZmlsZW5hbWUgPC0gcGFzdGUwKGZpbGVuYW1lLCBkYXRlUmFuZ2VbaV0sICIudHN2IikKICAjIC0gd3JpdGUgaHFsCiAgd3JpdGUoaGl2ZVFMcXVlcnksIHBhc3RlMChmUGF0aCwgcXVlcnlGaWxlKSkKICAjIC0gdG8gUmVwb3J0CiAgcHJpbnQoIkZldGNoaW5nIHNwYXJxbF90ZXN0RGF0YSBmcm9tIHdtZi53ZWJyZXF1ZXN0IG5vdy4iKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChmUGF0aCwgcXVlcnlGaWxlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciID4gJywgZGF0YURpciwgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIiksCiAgICAgICAgICAgICAgICAgIHdhaXQgPSBUUlVFKQogIHByaW50KHBhc3RlMCgiQ29tcGxldGVkOiAiLCBkYXRlUmFuZ2VbaV0pKQogIHByaW50KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSIpCn0KIyAtIHRvIFJlcG9ydApwcmludCgiRE9ORSB3LiBFVEwgZnJvbSBIYWRvb3A6IHdtZi53ZWJyZXF1ZXN0LiIpCmBgYAoKCiMjIDEuIEV4cGxvcmF0b3J5IERhdGEgQW5hbHl0aWNzIAoKQWxsIG9mIHRoZSBmb2xsb3dpbmcgYW5hbHl0aWNzIHdlcmUgZGVyaXZlZCBmcm9tIHRoZSBkYXRhc2V0cyBwcm9kdWNlZCBieSB0aGUgZm9sbG93aW5nIFIgc2NyaXB0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBGLCBtZXNzYWdlID0gRn0KCiMhL3Vzci9iaW4vZW52IFJzY3JpcHQKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBXRFFTX0V2ZW50X1M1X1JlcG9ydGluZ0RhdGFzZXRzLlIsIHYuIDAuMC4xCiMjIyAtLS0gU2NyaXB0OiBXRFFTX0V2ZW50X1M1X1JlcG9ydGluZ0RhdGFzZXRzLlIsIHYuIDAuMC4xCiMjIyAtLS0gRGVzY3JpcHRpb246IFdEUVNfRXZlbnRfUzVfUmVwb3J0aW5nRGF0YXNldHMuUiBpcyB1c2VkCiMjIyAtLS0gb25seSB0byBwcm9kdWNlIHRoZSByZXBvcnRpbmcgZGF0YXNldHMgZm9yIHRoZSAKIyMjIC0tLSBXaWtpZGF0YSBTUEFSUUwgRW5kcG9pbnQgQW5hbHl0aWNzIFByb2plY3QuCiMjIyAtLS0gQWxsIGRhdGEgYXJlIG9idGFpbmVkIGZyb20gdGhlIGV2ZW50LndkcXNfZXh0ZXJuYWxfc3BhcnFsX3F1ZXJ5IAojIyMgLS0tIHRhYmxlIGluIHRoZSBXTUYgRGF0YSBMYWtlLgojIyMgLS0tIEF1dGhvcjogR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgU2NpZW50aXN0LCBXTURFCiMjIyAtLS0gRGV2ZWxvcGVkIHVuZGVyIHRoZSBjb250cmFjdCBiZXR3ZWVuIEdvcmFuIE1pbG92YW5vdmljIFBSIERhdGEgS29sZWt0aXYKIyMjIC0tLSBhbmQgV01ERS4KIyMjIC0tLSBDb250YWN0OiBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBSVU4gRlJPTTogc3RhdDEwMDUKIyMjIC0tLSBEYXRlOiAyMDIwLzA0LzI3CiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBMSUNFTlNFOgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gR1BMIHYyCiMjIyAtLS0gVGhpcyBmaWxlIGlzIHBhcnQgb2YgdGhlIFdpa2lkYXRhIFNQQVJRTCBFbmRwb2ludCBBbmFseXRpY3MgUHJvamVjdAojIyMgLS0tCiMjIyAtLS0gV2lraWRhdGEgU1BBUlFMIEVuZHBvaW50IEFuYWx5dGljcyBQcm9qZWN0IGlzIGZyZWUgc29mdHdhcmU6IAojIyMgLS0tIHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkKIyMjIC0tLSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQojIyMgLS0tIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yCiMjIyAtLS0gKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4KIyMjIC0tLQojIyMgLS0tIFdpa2lkYXRhIFNQQVJRTCBFbmRwb2ludCBBbmFseXRpY3MgUHJvamVjdCBpcyBkaXN0cmlidXRlZCAKIyMjIC0tLSBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIyMgLS0tIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMjIyAtLS0gTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIyMgLS0tIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMjIyAtLS0KIyMjIC0tLSBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIyMgLS0tIGFsb25nIHdpdGggV2lraWRhdGEgU1BBUlFMIEVuZHBvaW50IEFuYWx5dGljcyBQcm9qZWN0LgojIyMgLS0tIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi4KIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLSBzZXR1cApsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShodHRyKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoeGdib29zdCkKbGlicmFyeShjYXJldCkKbGlicmFyeShmYXN0RHVtbWllcykKCiMjIyAtLS0gZGlyZWN0b3JpZXMKIyMjIC0tLSBSZWFkIFdEQ00gcGFyYW1lcmV0ZXJzCiMgLSBmUGF0aDogd2hlcmUgdGhlIHNjcmlwdHMgaXMgcnVuIGZyb20/CmZQYXRoIDwtIGFzLmNoYXJhY3Rlcihjb21tYW5kQXJncyh0cmFpbGluZ09ubHkgPSBGQUxTRSlbNF0pCmZQYXRoIDwtIGdzdWIoIi0tZmlsZT0iLCAiIiwgZlBhdGgsIGZpeGVkID0gVCkKZlBhdGggPC0gdW5saXN0KHN0cnNwbGl0KGZQYXRoLCBzcGxpdCA9ICIvIiwgZml4ZWQgPSBUKSkKZlBhdGggPC0gcGFzdGUoCiAgcGFzdGUoZlBhdGhbMTpsZW5ndGgoZlBhdGgpIC0gMV0sIGNvbGxhcHNlID0gIi8iKSwKICAiLyIsCiAgc2VwID0gIiIpCgpmUGF0aCA8LSAnL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvV2lraWRhdGEvV0RfbWlzYy9XRF9TUEFSUUxfRW5kcG9pbnRfQW5hbHl0aWNzLycKIyAtIE5PVEU6IEhldHpuZXIgbG9jYWwgZGlyCiMgIC0gZlBhdGggPC0gJy9ob21lL2dvcmFuL1dNREUvV0RfU1BBUlFMX0VuZHBvaW50X0FuYWx5dGljcy8nCmRhdGFEaXIgPC0gcGFzdGUwKGZQYXRoLCAnX2V2ZW50L19kYXRhLycpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoZlBhdGgsICdfZXZlbnQvX2FuYWx5dGljcy8nKQpyZXBvcnRpbmdEaXIgPC0gcGFzdGUwKGZQYXRoLCAnX2V2ZW50L19yZXBvcnRpbmcvJykKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTZWN0aW9uIDEuIFByb2R1Y2UgcmVwb3J0aW5nIGRhdGFzZXQKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLSBmZWF0dXJlIGZyZXF1ZW5jaWVzOiB2b2NhYnVsYXJ5CmZpbGVuYW1lIDwtICdxdWVyaWVzX3ZvY2FidWxhcnkuY3N2Jwp2b2NhYnVsYXJ5IDwtIGZyZWFkKHBhc3RlMChhbmFseXRpY3NEaXIsIGZpbGVuYW1lKSkKdm9jYWJ1bGFyeSRWMSA8LSBOVUxMCgojIyMgLS0tIGRhdGFzZXQKZmlsZW5hbWUgPC0gJ3dkcXNfZGF0YV9zYW1wbGVfMjAyMC0wNC0yMi5jc3YnCmRhdGFTZXQgPC0gZnJlYWQocGFzdGUwKGFuYWx5dGljc0RpciwgZmlsZW5hbWUpKQpkYXRhU2V0JFYxIDwtIE5VTEwKIyAtIGRlcml2ZTogbl9jb25jX3F1ZXJpZXMgcGVyIGRhdGFjZW50ZXIgeCBiYWNrX2VuZGhvc3QKZGF0YVNldCRkYXRhY2VudGVyX2hvc3QgPC0gcGFzdGUoZGF0YVNldCRkYXRhY2VudGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhU2V0JGJhY2tlbmRfaG9zdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJfIikKZGF0YVNldCRkYXRhY2VudGVyIDwtIE5VTEwKZGF0YVNldCRiYWNrZW5kX2hvc3QgPC0gTlVMTAojIC0gdGhlIG51bWJlciBvZiBxdWVyaWVzIHN0YXJ0ZWQgaW4gdGhlIHNhbWUgbWludXRlCiMgLSBhcyB0aGUgdGFyZ2V0IHF1ZXJ5CiMgLSBwZXIgZGF0YWNlbnRlciB4IGJhY2tfZW5kaG9zdApkYXRhU2V0JGRhdGFjZW50ZXJfaG9zdF95bWRobSA8LSBwYXN0ZShkYXRhU2V0JGRhdGFjZW50ZXJfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YVNldCR5bWRobSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJfIikKZGF0YVNldCR5bWRobSA8LSBOVUxMCm5fY29uY19xdWVyaWVzIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoZGF0YWNlbnRlcl9ob3N0X3ltZGhtKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGRhdGFjZW50ZXJfaG9zdF95bWRobSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2Uobl9jb25jX3F1ZXJpZXMgPSBuKCkpCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OmxlZnRfam9pbihuX2NvbmNfcXVlcmllcywgYnkgPSAiZGF0YWNlbnRlcl9ob3N0X3ltZGhtIikKZGF0YVNldCRkYXRhY2VudGVyX2hvc3RfeW1kaG0gPC0gTlVMTApybShuX2NvbmNfcXVlcmllcykKIyAtIGRlcml2ZTogbWlsZCBhbmQgZXh0cmVtZSBvdXRsaWVycyBvbiBxdWVyeV90aW1lCiMgLSBxdWVyeV90aW1lID49IHEzICsgMyppcSAKIyAtIHEzID0gNzUgcGVyY2VudGlsZSwgcTEgPSAyNSBwZXJjZW50aWxlCnEzIDwtIHF1YW50aWxlKGRhdGFTZXQkcXVlcnlfdGltZSwgLjc1KQpxMSA8LSBxdWFudGlsZShkYXRhU2V0JHF1ZXJ5X3RpbWUsIC4yNSkKIyAtIGlxID0gcTMgLSBxMTsgSW50ZXJxdWFydGlsZSByYW5nZQppcSA8LSB1bm5hbWUocTMgLSBxMSkKdXBwZXJfb3V0ZXJfZmVuY2UgPC0gcTMgKyAzKmlxCnVwcGVyX2lubmVyX2ZlbmNlIDwtIHEzICsgMS41KmlxCmRhdGFTZXQkcXVlcnlfdGltZV9leHRyZW1lIDwtIAogIGlmZWxzZShkYXRhU2V0JHF1ZXJ5X3RpbWUgPj0gdXBwZXJfb3V0ZXJfZmVuY2UsIDEsIDApCmRhdGFTZXQkcXVlcnlfdGltZV9taWxkIDwtIAogIGlmZWxzZShkYXRhU2V0JHF1ZXJ5X3RpbWUgPj0gdXBwZXJfaW5uZXJfZmVuY2UsIDEsIDApCiMgLSBkZXJpdmUgbXVsdGljbGFzcyBEVjogMCAtIG1pbGQsIG1pbGQgLSBleHRyZW1lLCBleHRyZW1lIHF1ZXJ5X3RpbWUKZGF0YVNldCRxdWVyeV90aW1lX2NsYXNzIDwtIDAKZGF0YVNldCRxdWVyeV90aW1lX2NsYXNzW2RhdGFTZXQkcXVlcnlfdGltZV9leHRyZW1lID09IDAgJiBkYXRhU2V0JHF1ZXJ5X3RpbWVfbWlsZCA9PSAxXSA8LSAxCmRhdGFTZXQkcXVlcnlfdGltZV9jbGFzc1tkYXRhU2V0JHF1ZXJ5X3RpbWVfZXh0cmVtZSA9PSAxXSA8LSAyCiMgLSBkZXJpdmU6IGJlbG93IGFuZCBhYm92ZSBtZWRpYW4gcXVlcnlfdGltZQpkYXRhU2V0JHF1ZXJ5X3RpbWVfbWVkaWFuIDwtIGlmZWxzZShkYXRhU2V0JHF1ZXJ5X3RpbWUgPj0gbWVkaWFuKGRhdGFTZXQkcXVlcnlfdGltZSksIDEsIDApCgojIyMgLS0tIHZvY2FidWxhcnk6IHRvcF9mIG1vc3QgZnJlcXVlbnRseSB1c2VkIGZlYXR1cmVzCnRvcF9mIDwtIDMwMApvYl9mZWF0dXJlcyA8LSBjKCduY2hhcicsICdfX3ZhcnNfXycsICdfX3ZhcnNfdXNhZ2VfXycsICdfX2xpdGVyYWxfXycsIAogICAgICAgICAgICAgICAgICdfX3ZhcnNfbGFiZWxfXycsICdfX3ZhcnNfbGFiZWxfdXNhZ2VfXycsICdfX2lyaV9yZWZfXycpCnZvY2FidWxhcnkgPC0gdm9jYWJ1bGFyeSRmZWF0dXJlWzE6dG9wX2ZdCnZvY2FidWxhcnkgPC0gdW5pcXVlKGModm9jYWJ1bGFyeSwgb2JfZmVhdHVyZXMpKQoKIyMjIC0tLSBqb2luIHZvY2FidWxhcnkgdG8gZGF0YVNldApxdWVyaWVzX3Byb2Nlc3NlZCA8LSBmcmVhZChwYXN0ZTAoYW5hbHl0aWNzRGlyLCAncXVlcmllc19wcm9jZXNzZWQuY3N2JykpCnF1ZXJpZXNfcHJvY2Vzc2VkJFYxIDwtIE5VTEwKZGltKHF1ZXJpZXNfcHJvY2Vzc2VkKQpxdWVyaWVzX3Byb2Nlc3NlZCA8LSBxdWVyaWVzX3Byb2Nlc3NlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihmZWF0dXJlICVpbiUgdm9jYWJ1bGFyeSkKcm0odm9jYWJ1bGFyeSkKZGltKHF1ZXJpZXNfcHJvY2Vzc2VkKQpmZWF0dXJlRnJhbWUgPC0gZGF0YS50YWJsZTo6ZGNhc3QoYXMuZGF0YS50YWJsZShxdWVyaWVzX3Byb2Nlc3NlZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWVTcGFycWxJZCB+IGZlYXR1cmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS52YXIgPSAiZnJlcSIpCnJtKHF1ZXJpZXNfcHJvY2Vzc2VkKTsgZ2MoKQojIC0gbWFyayBmZWF0dXJlcyBpbiBkYXRhU2V0OiBkcwpjb2xuYW1lcyhkYXRhU2V0KSA8LSBwYXN0ZTAoImRzXyIsIGNvbG5hbWVzKGRhdGFTZXQpKQojIC0gam9pbjoKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6bGVmdF9qb2luKGZlYXR1cmVGcmFtZSwgCiAgICAgICAgICAgICAgICAgICBieSA9IGMoJ2RzX3VuaXF1ZVNwYXJxbElkJyA9ICd1bmlxdWVTcGFycWxJZCcpKQpybShmZWF0dXJlRnJhbWUpOyBnYygpCmNvbG5hbWVzKGRhdGFTZXQpCgojIC0gc3RvcmUgcmVwb3J0aW5nIGRhdGEuZnJhbWU6IHJlcG9ydGluZ0ZyYW1lLmNzdgpmaWxlbmFtZSA8LSBwYXN0ZTAoJ3JlcG9ydGluZ0ZyYW1lJywgJ18nLCB0b3BfZiwgJy5jc3YnKQp3cml0ZS5jc3YoZGF0YVNldCwKICAgICAgICAgIHBhc3RlMChyZXBvcnRpbmdEaXIsIGZpbGVuYW1lKSkKCiMgLSBjbGVhcgpybShsaXN0ID0gc2V0ZGlmZihscygpLCBjKCdyZXBvcnRpbmdEaXInLCAnYW5hbHl0aWNzRGlyJywgJ2RhdGFEaXInLCAnZlBhdGgnKSkpCmdjKCkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTZWN0aW9uIDIuIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLSBsb2FkIHJlcG9ydGluZyBkYXRhLmZyYW1lCnRvcF9mIDwtIDMwMApmaWxlbmFtZSA8LSBwYXN0ZTAoJ3JlcG9ydGluZ0ZyYW1lJywgJ18nLCB0b3BfZiwgJy5jc3YnKQpyZXBvcnRGcmFtZSA8LSBmcmVhZChwYXN0ZTAocmVwb3J0aW5nRGlyLCBmaWxlbmFtZSkpCnJlcG9ydEZyYW1lJFYxIDwtIE5VTEwKCiMjIyAtLS0gcmVwb3J0RnJhbWUkcmVjb2RlIGRzX2RheSB0byBkYXkgb2Ygd2VlawpzYW1wbGVEYXlzIDwtIGlmZWxzZShuY2hhcihyZXBvcnRGcmFtZSRkc19kYXkpID09IDEsIAogICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoJzAnLCByZXBvcnRGcmFtZSRkc19kYXkpLAogICAgICAgICAgICAgICAgICAgICByZXBvcnRGcmFtZSRkc19kYXkpCnJlcG9ydEZyYW1lJGRzX2RheSA8LSBwYXN0ZTAoIjIwMjAtMDQtIiwgc2FtcGxlRGF5cykKcmVwb3J0RnJhbWUkZHNfZGF5IDwtIHdlZWtkYXlzKGFzLkRhdGUocmVwb3J0RnJhbWUkZHNfZGF5KSkKcm0oc2FtcGxlRGF5cykKCiMjIyAtLS0gU2FtcGxlIGNoYXJhY3RlcmlzdGljcwojIC0gZXh0ZXJuYWwgKG5vbi1TUEFSUUwgcGFyc2luZyBiYXNlZCkgZmF0dXJlcyBpbiB0aGUgZGF0YXNldApkc19mZWF0dXJlcyA8LSAKICBjb2xuYW1lcyhyZXBvcnRGcmFtZSlbd2hpY2goZ3JlcGwoIl5kc18iLCBjb2xuYW1lcyhyZXBvcnRGcmFtZSkpKV0KCiMgLSBkYXlzCnNhbXBsZURheXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShyZXBvcnRGcmFtZSRkc19kYXkpKQpjb2xuYW1lcyhzYW1wbGVEYXlzKSA8LSBjKCdEYXknLCAnTnVtLlF1ZXJpZXMnKQpzYW1wbGVEYXlzJFBlcmNlbnQgPC0gcm91bmQoCiAgc2FtcGxlRGF5cyROdW0uUXVlcmllcy9zdW0oc2FtcGxlRGF5cyROdW0uUXVlcmllcykqMTAwLCAyKQpzYW1wbGVEYXlzIDwtZHBseXI6OmFycmFuZ2Uoc2FtcGxlRGF5cywgZGVzYyhQZXJjZW50KSkKd3JpdGUuY3N2KHNhbXBsZURheXMsIHBhc3RlMChyZXBvcnRpbmdEaXIsICdzYW1wbGVDaGFyX0RheXMuY3N2JykpCiMgLSBob3VycwpzYW1wbGVIb3VycyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHJlcG9ydEZyYW1lJGRzX2hvdXIpKQpjb2xuYW1lcyhzYW1wbGVIb3VycykgPC0gYygnSG91cicsICdOdW0uUXVlcmllcycpCnNhbXBsZUhvdXJzJFBlcmNlbnQgPC0gcm91bmQoCiAgc2FtcGxlSG91cnMkTnVtLlF1ZXJpZXMvc3VtKHNhbXBsZUhvdXJzJE51bS5RdWVyaWVzKSoxMDAsIDIpCnNhbXBsZUhvdXJzIDwtZHBseXI6OmFycmFuZ2Uoc2FtcGxlSG91cnMsIGRlc2MoUGVyY2VudCkpCndyaXRlLmNzdihzYW1wbGVIb3VycywgcGFzdGUwKHJlcG9ydGluZ0RpciwgJ3NhbXBsZUNoYXJfSG91cnMuY3N2JykpCiMgLSBIVFRQIG1ldGhvZApzYW1wbGVNZXRob2QgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShyZXBvcnRGcmFtZSRkc19tZXRob2QpKQpjb2xuYW1lcyhzYW1wbGVNZXRob2QpIDwtIGMoJ0hUVFAubWV0aG9kJywgJ051bS5RdWVyaWVzJykKc2FtcGxlTWV0aG9kJFBlcmNlbnQgPC0gcm91bmQoCiAgc2FtcGxlTWV0aG9kJE51bS5RdWVyaWVzL3N1bShzYW1wbGVNZXRob2QkTnVtLlF1ZXJpZXMpKjEwMCwgMikKc2FtcGxlTWV0aG9kIDwtZHBseXI6OmFycmFuZ2Uoc2FtcGxlTWV0aG9kLCBkZXNjKFBlcmNlbnQpKQp3cml0ZS5jc3Yoc2FtcGxlTWV0aG9kLCBwYXN0ZTAocmVwb3J0aW5nRGlyLCAnc2FtcGxlQ2hhcl9IVFRQbWV0aG9kLmNzdicpKQojIC0gZGF0YWNlbnRlcl9ob3N0CnNhbXBsZV9kYXRhY2VudGVyX2hvc3QgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShyZXBvcnRGcmFtZSRkc19kYXRhY2VudGVyX2hvc3QpKQpjb2xuYW1lcyhzYW1wbGVfZGF0YWNlbnRlcl9ob3N0KSA8LSBjKCdEYXRhY2VudGVyLkhvc3QnLCAnTnVtLlF1ZXJpZXMnKQpzYW1wbGVfZGF0YWNlbnRlcl9ob3N0JFBlcmNlbnQgPC0gcm91bmQoCiAgc2FtcGxlX2RhdGFjZW50ZXJfaG9zdCROdW0uUXVlcmllcy9zdW0oc2FtcGxlX2RhdGFjZW50ZXJfaG9zdCROdW0uUXVlcmllcykqMTAwLCAyKQpzYW1wbGVfZGF0YWNlbnRlcl9ob3N0IDwtZHBseXI6OmFycmFuZ2Uoc2FtcGxlX2RhdGFjZW50ZXJfaG9zdCwgZGVzYyhQZXJjZW50KSkKd3JpdGUuY3N2KHNhbXBsZV9kYXRhY2VudGVyX2hvc3QsIHBhc3RlMChyZXBvcnRpbmdEaXIsICdzYW1wbGVDaGFyX0RhdGFjZW50ZXJIb3N0LmNzdicpKQojIC0gU3RhdHVzIENvZGUKc2FtcGxlX1N0YXR1c0NvZGUgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShyZXBvcnRGcmFtZSRkc19zdGF0dXNfY29kZSkpCmNvbG5hbWVzKHNhbXBsZV9TdGF0dXNDb2RlKSA8LSBjKCdTdGF0dXMnLCAnTnVtLlF1ZXJpZXMnKQpzYW1wbGVfU3RhdHVzQ29kZSRQZXJjZW50IDwtIHJvdW5kKAogIHNhbXBsZV9TdGF0dXNDb2RlJE51bS5RdWVyaWVzL3N1bShzYW1wbGVfU3RhdHVzQ29kZSROdW0uUXVlcmllcykqMTAwLCAyKQpzYW1wbGVfU3RhdHVzQ29kZSA8LWRwbHlyOjphcnJhbmdlKHNhbXBsZV9TdGF0dXNDb2RlLCBkZXNjKFBlcmNlbnQpKQp3cml0ZS5jc3Yoc2FtcGxlX1N0YXR1c0NvZGUsIHBhc3RlMChyZXBvcnRpbmdEaXIsICdzYW1wbGVDaGFyX1N0YXR1c0NvZGUuY3N2JykpCiMgLSBGb3JtYXQKc2FtcGxlX0Zvcm1hdCA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHJlcG9ydEZyYW1lJGRzX2Zvcm1hdCkpCmNvbG5hbWVzKHNhbXBsZV9Gb3JtYXQpIDwtIGMoJ0Zvcm1hdCcsICdOdW0uUXVlcmllcycpCnNhbXBsZV9Gb3JtYXQkUGVyY2VudCA8LSByb3VuZCgKICBzYW1wbGVfRm9ybWF0JE51bS5RdWVyaWVzL3N1bShzYW1wbGVfRm9ybWF0JE51bS5RdWVyaWVzKSoxMDAsIDIpCnNhbXBsZV9Gb3JtYXQgPC1kcGx5cjo6YXJyYW5nZShzYW1wbGVfRm9ybWF0LCBkZXNjKFBlcmNlbnQpKQp3cml0ZS5jc3Yoc2FtcGxlX0Zvcm1hdCwgcGFzdGUwKHJlcG9ydGluZ0RpciwgJ3NhbXBsZV9Gb3JtYXQuY3N2JykpCiMgLSBEaXN0cmlidXRpb24gb2YgdGhlIG51bWJlciBvZiBxdWVyaWVzIAojIC0gY29uY3VycmVudGx5IHN0YXJ0ZWQgaW4gdGhlIHNhbWUgbWludXRlCnJlcG9ydEZyYW1lJGRzX2RheV9ob3VyX21pbnV0ZSA8LSBwYXN0ZShyZXBvcnRGcmFtZSRkc19kYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBvcnRGcmFtZSRkc19ob3VyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwb3J0RnJhbWUkZHNfbWludXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIi0iKQpzYW1wbGVfZGF0YWNlbnRlcl9ob3N0X3FycXVlcmllcyA8LSByZXBvcnRGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChkc19kYXRhY2VudGVyX2hvc3QsIGRzX2RheV9ob3VyX21pbnV0ZSkgJT4lIAogIGRwbHlyOjpncm91cF9ieShkc19kYXRhY2VudGVyX2hvc3QsIGRzX2RheV9ob3VyX21pbnV0ZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UocXVlcnlfY291bnQgPSBuKCkpICU+JQogIGRwbHlyOjpzZWxlY3QoZHNfZGF0YWNlbnRlcl9ob3N0LCBxdWVyeV9jb3VudCkgJT4lIAogIGRwbHlyOjpncm91cF9ieShkc19kYXRhY2VudGVyX2hvc3QpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKG1lYW5fY29uY19xdWVyaWVzID0gbWVhbihxdWVyeV9jb3VudCksIAogICAgICAgICAgICAgICAgICAgbWVkaWFuX2NvbmNfcXVlcmllcyA9IG1lZGlhbihxdWVyeV9jb3VudCksIAogICAgICAgICAgICAgICAgICAgc3RkZXZfY29uY19xdWVyaWVzID0gc2QocXVlcnlfY291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKG1lYW5fY29uY19xdWVyaWVzKSkKd3JpdGUuY3N2KHNhbXBsZV9kYXRhY2VudGVyX2hvc3RfcXJxdWVyaWVzLCAKICAgICAgICAgIHBhc3RlMChyZXBvcnRpbmdEaXIsICdzYW1wbGVfZGF0YWNlbnRlcl9ob3N0X3FycXVlcmllcy5jc3YnKSkKcmVwb3J0RnJhbWUkZHNfZGF5X2hvdXJfbWludXRlIDwtIE5VTEwKCiMjIyAtLS0gUHJvZHVjZSByZXBlYXRlZCBxdWVyaWVzIGRhdGFzZXQKcmVwZWF0ZWRRdWVyaWVzIDwtIHJlcG9ydEZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGRzX3VuaXF1ZVNwYXJxbElkLCAKICAgICAgICAgICAgICAgIGRzX3NwYXJxbCwgCiAgICAgICAgICAgICAgICBkc19xdWVyeV90aW1lKQpjb2xuYW1lcyhyZXBlYXRlZFF1ZXJpZXMpCnJlcGVhdGVkUXVlcmllcyA8LSByZXBlYXRlZFF1ZXJpZXMgJT4lIAogIGRwbHlyOjpncm91cF9ieShkc191bmlxdWVTcGFycWxJZCwgZHNfc3BhcnFsKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShudW0gPSBuKCksCiAgICAgICAgICAgICAgICAgICBtZWFuX3F1ZXJ5X3RpbWUgPSBtZWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIG1lZGlhbl9xdWVyeV90aW1lID0gbWVkaWFuKGRzX3F1ZXJ5X3RpbWUpLAogICAgICAgICAgICAgICAgICAgc3RkZXZfcXVlcnlfdGltZSA9IHNkKGRzX3F1ZXJ5X3RpbWUpKQpyZXBlYXRlZFF1ZXJpZXMkUGVyY2VudCA8LSByb3VuZCgKICByZXBlYXRlZFF1ZXJpZXMkbnVtL3N1bShyZXBlYXRlZFF1ZXJpZXMkbnVtKSoxMDAsIDYpCnJlcGVhdGVkUXVlcmllcyA8LSBkcGx5cjo6YXJyYW5nZShyZXBlYXRlZFF1ZXJpZXMsIGRlc2MobnVtKSkKd3JpdGUuY3N2KHJlcGVhdGVkUXVlcmllcywgCiAgICAgICAgICBwYXN0ZTAocmVwb3J0aW5nRGlyLCAncmVwZWF0ZWRRdWVyaWVzLmNzdicpKQpybShyZXBlYXRlZFF1ZXJpZXMpOyBnYygpCgojIyMgLS0tIENyb3NzLVRhYnVsYXRpb246IGRzXyogZmVhdHVyZXMgeCBxdWVyeV90aW1lCiMgLSBkc19mb3JtYXQKY3Jvc3NUYWJzX2Zvcm1hdCA8LSByZXBvcnRGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChkc19mb3JtYXQsIGRzX3F1ZXJ5X3RpbWUpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZHNfZm9ybWF0KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuX3F1ZXJ5X3RpbWUgPSBtZWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIG1lZGlhbl9xdWVyeV90aW1lID0gbWVkaWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIHN0ZGV2X3F1ZXJ5X3RpbWUgPSBzZChkc19xdWVyeV90aW1lKSkgJT4lIAogIGRwbHlyOjphcnJhbmdlKGRlc2MobWVhbl9xdWVyeV90aW1lKSkKd3JpdGUuY3N2KGNyb3NzVGFic19mb3JtYXQsIAogICAgICAgICAgcGFzdGUwKHJlcG9ydGluZ0RpciwgJ2Nyb3NzVGFic19mb3JtYXQuY3N2JykpCiMgLSBkc19kYXkKY3Jvc3NUYWJzX2RheSA8LSByZXBvcnRGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChkc19kYXksIGRzX3F1ZXJ5X3RpbWUpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZHNfZGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuX3F1ZXJ5X3RpbWUgPSBtZWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIG1lZGlhbl9xdWVyeV90aW1lID0gbWVkaWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIHN0ZGV2X3F1ZXJ5X3RpbWUgPSBzZChkc19xdWVyeV90aW1lKSkgJT4lIAogIGRwbHlyOjphcnJhbmdlKGRlc2MobWVhbl9xdWVyeV90aW1lKSkKd3JpdGUuY3N2KGNyb3NzVGFic19kYXksIAogICAgICAgICAgcGFzdGUwKHJlcG9ydGluZ0RpciwgJ2Nyb3NzVGFic19kYXkuY3N2JykpCiMgLSBkc19tZXRob2QKY3Jvc3NUYWJzX21ldGhvZCA8LSByZXBvcnRGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChkc19tZXRob2QsIGRzX3F1ZXJ5X3RpbWUpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZHNfbWV0aG9kKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuX3F1ZXJ5X3RpbWUgPSBtZWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIG1lZGlhbl9xdWVyeV90aW1lID0gbWVkaWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIHN0ZGV2X3F1ZXJ5X3RpbWUgPSBzZChkc19xdWVyeV90aW1lKSkgJT4lIAogIGRwbHlyOjphcnJhbmdlKGRlc2MobWVhbl9xdWVyeV90aW1lKSkKd3JpdGUuY3N2KGNyb3NzVGFic19tZXRob2QsIAogICAgICAgICAgcGFzdGUwKHJlcG9ydGluZ0RpciwgJ2Nyb3NzVGFic19tZXRob2QuY3N2JykpCiMgLSBkc19kYXRhY2VudGVyX2hvc3QKY3Jvc3NUYWJzX2RhdGFjZW50ZXJfaG9zdCA8LSByZXBvcnRGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChkc19kYXRhY2VudGVyX2hvc3QsIGRzX3F1ZXJ5X3RpbWUpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZHNfZGF0YWNlbnRlcl9ob3N0KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuX3F1ZXJ5X3RpbWUgPSBtZWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIG1lZGlhbl9xdWVyeV90aW1lID0gbWVkaWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIHN0ZGV2X3F1ZXJ5X3RpbWUgPSBzZChkc19xdWVyeV90aW1lKSkgJT4lIAogIGRwbHlyOjphcnJhbmdlKGRlc2MobWVhbl9xdWVyeV90aW1lKSkKd3JpdGUuY3N2KGNyb3NzVGFic19kYXRhY2VudGVyX2hvc3QsIAogICAgICAgICAgcGFzdGUwKHJlcG9ydGluZ0RpciwgJ2Nyb3NzVGFic19kYXRhY2VudGVyX2hvc3QuY3N2JykpCgojIC0gZHNfc3RhdHVzX2NvZGUKY3Jvc3NUYWJzX3N0YXR1c19jb2RlIDwtIHJlcG9ydEZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGRzX3N0YXR1c19jb2RlLCBkc19xdWVyeV90aW1lKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGRzX3N0YXR1c19jb2RlKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuX3F1ZXJ5X3RpbWUgPSBtZWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIG1lZGlhbl9xdWVyeV90aW1lID0gbWVkaWFuKGRzX3F1ZXJ5X3RpbWUpLCAKICAgICAgICAgICAgICAgICAgIHN0ZGV2X3F1ZXJ5X3RpbWUgPSBzZChkc19xdWVyeV90aW1lKSkgJT4lIAogIGRwbHlyOjphcnJhbmdlKGRlc2MobWVhbl9xdWVyeV90aW1lKSkKd3JpdGUuY3N2KGNyb3NzVGFic19zdGF0dXNfY29kZSwgCiAgICAgICAgICBwYXN0ZTAocmVwb3J0aW5nRGlyLCAnY3Jvc3NUYWJzX3N0YXR1c19jb2RlLmNzdicpKQpgYGAKCiMjIyAxLjEgU2FtcGxlIERlc2NyaXB0aW9uCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIG1lc3NhZ2UgPSBGfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmBgYAoKRmlyc3Qgd2UgcHJvdmlkZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldC4KCiMjIyMgMS4xLjEgVGhlIGRpc3RyaWJ1dGlvbiBvZiBxdWVyaWVzIHBlciBkYXkgb2Ygd2VlawoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdzYW1wbGVDaGFyX0RheXMuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSREYXkgPC0gZmFjdG9yKHBGcmFtZSREYXksCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJ01vbmRheScsICdUdWVzZGF5JywgJ1dlZG5lc2RheScsICdUaHVyc2RheScsICdGcmlkYXknLCAnU2F0dXJkYXknLCAnU3VuZGF5JykpCmdncGxvdChkYXRhID0gcEZyYW1lLCAKICAgICAgIGFlcyh4ID0gRGF5LCAKICAgICAgICAgICB5ID0gTnVtLlF1ZXJpZXMsIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUwKFBlcmNlbnQsICIlIikpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrcmVkJywgZmlsbCA9ICdyZWQnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsgCiAgdGhlbWVfYncoKQpgYGAKCiMjIyMgMS4xLjIgVGhlIGRpc3RyaWJ1dGlvbiBvZiBxdWVyaWVzIHBlciBob3VyIG9mIGRheQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdzYW1wbGVDaGFyX0hvdXJzLmNzdicsIAogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpwRnJhbWUkSG91ciA8LSBmYWN0b3IocEZyYW1lJEhvdXIsCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGFzLmNoYXJhY3RlcigwOjIzKSkKZ2dwbG90KGRhdGEgPSBwRnJhbWUsIAogICAgICAgYWVzKHggPSBIb3VyLCAKICAgICAgICAgICB5ID0gTnVtLlF1ZXJpZXMsIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUwKFBlcmNlbnQsICIlIikpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrcmVkJywgZmlsbCA9ICdyZWQnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsgCiAgdGhlbWVfYncoKQpgYGAKCiMjIyMgMS4xLjNBIFRoZSBkaXN0cmlidXRpb24gb2YgcXVlcmllcyBwZXIgV01GIERhdGFjZW50ZXIvSG9zdAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdzYW1wbGVDaGFyX0RhdGFjZW50ZXJIb3N0LmNzdicsIAogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpwRnJhbWUkRGF0YWNlbnRlci5Ib3N0IDwtIGZhY3RvcihwRnJhbWUkRGF0YWNlbnRlci5Ib3N0LAogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSB1bmlxdWUocEZyYW1lJERhdGFjZW50ZXIuSG9zdCkpCmdncGxvdChkYXRhID0gcEZyYW1lLCAKICAgICAgIGFlcyh4ID0gRGF0YWNlbnRlci5Ib3N0LCAKICAgICAgICAgICB5ID0gTnVtLlF1ZXJpZXMsIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUwKFBlcmNlbnQsICIlIikpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrZ3JlZW4nLCBmaWxsID0gJ2dyZWVuJywgc3RhdCA9ICdpZGVudGl0eScpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMykgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCiMjIyMgMS4xLjNCIFRoZSBtZWFuIGFuZCBtZWRpYW4gbnVtYmVyIG9mIHF1ZXJpZXMgY29uY3VycmVudGx5IHN0YXJ0ZWQgaW4gdGhlIHNhbWUgbWludXRlLCBwZXIgV01GIERhdGFjZW50ZXIvSG9zdAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdzYW1wbGVfZGF0YWNlbnRlcl9ob3N0X3FycXVlcmllcy5jc3YnLCAKICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIAogICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwgCiAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcEZyYW1lJERhdGFjZW50ZXIuSG9zdCA8LSBmYWN0b3IocEZyYW1lJGRzX2RhdGFjZW50ZXJfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gdW5pcXVlKHBGcmFtZSRkc19kYXRhY2VudGVyX2hvc3QpKQpnZ3Bsb3QoZGF0YSA9IHBGcmFtZSwgCiAgICAgICBhZXMoeCA9IGRzX2RhdGFjZW50ZXJfaG9zdCwgCiAgICAgICAgICAgeSA9IG1lYW5fY29uY19xdWVyaWVzLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlMCgiTWVkOiAiLCBtZWRpYW5fY29uY19xdWVyaWVzKSkpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuMTUsIGNvbG9yID0gJ2RhcmtncmVlbicsIGZpbGwgPSAnZ3JlZW4nLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArIAogIHhsYWIoJ1dNRiBEYXRhY2VudGVyL0hvc3QnKSArIHlsYWIoJ01lYW4gUmVzcG9uc2UgVGltZSAoc2VjcyknKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gIGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgojIyMjIDEuMS40IFRoZSBkaXN0cmlidXRpb24gb2YgcXVlcmllcyBwZXIgSFRUUCBtZXRob2Qgb2YgcmVxdWVzdAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdzYW1wbGVDaGFyX0hUVFBtZXRob2QuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSRIVFRQLm1ldGhvZCA8LSBmYWN0b3IocEZyYW1lJEhUVFAubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSB1bmlxdWUocEZyYW1lJEhUVFAubWV0aG9kKSkKZ2dwbG90KGRhdGEgPSBwRnJhbWUsIAogICAgICAgYWVzKHggPSBIVFRQLm1ldGhvZCwgCiAgICAgICAgICAgeSA9IE51bS5RdWVyaWVzLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlMChQZXJjZW50LCAiJSIpKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC4xNSwgY29sb3IgPSAnZGFya2JsdWUnLCBmaWxsID0gJ2JsdWUnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsgCiAgdGhlbWVfYncoKQpgYGAKCiMjIyMgMS4xLjUgVGhlIGRpc3RyaWJ1dGlvbiBvZiBxdWVyaWVzIHBlciBzZXJ2ZXIgcmVzcG9uc2UgY29kZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdzYW1wbGVDaGFyX1N0YXR1c0NvZGUuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSRTdGF0dXM8LSBhcy5jaGFyYWN0ZXIocEZyYW1lJFN0YXR1cykKZ2dwbG90KGRhdGEgPSBwRnJhbWUsIAogICAgICAgYWVzKHggPSBTdGF0dXMsIAogICAgICAgICAgIHkgPSBOdW0uUXVlcmllcywgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoUGVyY2VudCwgIiUiKSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuMTUsIGNvbG9yID0gJ2RhcmtvcmFuZ2UnLCBmaWxsID0gJ29yYW5nZScsIHN0YXQgPSAnaWRlbnRpdHknKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMykgKyAKICB0aGVtZV9idygpCmBgYAoKIyMjIyAxLjEuNiBUaGUgZGlzdHJpYnV0aW9uIG9mIHF1ZXJpZXMgcGVyIG91dHB1dCBmb3JtYXQKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgbWVzc2FnZSA9IEZ9CnBGcmFtZSA8LSByZWFkLmNzdignc2FtcGxlX0Zvcm1hdC5jc3YnLCAKICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIAogICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwgCiAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcEZyYW1lJEZvcm1hdDwtIGZhY3RvcihwRnJhbWUkRm9ybWF0LCAKICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSB1bmlxdWUocEZyYW1lJEZvcm1hdCkpCmdncGxvdChkYXRhID0gcEZyYW1lLCAKICAgICAgIGFlcyh4ID0gRm9ybWF0LCAKICAgICAgICAgICB5ID0gTnVtLlF1ZXJpZXMsIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUwKFBlcmNlbnQsICIlIikpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrdmlvbGV0JywgZmlsbCA9ICd2aW9sZXQnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArIAogIHRoZW1lX2J3KCkKYGBgCgojIyMgMS4yIFdEUVMgcHJvY2Vzc2luZyB0aW1lcwoKIyMjIyAxLjIuMSBEYXkgb2Ygd2VlayB2cy4gV0RRUyByZXNwb25zZSB0aW1lCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIG1lc3NhZ2UgPSBGfQpwRnJhbWUgPC0gcmVhZC5jc3YoJ2Nyb3NzVGFic19kYXkuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSRkc19kYXkgPC0gZmFjdG9yKHBGcmFtZSRkc19kYXksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJ01vbmRheScsICdUdWVzZGF5JywgJ1dlZG5lc2RheScsICdUaHVyc2RheScsICdGcmlkYXknLCAnU2F0dXJkYXknLCAnU3VuZGF5JykpCmdncGxvdChkYXRhID0gcEZyYW1lLCAKICAgICAgIGFlcyh4ID0gZHNfZGF5LCAKICAgICAgICAgICB5ID0gbWVhbl9xdWVyeV90aW1lLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlMCgiTWVkOiAiLCBtZWRpYW5fcXVlcnlfdGltZSkpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrcmVkJywgZmlsbCA9ICdyZWQnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArIAogIHhsYWIoJ0RheScpICsgeWxhYignTWVhbiBRdWVyeSBUaW1lIChzZWNzKScpICsKICB0aGVtZV9idygpCmBgYAoKIyMjIyAxLjIuMiBIb3VyIG9mIGRheSB2cy4gV0RRUyByZXNwb25zZSB0aW1lCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIG1lc3NhZ2UgPSBGfQpwRnJhbWUgPC0gcmVhZC5jc3YoJ2Nyb3NzVGFic19ob3VyLmNzdicsIAogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpwRnJhbWUkZHNfaG91ciA8LSBmYWN0b3IocEZyYW1lJGRzX2hvdXIsCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGFzLmNoYXJhY3RlcigwOjIzKSkKZ2dwbG90KGRhdGEgPSBwRnJhbWUsIAogICAgICAgYWVzKHggPSBkc19ob3VyLCAKICAgICAgICAgICB5ID0gbWVhbl9xdWVyeV90aW1lLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlMCgiTWVkOiAiLCBtZWRpYW5fcXVlcnlfdGltZSkpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrcmVkJywgZmlsbCA9ICdyZWQnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArIAogIHhsYWIoJ0hvdXInKSArIHlsYWIoJ01lYW4gUXVlcnkgVGltZSAoc2VjcyknKSArCiAgdGhlbWVfYncoKQpgYGAKCiMjIyMgMS4yLjMgV01GIERhdGFjZW50ZXIvSG9zdCB2cy4gV0RRUyByZXNwb25zZSB0aW1lCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIG1lc3NhZ2UgPSBGfQpwRnJhbWUgPC0gcmVhZC5jc3YoJ2Nyb3NzVGFic19kYXRhY2VudGVyX2hvc3QuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSRkc19kYXRhY2VudGVyX2hvc3QgPC0gZmFjdG9yKHBGcmFtZSRkc19kYXRhY2VudGVyX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHVuaXF1ZShwRnJhbWUkZHNfZGF0YWNlbnRlcl9ob3N0KSkKZ2dwbG90KGRhdGEgPSBwRnJhbWUsIAogICAgICAgYWVzKHggPSBkc19kYXRhY2VudGVyX2hvc3QsIAogICAgICAgICAgIHkgPSBtZWFuX3F1ZXJ5X3RpbWUsIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUwKCJNZWQ6ICIsIG1lZGlhbl9xdWVyeV90aW1lKSkpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuMTUsIGNvbG9yID0gJ2RhcmtncmVlbicsIGZpbGwgPSAnZ3JlZW4nLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArIAogIHhsYWIoJ1dNRi9EYXRhY2VudGVyL0hvc3QnKSArIHlsYWIoJ01lYW4gUXVlcnkgVGltZSAoc2VjcyknKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gIGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgojIyMjIDEuMi40IEhUVFAgbWV0aG9kIG9mIHJlcXVlc3QgdnMuIFdEUVMgcmVzcG9uc2UgdGltZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdjcm9zc1RhYnNfbWV0aG9kLmNzdicsIAogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpwRnJhbWUkZHNfbWV0aG9kIDwtIGZhY3RvcihwRnJhbWUkZHNfbWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSB1bmlxdWUocEZyYW1lJGRzX21ldGhvZCkpCmdncGxvdChkYXRhID0gcEZyYW1lLCAKICAgICAgIGFlcyh4ID0gZHNfbWV0aG9kLCAKICAgICAgICAgICB5ID0gbWVhbl9xdWVyeV90aW1lLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlMCgiTWVkOiAiLCBtZWRpYW5fcXVlcnlfdGltZSkpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrYmx1ZScsIGZpbGwgPSAnYmx1ZScsIHN0YXQgPSAnaWRlbnRpdHknKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsgCiAgeGxhYignSFRUUCBtZXRob2Qgb2YgcmVxdWVzdCcpICsgeWxhYignTWVhbiBRdWVyeSBUaW1lIChzZWNzKScpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCiMjIyMgMS4yLjUgU2VydmVyIHJlc3BvbnNlIGNvZGUgdnMuIFdEUVMgcmVzcG9uc2UgdGltZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCBtZXNzYWdlID0gRn0KcEZyYW1lIDwtIHJlYWQuY3N2KCdjcm9zc1RhYnNfc3RhdHVzX2NvZGUuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSRkc19zdGF0dXNfY29kZSA8LSBmYWN0b3IocEZyYW1lJGRzX3N0YXR1c19jb2RlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHVuaXF1ZShwRnJhbWUkZHNfc3RhdHVzX2NvZGUpKQpnZ3Bsb3QoZGF0YSA9IHBGcmFtZSwgCiAgICAgICBhZXMoeCA9IGRzX3N0YXR1c19jb2RlLCAKICAgICAgICAgICB5ID0gbWVhbl9xdWVyeV90aW1lLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlMCgiTWVkOiAiLCBtZWRpYW5fcXVlcnlfdGltZSkpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjE1LCBjb2xvciA9ICdkYXJrb3JhbmdlJywgZmlsbCA9ICdvcmFuZ2UnLCBzdGF0ID0gJ2lkZW50aXR5JykgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArIAogIHhsYWIoJ1N0YXR1cyBDb2RlJykgKyB5bGFiKCdNZWFuIFF1ZXJ5IFRpbWUgKHNlY3MpJykgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9ICBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMjIyAxLjIuNiBPdXRwdXQgZm9ybWF0IHZzLiBXRFFTIHJlc3BvbnNlIHRpbWUKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgbWVzc2FnZSA9IEZ9CnBGcmFtZSA8LSByZWFkLmNzdignY3Jvc3NUYWJzX2Zvcm1hdC5jc3YnLCAKICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIAogICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwgCiAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcEZyYW1lJGRzX2Zvcm1hdCA8LSBmYWN0b3IocEZyYW1lJGRzX2Zvcm1hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gdW5pcXVlKHBGcmFtZSRkc19mb3JtYXQpKQpnZ3Bsb3QoZGF0YSA9IHBGcmFtZSwgCiAgICAgICBhZXMoeCA9IGRzX2Zvcm1hdCwgCiAgICAgICAgICAgeSA9IG1lYW5fcXVlcnlfdGltZSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoIk1lZDogIiwgbWVkaWFuX3F1ZXJ5X3RpbWUpKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC4xNSwgY29sb3IgPSAnZGFya3Zpb2xldCcsIGZpbGwgPSAndmlvbGV0Jywgc3RhdCA9ICdpZGVudGl0eScpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMykgKyAKICB4bGFiKCdTdGF0dXMgQ29kZScpICsgeWxhYignTWVhbiBRdWVyeSBUaW1lIChzZWNzKScpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCiMjIyAxLjMgVGhlIGRpc3RyaWJ1dGlvbiBvZiBXRFFTIHByb2Nlc3NpbmcgdGltZXMKCiMjIyMgMS4zLjEgVGhlIGRpc3RyaWJ1dGlvbiBvZiBXRFFTIHByb2Nlc3NpbmcgdGltZXMgYWNyb3NzIFdNRiBEYXRhY2VudGVyL0hvc3RzCgoqKk5PVEUuKiogUGxlYXNlIG1pbmQgdGhhdCB3ZSB1c2UgYSBsb2dhcml0aG1pYyBzY2FsZSBmb3IgdGhlIFdEUVMgcXVlcnkgcHJvY2Vzc2luZyB0aW1lcy4gVGhlIGRpc3RyaWJ1dGlvbnMgYXJlICpoaWdobHkgc2tld2VkKiBvbiB0aGVpciBuYXR1cmFsIHNjYWxlcy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgbWVzc2FnZSA9IEZ9CnBGcmFtZSA8LSByZWFkLmNzdigncXREaXN0cmlidXRpb25fRGF0YWNlbnRlckhvc3QuY3N2JywgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCAKICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIAogICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmdncGxvdChkYXRhID0gcEZyYW1lLCAKICAgICAgIGFlcyh4ID0gbG9nKGRzX3F1ZXJ5X3RpbWUpKSkgKwogIGdlb21fZGVuc2l0eShzaXplID0gLjI1LCBjb2xvciA9ICJkYXJrZ3JlZW4iLCBmaWxsPSJncmVlbiIsIGFscGhhID0gLjIpICsKICBmYWNldF93cmFwKH5kc19kYXRhY2VudGVyX2hvc3QpICsKICB4bGFiKCdsb2coV0RRUyBwcm9jZXNzaW5nIHRpbWUpJykgKyB5bGFiKCdEZW5zaXR5JykgKyAKICB5bGltKGMoMCwgMSkpICsgCiAgdGhlbWVfYncoKSsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCiMjIyMgMS4zLjIgVGhlIGRpc3RyaWJ1dGlvbiBvZiBXRFFTIHByb2Nlc3NpbmcgdGltZXMgYWNyb3NzIG91dHB1dCBmb3JtYXRzCgoqKk5PVEUuKiogVGhlIGB0ZXh0L3BsYWluYCBvdXRwdXQgZm9ybWF0IGhhcyB0d28gZmV3IG9ic2VydmF0aW9ucy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgbWVzc2FnZSA9IEZ9CnBGcmFtZSA8LSByZWFkLmNzdigncXREaXN0cmlidXRpb25fRm9ybWF0LmNzdicsIAogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnZ3Bsb3QoZGF0YSA9IHBGcmFtZSwgCiAgICAgICBhZXMoeCA9IGxvZyhkc19xdWVyeV90aW1lKSkpICsKICBnZW9tX2RlbnNpdHkoc2l6ZSA9IC4yNSwgY29sb3IgPSAiZGFya3Zpb2xldCIsIGZpbGw9InZpb2xldCIsIGFscGhhID0gLjIpICsKICBmYWNldF93cmFwKH5kc19mb3JtYXQpICsKICB4bGFiKCdsb2coV0RRUyBwcm9jZXNzaW5nIHRpbWUpJykgKyB5bGFiKCdEZW5zaXR5JykgKyAKICB5bGltKGMoMCwgMSkpICsgCiAgdGhlbWVfYncoKSsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCioqKgpHb3JhbiBTLiBNaWxvdmFub3ZpxIcKCldpa2ltZWRpYSBEZXV0c2NobGFuZCwgRGF0YSBTY2llbnRpc3QKRGF0YUtvbGVrdGl2LCBPd25lcgoKMjAyMC4KCmNvbnRhY3Q6IGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGUKCiFbXShfaW1nL0RLX0xvZ29fMTAwLnBuZykKIVtdKF9pbWcvV2lraWRhdGEtbG9nby1lbi5wbmcpCiFbXShfaW1nL1dpa2ltZWRpYV9EZXV0c2NobGFuZF9Mb2dvX3NtYWxsLnBuZykKCgoKCgo=