So far, we have analyzed a sample data set from LinkedIn and Twitter to understand the trends in ‘Data Science’. Now, let’s take a large dataset containing all the jobs related to ‘Data Science’ to validate our hypothesis. For this purpose, we need to select a company which is one of the largest hiring company for data science related roles and also is a reputable company that has positive reviews in the job market.

One of the largest job portals company, glassdoor.com shows ‘Amazon’ as the best candidate for this purpose.

The search on Amazon.jobs for keyword “Data Science” in the location “United States” revealed 6630 results. This data is scraped from the website using python query and using a proxy website ‘Crawlera’.

Data Extraction

from time import time
from time import sleep
from datetime import datetime
from requests import get
from random import randint
from random import choice
from IPython.core.display import clear_output
from warnings import warn
import json
import csv

''' Amazon Job Search URL:
https://www.amazon.jobs/en/search?offset=0&result_limit=10&sort=relevant&job_type=Full-Time&
cities[]=Seattle%2C%20Washington%2C%20USA&cities[]=San%20Francisco%2C%20California%2C%20USA&
cities[]=Sunnyvale%2C%20California%2C%20USA&cities[]=Bellevue%2C%20Washington%2C%20USA&
cities[]=East%20Palo%20Alto%2C%20California%2C%20USA&cities[]=Santa%20Monica%2C%20California%2C%20USA&
category_type=Corporate&distanceType=Mi&radius=24km&latitude=&longitude=&loc_group_id=&loc_query=USA&base_query=&
city=&country=USA&region=&county=&query_options=& 
'''


def get_all_jobs(pages):
    requests = 0
    start_time = time()
    total_runtime = datetime.now()
    user_agent_list = [
        # Chrome
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
        'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36'
        'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36'
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        'Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
        'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
        'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'

        # Firefox
        'Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1'
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0'
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0'
        'Mozilla/5.0 (X11; U; Linux Core i7-4980HQ; de; rv:32.0; compatible; JobboerseBot; http://www.jobboerse.com/bot.htm) Gecko/20100101 Firefox/38.0'
        'Mozilla/5.0 (Windows NT 5.1; rv:36.0) Gecko/20100101 Firefox/36.0'
        'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0'
        'Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0'
        'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0'
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0'
    ]

    for page in pages:
        try:
            user_agent = choice(user_agent_list)
            headers = {'User-Agent': user_agent}

            response = get('https://www.amazon.jobs/en/search.json?base_query=&city=&country=USA&county=&'
                           'facets%5B%5D=location&facets%5B%5D=business_category&facets%5B%5D=category&'
                           'facets%5B%5D=schedule_type_id&facets%5B%5D=employee_class&facets%5B%5D=normalized_location'
                           '&facets%5B%5D=job_function_id&job_function_id%5B%5D=job_function_corporate_80rdb4&'
                           'latitude=&loc_group_id=&loc_query=USA&longitude=&'
                           'normalized_location%5B%5D=Seattle%2C+Washington%2C+USA&'
                           'normalized_location%5B%5D=San+Francisco'
                           '%2C+California%2C+USA&normalized_location%5B%5D=Sunnyvale%2C+California%2C+USA&'
                           'normalized_location%5B%5D=Bellevue%2C+Washington%2C+USA&'
                           'normalized_location%5B%5D=East+Palo+Alto%2C+California%2C+USA&'
                           'normalized_location%5B%5D=Santa+Monica%2C+California%2C+USA&offset={}&query_options=&'
                           'radius=24km&region=&result_limit=10&schedule_type_id%5B%5D=Full-Time&'
                           'sort=relevant'.format(page),
                           headers=headers,
                           # You will need your own Crawlera account and place below.
                           proxies={
                               "http": "http://ef31668955f04d569bde95a6b0d3dadb:@proxy.crawlera.com:8010/"
                           }
                           )
            # Monitor the frequency of requests
            requests += 1

            # Pauses the loop between 8 - 15 seconds and marks the elapsed time
            sleep(randint(8, 15))
            current_time = time()
            elapsed_time = current_time - start_time
            print("Amazon Request:{}; Frequency: {} request/s; Total Run Time: {}".format(requests,
                  requests / elapsed_time, datetime.now() - total_runtime))
            clear_output(wait=True)

            # Throw a warning for non-200 status codes
            if response.status_code != 200:
                warn("Request: {}; Status code: {}".format(requests, response.status_code))

            # Set page requests. Break the loop if number of requests is greater than expected
            if requests > 999:
                warn("Number of requests was greater than expected.")
                break
            yield from get_job_infos(response)

        except AttributeError as e:
            print(e)
            continue


def get_job_infos(response):
    amazon_jobs = json.loads(response.text)
    for website in amazon_jobs['jobs']:
        site = website['company_name']
        title = website['title']
        location = website['normalized_location']
        job_link = 'https://www.amazon.jobs' + website['job_path']
        basic_qualifications= website['basic_qualifications']
        business_category= website['business_category']
        description= website['description']
        description_short= website['description_short']
        job_category= website['job_category']
        job_family= website['job_family']
        posted_date= website['posted_date']
        preferred_qualifications= website['preferred_qualifications']

        yield site, title, location, job_link, basic_qualifications,business_category,description,description_short,job_category,job_family,posted_date,preferred_qualifications



def main():
    # Page range starts from 0 and the middle value increases by 10 each page.
    pages = [str(i) for i in range(0, 6630, 10)]

    # Writes to csv file
    with open('amazon_jobs.csv', "w", newline='', encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["Website", "Title", "Location", "Job URL","Basic Qualifications","Business Category","Description","Description_short","Job category","Job family","Posted date","Preferred Qualifications"])
        writer.writerows(get_all_jobs(pages))


if __name__ == "__main__":
    main()

Data Load

The dataset is then loaded to AWS db ensuring that no passwords are stored in the process. All the datasets leveraged in the project were loaded using the same approach.

#prompt for input 

password_file<-"C:\\password-files-for-r\\AWS_login.csv"

passwords<-read.csv(password_file)
# read in login credentials
df_login <- passwords   # read in login credentials

vardb_user <- df_login$login_name
vardb_password <- df_login$login_password
vardb_schema <- df_login$login_schema
vardb_host <- df_login$login_host

#cat("Host: ", vardb_host, " Schema=", vardb_schema, " username=", vardb_user, " password=", vardb_password)


mydb = dbConnect(RMySQL::MySQL(), user=vardb_user,  password=vardb_password, port=3306, dbname=vardb_schema, host=vardb_host)


#load amazon.csv into R

setwd("LOCATION OF .CSV ON YOUR COMPUTER")
amazon_table<-read_csv("amazon_table.csv")

# correct file encoding for upload to MySQL

write.table(amazon_table,file="tmp.txt", fileEncoding ="utf8")
mydata_utf8 <- read.table(file="tmp.txt",encoding="utf8") 


#upload table to MySQL

written_df<-dbWriteTable(mydb,"DESIRED TABLE NAME",
                         mydata_utf8,append=TRUE,row.names=FALSE)

Tidying data for Analysis

Load required libraries such as Tidyverse, Tidytext, Quanteda and Corpus
library(tidyverse)
library(tidytext)
library(quanteda)
library(corpus)
library(scales)
knitr::opts_chunk$set(eval = TRUE, results = FALSE)
amzn_jobs_raw <- read.csv("amazon_jobs.csv")
#amzn_jobs_raw<-dbReadTable(mydb,"amazon_table")

Select only the required columns to perform the analysis. Both ‘Basic Qualifications’ and ‘Preferred Qualifications’ appear to be key columns(apart from job category), let’s select them both and subset the data to the year 2020 alone.

Data reveals that ‘Software Development’ department offers the most data science jobs at Amazon! Let’s look into the table a bit. In total, the top three job categories such as Software Development, Product Management (technical+non-technical) and Finance & Accounting contribute to 63% of the data science jobs.

amzn_jobs_latest <- amzn_jobs_raw %>% select(Job.URL,Basic.Qualifications,Job.category,Posted.date,Preferred.Qualifications)%>%
  filter(str_sub(amzn_jobs_raw$Posted.date,-4,-1) == "2020")

jobs_cnt <- amzn_jobs_latest %>% count(Job.category,sort = TRUE)
jobs_cnt = mutate(jobs_cnt, jobs_pct = round((n / sum(n))*100))

knitr:: kable(head(jobs_cnt,10)) 
Job.category n jobs_pct
Software Development 2177 36
Project/Program/Product Management–Technical 669 11
Project/Program/Product Management–Non-Tech 649 11
Finance & Accounting 274 5
Business Intelligence 233 4
Systems, Quality, & Security Engineering 224 4
Solutions Architect 208 3
Operations, IT, & Support Engineering 147 2
Sales, Advertising, & Account Management 141 2
Design 127 2
head(jobs_cnt,5) %>% arrange(jobs_pct) %>%  mutate(Job.category = fct_reorder(Job.category, jobs_pct)) %>%
ggplot( aes(y=Job.category, x=jobs_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(jobs_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  xlab("Percentage of Jobs")+ ylab("")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("'Data Science' Jobs by Job Category") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

On closer look, ‘Perferred Qualifications’ seen to have more information about the job than ‘Basic Qualifications’. And next, removing legal and PR content from the column to perform text analysis

amzn_jobs_latest$Preferred.Qualifications <- sub('Amazon is committed.*$', '', amzn_jobs_latest$Preferred.Qualifications)
amzn_jobs_latest$Preferred.Qualifications <- sub('race.*$', '', amzn_jobs_latest$Preferred.Qualifications)
amzn_jobs_latest$Preferred.Qualifications <- sub('equal opportunity.*$', '', amzn_jobs_latest$Preferred.Qualifications)

Exploratory data analysis

One Word Frequency count

ngram_list <- c('br', 'race', 'national origin', 'gender', 'identity', 'sexual','orientation', 'protected', 'veteran', 'status', 'disability', 'age', 'protected status','â', 'equal','opportunity','amazon','employer','https','en','legally','discriminate','workplace','national','amazonâ')

Pref_freq <- amzn_jobs_latest %>%
  unnest_tokens(word, `Preferred.Qualifications`) %>%
  anti_join(stop_words) %>%  
  filter(
    !str_detect(word, pattern = "[[:digit:]]"), # removes any words with numeric digits
    !str_detect(word, pattern = "[[:punct:]]"), # removes any remaining punctuations
    !str_detect(word, pattern = "(.)\\1{2,}"),  # removes any words with 3 or more repeated letters
    !str_detect(word, pattern = "\\b(.)\\b"),    # removes any remaining single letter words
    !word %in% ngram_list                 # remove stopwords from both words in bi-gram
    ) %>%
  count(word) %>%
  filter(n >= 1000) %>% # filter for words used 1000 or more times
  spread(word, n) %>%                 # convert to wide format
  map_df(replace_na, 0)                 # replace NAs with 0

Pref_freq_tall <-  as.data.frame(t(Pref_freq))
Pref_freq_tall  <- tibble::rownames_to_column(Pref_freq_tall, "Keywords")
Pref_freq_tall <- Pref_freq_tall[order(-Pref_freq_tall$V1),]
Pref_freq_tall = mutate(Pref_freq_tall, one_pct = round((V1 / sum(V1))*100))
#knitr:: kable(head(Pref_freq_tall,10)) 
head(Pref_freq_tall,10) %>% arrange(one_pct) %>%  mutate(Keywords = fct_reorder(Keywords, one_pct)) %>%
ggplot(aes( x=Keywords,y=one_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(one_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
  coord_flip()+
    xlab("")+ ylab("Percentage of Frequency")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("Percentage of One Word Frequencies") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

Two Words Frequency count

Pref_freq_bi <- amzn_jobs_latest %>%
  unnest_tokens(bigram, `Preferred.Qualifications`,token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>%  
  filter(
    !word1 %in% stop_words$word,                 # remove stopwords from both words in bi-gram
    !word2 %in% stop_words$word,
    !str_detect(word1, pattern = "[[:digit:]]"), # removes any words with numeric digits
    !str_detect(word2, pattern = "[[:digit:]]"),
    !str_detect(word1, pattern = "[[:punct:]]"), # removes any remaining punctuations
    !str_detect(word2, pattern = "[[:punct:]]"),
    !str_detect(word1, pattern = "(.)\\1{2,}"),  # removes any words with 3 or more repeated letters
    !str_detect(word2, pattern = "(.)\\1{2,}"),
    !str_detect(word1, pattern = "\\b(.)\\b"),   # removes any remaining single letter words
    !str_detect(word2, pattern = "\\b(.)\\b"),
    !word1 %in% ngram_list,                 # remove stopwords from both words in bi-gram
    !word2 %in% ngram_list
    ) %>%
  unite("bigram", c(word1, word2), sep = " ") %>%
  count(bigram) %>%
  filter(n >= 800) %>% # filter for bi-grams used 1000 or more times
  spread(bigram, n) %>%                 # convert to wide format
  map_df(replace_na, 0)                 # replace NAs with 0

Pref_freq_bi_tall <- as.data.frame(t(Pref_freq_bi))
Pref_freq_bi_tall  <- tibble::rownames_to_column(Pref_freq_bi_tall, "Keywords")
Pref_freq_bi_tall <- Pref_freq_bi_tall[order(-Pref_freq_bi_tall$V1),]
#knitr:: kable(head(Pref_freq_bi_tall,10))
Pref_freq_bi_tall = mutate(Pref_freq_bi_tall, one_pct = round((V1 / sum(V1))*100))


head(Pref_freq_bi_tall,10) %>% arrange(one_pct) %>%  mutate(Keywords = fct_reorder(Keywords, one_pct)) %>%
ggplot(aes( x=Keywords,y=one_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(one_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
  coord_flip()+
    xlab("")+ ylab("Percentage of Frequency")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("Percentage of Two Words Frequencies") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

Three Words Frequency count

Pref_freq_tri <- amzn_jobs_latest %>%
  unnest_tokens(trigram, `Preferred.Qualifications`,token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2","word3"), sep = " ") %>%  
  filter(
    !word1 %in% stop_words$word,                 # remove stopwords from both words in bi-gram
    !word2 %in% stop_words$word,
    !str_detect(word1, pattern = "[[:digit:]]"), # removes any words with numeric digits
    !str_detect(word2, pattern = "[[:digit:]]"),
    !str_detect(word3, pattern = "[[:digit:]]"),    
    !str_detect(word1, pattern = "[[:punct:]]"), # removes any remaining punctuations
    !str_detect(word2, pattern = "[[:punct:]]"),
    !str_detect(word3, pattern = "[[:punct:]]"),    
    !str_detect(word1, pattern = "(.)\\1{2,}"),  # removes any words with 3 or more repeated letters
    !str_detect(word2, pattern = "(.)\\1{2,}"),
    !str_detect(word3, pattern = "(.)\\1{2,}"),    
    !str_detect(word1, pattern = "\\b(.)\\b"),   # removes any remaining single letter words
    !str_detect(word2, pattern = "\\b(.)\\b"),
    !str_detect(word3, pattern = "\\b(.)\\b"),    
    !word1 %in% ngram_list,                 # remove stopwords from both words in bi-gram
    !word2 %in% ngram_list,
    !word3 %in% ngram_list
    ) %>%
  unite("trigram", c(word1, word2, word3), sep = " ") %>%
  count(trigram) %>%
  filter(n >= 500) %>% # filter for bi-grams used 500 or more times
  spread(trigram, n) %>%                 # convert to wide format
  map_df(replace_na, 0)                 # replace NAs with 0

Pref_freq_tri_tall <- as.data.frame(t(Pref_freq_tri))
Pref_freq_tri_tall  <- tibble::rownames_to_column(Pref_freq_tri_tall, "Keywords")
Pref_freq_tri_tall <- Pref_freq_tri_tall[order(-Pref_freq_tri_tall$V1),]
#knitr:: kable(head(Pref_freq_tri_tall,10))

Pref_freq_tri_tall = mutate(Pref_freq_tri_tall, one_pct = round((V1 / sum(V1))*100))


head(Pref_freq_tri_tall,10) %>% arrange(one_pct) %>%  mutate(Keywords = fct_reorder(Keywords, one_pct)) %>%
ggplot(aes( x=Keywords,y=one_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(one_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
  coord_flip()+
    xlab("")+ ylab("Percentage of Frequency")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("Percentage of Three Words Frequencies") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

The most important Soft skills are:

data_dict<-dfm(amzn_jobs_latest$Preferred.Qualifications, dictionary=soft_skills)
top_soft <- as.data.frame(topfeatures(data_dict))
top_soft <- tibble::rownames_to_column(top_soft, "Keywords")
colnames(top_soft) <- c("Keywords","freq")
top_soft = mutate(top_soft, skills_pct = round((freq / sum(freq))*100))

top_soft %>% arrange(skills_pct) %>%  mutate(Keywords = fct_reorder(Keywords, skills_pct)) %>%
ggplot(aes( x=Keywords,y=skills_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(skills_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
  coord_flip()+
    xlab("")+ ylab("")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("Top Soft Skills for 'Data Science' at Amazon") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

The most important Technical skills are:

data_dict2<-dfm(amzn_jobs_latest$Preferred.Qualifications, dictionary=technical_skills)
top_tech <- as.data.frame(topfeatures(data_dict2))
top_tech <- tibble::rownames_to_column(top_tech, "Keywords")
colnames(top_tech) <- c("Keywords","freq")
top_tech = mutate(top_tech, skills_pct = round((freq / sum(freq))*100))

top_tech %>% arrange(skills_pct) %>%  mutate(Keywords = fct_reorder(Keywords, skills_pct)) %>%
ggplot(aes( x=Keywords,y=skills_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(skills_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
  coord_flip()+
    xlab("")+ ylab("")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("Top Technical Skills for 'Data Science' at Amazon") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

Conclusion

Now, let’s combine both soft skills and technical skills and find the top Data Science skills at Amazon. We find that 8 out of top 10 skills required at Amazon are Soft Skills. So, when you prepare for an interview at Amazon next time, be sure to focus on highlighting your “Soft Skills”

data_dict4<-dfm(amzn_jobs_latest$Preferred.Qualifications, dictionary=all_skills)
top_skill <- as.data.frame(topfeatures(data_dict4))
top_skill <- tibble::rownames_to_column(top_skill, "Keywords")
colnames(top_skill) <- c("Keywords","freq")
top_skill = mutate(top_skill, skills_pct = round((freq / sum(freq))*100))

top_skill %>% arrange(skills_pct) %>%  mutate(Keywords = fct_reorder(Keywords, skills_pct)) %>%
ggplot(aes( x=Keywords,y=skills_pct)) + 
    geom_bar( stat="identity",color="black")+
    geom_text(aes(label=paste0(skills_pct,"%")),color = "orange",hjust=1,vjust=0.3)+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
  coord_flip()+
    xlab("")+ ylab("")+
  theme( axis.line = element_line(colour = "black", 
                      size = 1, linetype = "solid"))+
   ggtitle("Top Technical Skills for 'Data Science' at Amazon") + 
     theme(plot.title = element_text(lineheight=.8, face="bold"))

LS0tDQp0aXRsZTogIkFtYXpvbiBKb2JzIEFuYWx5c2lzIg0Kb3V0cHV0Og0KICBvcGVuaW50cm86OmxhYl9yZXBvcnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgaW5jbHVkZXM6DQogICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sDQogICAgY3NzOiAuL2xhYi5jc3MNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICB3b3JkX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQplZGl0b3Jfb3B0aW9uczoNCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUNCi0tLQ0KDQpTbyBmYXIsIHdlIGhhdmUgYW5hbHl6ZWQgYSBzYW1wbGUgZGF0YSBzZXQgZnJvbSBMaW5rZWRJbiBhbmQgVHdpdHRlciB0byB1bmRlcnN0YW5kIHRoZSB0cmVuZHMgaW4gJ0RhdGEgU2NpZW5jZScuIE5vdywgbGV0J3MgdGFrZSBhIGxhcmdlIGRhdGFzZXQgY29udGFpbmluZyBhbGwgdGhlIGpvYnMgcmVsYXRlZCB0byAnRGF0YSBTY2llbmNlJyB0byB2YWxpZGF0ZSBvdXIgaHlwb3RoZXNpcy4gRm9yIHRoaXMgcHVycG9zZSwgd2UgbmVlZCB0byBzZWxlY3QgYSBjb21wYW55IHdoaWNoIGlzIG9uZSBvZiB0aGUgbGFyZ2VzdCBoaXJpbmcgY29tcGFueSBmb3IgZGF0YSBzY2llbmNlIHJlbGF0ZWQgcm9sZXMgYW5kIGFsc28gaXMgYSByZXB1dGFibGUgY29tcGFueSB0aGF0IGhhcyBwb3NpdGl2ZSByZXZpZXdzIGluIHRoZSBqb2IgbWFya2V0LiANCg0KT25lIG9mIHRoZSBsYXJnZXN0IGpvYiBwb3J0YWxzIGNvbXBhbnksIFtnbGFzc2Rvb3IuY29tXShodHRwczovL3d3dy5nbGFzc2Rvb3IuY29tL2Jsb2cvY29tcGFuaWVzLWhpcmluZy1kYXRhLXNjaWVudGlzdHMvKSBzaG93cyAqKidBbWF6b24nKiogYXMgdGhlIGJlc3QgY2FuZGlkYXRlIGZvciB0aGlzIHB1cnBvc2UuIA0KDQpgYGB7ciBBbWF6b24sIHJlc3VsdHM9ImFzaXMiLCBlY2hvID1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3JldGFpbC10b2RheS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMjAvMDkvQW1hem9uLmpwZyIpDQpgYGANCg0KVGhlIHNlYXJjaCBvbiBbQW1hem9uLmpvYnNdKGh0dHBzOi8vd3d3LmFtYXpvbi5qb2JzL2VuL3NlYXJjaD9iYXNlX3F1ZXJ5PURhdGErU2NpZW5jZSZsb2NfcXVlcnk9VW5pdGVkK1N0YXRlcyZ0eXBlPWFyZWEmbGF0aXR1ZGU9MzguODkwMzcmbG9uZ2l0dWRlPS03Ny4wMzE5NiZjb3VudHJ5PVVTQSkgZm9yIGtleXdvcmQgIkRhdGEgU2NpZW5jZSIgaW4gdGhlIGxvY2F0aW9uICJVbml0ZWQgU3RhdGVzIiByZXZlYWxlZCA2NjMwIHJlc3VsdHMuIFRoaXMgZGF0YSBpcyBzY3JhcGVkIGZyb20gdGhlIHdlYnNpdGUgdXNpbmcgcHl0aG9uIHF1ZXJ5IGFuZCB1c2luZyBhIHByb3h5IHdlYnNpdGUgJ0NyYXdsZXJhJy4gDQoNCiMjIyBEYXRhIEV4dHJhY3Rpb24NCmBgYHtyICBldmFsPSBGQUxTRX0NCmZyb20gdGltZSBpbXBvcnQgdGltZQ0KZnJvbSB0aW1lIGltcG9ydCBzbGVlcA0KZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUNCmZyb20gcmVxdWVzdHMgaW1wb3J0IGdldA0KZnJvbSByYW5kb20gaW1wb3J0IHJhbmRpbnQNCmZyb20gcmFuZG9tIGltcG9ydCBjaG9pY2UNCmZyb20gSVB5dGhvbi5jb3JlLmRpc3BsYXkgaW1wb3J0IGNsZWFyX291dHB1dA0KZnJvbSB3YXJuaW5ncyBpbXBvcnQgd2Fybg0KaW1wb3J0IGpzb24NCmltcG9ydCBjc3YNCg0KJycnIEFtYXpvbiBKb2IgU2VhcmNoIFVSTDoNCmh0dHBzOi8vd3d3LmFtYXpvbi5qb2JzL2VuL3NlYXJjaD9vZmZzZXQ9MCZyZXN1bHRfbGltaXQ9MTAmc29ydD1yZWxldmFudCZqb2JfdHlwZT1GdWxsLVRpbWUmDQpjaXRpZXNbXT1TZWF0dGxlJTJDJTIwV2FzaGluZ3RvbiUyQyUyMFVTQSZjaXRpZXNbXT1TYW4lMjBGcmFuY2lzY28lMkMlMjBDYWxpZm9ybmlhJTJDJTIwVVNBJg0KY2l0aWVzW109U3Vubnl2YWxlJTJDJTIwQ2FsaWZvcm5pYSUyQyUyMFVTQSZjaXRpZXNbXT1CZWxsZXZ1ZSUyQyUyMFdhc2hpbmd0b24lMkMlMjBVU0EmDQpjaXRpZXNbXT1FYXN0JTIwUGFsbyUyMEFsdG8lMkMlMjBDYWxpZm9ybmlhJTJDJTIwVVNBJmNpdGllc1tdPVNhbnRhJTIwTW9uaWNhJTJDJTIwQ2FsaWZvcm5pYSUyQyUyMFVTQSYNCmNhdGVnb3J5X3R5cGU9Q29ycG9yYXRlJmRpc3RhbmNlVHlwZT1NaSZyYWRpdXM9MjRrbSZsYXRpdHVkZT0mbG9uZ2l0dWRlPSZsb2NfZ3JvdXBfaWQ9JmxvY19xdWVyeT1VU0EmYmFzZV9xdWVyeT0mDQpjaXR5PSZjb3VudHJ5PVVTQSZyZWdpb249JmNvdW50eT0mcXVlcnlfb3B0aW9ucz0mIA0KJycnDQoNCg0KZGVmIGdldF9hbGxfam9icyhwYWdlcyk6DQogICAgcmVxdWVzdHMgPSAwDQogICAgc3RhcnRfdGltZSA9IHRpbWUoKQ0KICAgIHRvdGFsX3J1bnRpbWUgPSBkYXRldGltZS5ub3coKQ0KICAgIHVzZXJfYWdlbnRfbGlzdCA9IFsNCiAgICAgICAgIyBDaHJvbWUNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS82MC4wLjMxMTIuMTEzIFNhZmFyaS81MzcuMzYnDQogICAgICAgICdNb3ppbGxhLzUuMCAoV2luZG93cyBOVCA2LjE7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS82MC4wLjMxMTIuOTAgU2FmYXJpLzUzNy4zNicNCiAgICAgICAgJ01vemlsbGEvNS4wIChYMTE7IExpbnV4IHg4Nl82NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzQ0LjAuMjQwMy4xNTcgU2FmYXJpLzUzNy4zNicNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDUuMSkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzQ2LjAuMjQ5MC43MSBTYWZhcmkvNTM3LjM2Jw0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzY5LjAuMzQ5Ny4xMDAgU2FmYXJpLzUzNy4zNicNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS82My4wLjMyMzkuMTMyIFNhZmFyaS81MzcuMzYnDQogICAgICAgICdNb3ppbGxhLzUuMCAoV2luZG93cyBOVCA1LjE7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS82MC4wLjMxMTIuOTAgU2FmYXJpLzUzNy4zNicNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMjsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzYwLjAuMzExMi45MCBTYWZhcmkvNTM3LjM2Jw0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgNi4zOyBXaW42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvNjAuMC4zMTEyLjExMyBTYWZhcmkvNTM3LjM2Jw0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzY3LjAuMzM5Ni45OSBTYWZhcmkvNTM3LjM2Jw0KDQogICAgICAgICMgRmlyZWZveA0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgNS4xOyBydjo3LjAuMSkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC83LjAuMScNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IHJ2OjU0LjApIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvNTQuMCcNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IHJ2OjQwLjApIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvNDAuMScNCiAgICAgICAgJ01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IHJ2OjE4LjApIEdlY2tvLzIwMTAwMTAxIEZpcmVmb3gvMTguMCcNCiAgICAgICAgJ01vemlsbGEvNS4wIChYMTE7IFU7IExpbnV4IENvcmUgaTctNDk4MEhROyBkZTsgcnY6MzIuMDsgY29tcGF0aWJsZTsgSm9iYm9lcnNlQm90OyBodHRwOi8vd3d3LmpvYmJvZXJzZS5jb20vYm90Lmh0bSkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8zOC4wJw0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgNS4xOyBydjozNi4wKSBHZWNrby8yMDEwMDEwMSBGaXJlZm94LzM2LjAnDQogICAgICAgICdNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXT1c2NDsgcnY6NTAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC81MC4wJw0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgNS4xOyBydjozMy4wKSBHZWNrby8yMDEwMDEwMSBGaXJlZm94LzMzLjAnDQogICAgICAgICdNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXT1c2NDsgcnY6NTIuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC81Mi4wJw0KICAgICAgICAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgNi4xOyBXT1c2NDsgcnY6NTAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC81MC4wJw0KICAgIF0NCg0KICAgIGZvciBwYWdlIGluIHBhZ2VzOg0KICAgICAgICB0cnk6DQogICAgICAgICAgICB1c2VyX2FnZW50ID0gY2hvaWNlKHVzZXJfYWdlbnRfbGlzdCkNCiAgICAgICAgICAgIGhlYWRlcnMgPSB7J1VzZXItQWdlbnQnOiB1c2VyX2FnZW50fQ0KDQogICAgICAgICAgICByZXNwb25zZSA9IGdldCgnaHR0cHM6Ly93d3cuYW1hem9uLmpvYnMvZW4vc2VhcmNoLmpzb24/YmFzZV9xdWVyeT0mY2l0eT0mY291bnRyeT1VU0EmY291bnR5PSYnDQogICAgICAgICAgICAgICAgICAgICAgICAgICAnZmFjZXRzJTVCJTVEPWxvY2F0aW9uJmZhY2V0cyU1QiU1RD1idXNpbmVzc19jYXRlZ29yeSZmYWNldHMlNUIlNUQ9Y2F0ZWdvcnkmJw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2ZhY2V0cyU1QiU1RD1zY2hlZHVsZV90eXBlX2lkJmZhY2V0cyU1QiU1RD1lbXBsb3llZV9jbGFzcyZmYWNldHMlNUIlNUQ9bm9ybWFsaXplZF9sb2NhdGlvbicNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICcmZmFjZXRzJTVCJTVEPWpvYl9mdW5jdGlvbl9pZCZqb2JfZnVuY3Rpb25faWQlNUIlNUQ9am9iX2Z1bmN0aW9uX2NvcnBvcmF0ZV84MHJkYjQmJw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2xhdGl0dWRlPSZsb2NfZ3JvdXBfaWQ9JmxvY19xdWVyeT1VU0EmbG9uZ2l0dWRlPSYnDQogICAgICAgICAgICAgICAgICAgICAgICAgICAnbm9ybWFsaXplZF9sb2NhdGlvbiU1QiU1RD1TZWF0dGxlJTJDK1dhc2hpbmd0b24lMkMrVVNBJicNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdub3JtYWxpemVkX2xvY2F0aW9uJTVCJTVEPVNhbitGcmFuY2lzY28nDQogICAgICAgICAgICAgICAgICAgICAgICAgICAnJTJDK0NhbGlmb3JuaWElMkMrVVNBJm5vcm1hbGl6ZWRfbG9jYXRpb24lNUIlNUQ9U3Vubnl2YWxlJTJDK0NhbGlmb3JuaWElMkMrVVNBJicNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdub3JtYWxpemVkX2xvY2F0aW9uJTVCJTVEPUJlbGxldnVlJTJDK1dhc2hpbmd0b24lMkMrVVNBJicNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdub3JtYWxpemVkX2xvY2F0aW9uJTVCJTVEPUVhc3QrUGFsbytBbHRvJTJDK0NhbGlmb3JuaWElMkMrVVNBJicNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdub3JtYWxpemVkX2xvY2F0aW9uJTVCJTVEPVNhbnRhK01vbmljYSUyQytDYWxpZm9ybmlhJTJDK1VTQSZvZmZzZXQ9e30mcXVlcnlfb3B0aW9ucz0mJw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZGl1cz0yNGttJnJlZ2lvbj0mcmVzdWx0X2xpbWl0PTEwJnNjaGVkdWxlX3R5cGVfaWQlNUIlNUQ9RnVsbC1UaW1lJicNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdzb3J0PXJlbGV2YW50Jy5mb3JtYXQocGFnZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXJzPWhlYWRlcnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAjIFlvdSB3aWxsIG5lZWQgeW91ciBvd24gQ3Jhd2xlcmEgYWNjb3VudCBhbmQgcGxhY2UgYmVsb3cuDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwcm94aWVzPXsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaHR0cCI6ICJodHRwOi8vZWYzMTY2ODk1NWYwNGQ1NjliZGU5NWE2YjBkM2RhZGI6QHByb3h5LmNyYXdsZXJhLmNvbTo4MDEwLyINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgICMgTW9uaXRvciB0aGUgZnJlcXVlbmN5IG9mIHJlcXVlc3RzDQogICAgICAgICAgICByZXF1ZXN0cyArPSAxDQoNCiAgICAgICAgICAgICMgUGF1c2VzIHRoZSBsb29wIGJldHdlZW4gOCAtIDE1IHNlY29uZHMgYW5kIG1hcmtzIHRoZSBlbGFwc2VkIHRpbWUNCiAgICAgICAgICAgIHNsZWVwKHJhbmRpbnQoOCwgMTUpKQ0KICAgICAgICAgICAgY3VycmVudF90aW1lID0gdGltZSgpDQogICAgICAgICAgICBlbGFwc2VkX3RpbWUgPSBjdXJyZW50X3RpbWUgLSBzdGFydF90aW1lDQogICAgICAgICAgICBwcmludCgiQW1hem9uIFJlcXVlc3Q6e307IEZyZXF1ZW5jeToge30gcmVxdWVzdC9zOyBUb3RhbCBSdW4gVGltZToge30iLmZvcm1hdChyZXF1ZXN0cywNCiAgICAgICAgICAgICAgICAgIHJlcXVlc3RzIC8gZWxhcHNlZF90aW1lLCBkYXRldGltZS5ub3coKSAtIHRvdGFsX3J1bnRpbWUpKQ0KICAgICAgICAgICAgY2xlYXJfb3V0cHV0KHdhaXQ9VHJ1ZSkNCg0KICAgICAgICAgICAgIyBUaHJvdyBhIHdhcm5pbmcgZm9yIG5vbi0yMDAgc3RhdHVzIGNvZGVzDQogICAgICAgICAgICBpZiByZXNwb25zZS5zdGF0dXNfY29kZSAhPSAyMDA6DQogICAgICAgICAgICAgICAgd2FybigiUmVxdWVzdDoge307IFN0YXR1cyBjb2RlOiB7fSIuZm9ybWF0KHJlcXVlc3RzLCByZXNwb25zZS5zdGF0dXNfY29kZSkpDQoNCiAgICAgICAgICAgICMgU2V0IHBhZ2UgcmVxdWVzdHMuIEJyZWFrIHRoZSBsb29wIGlmIG51bWJlciBvZiByZXF1ZXN0cyBpcyBncmVhdGVyIHRoYW4gZXhwZWN0ZWQNCiAgICAgICAgICAgIGlmIHJlcXVlc3RzID4gOTk5Og0KICAgICAgICAgICAgICAgIHdhcm4oIk51bWJlciBvZiByZXF1ZXN0cyB3YXMgZ3JlYXRlciB0aGFuIGV4cGVjdGVkLiIpDQogICAgICAgICAgICAgICAgYnJlYWsNCiAgICAgICAgICAgIHlpZWxkIGZyb20gZ2V0X2pvYl9pbmZvcyhyZXNwb25zZSkNCg0KICAgICAgICBleGNlcHQgQXR0cmlidXRlRXJyb3IgYXMgZToNCiAgICAgICAgICAgIHByaW50KGUpDQogICAgICAgICAgICBjb250aW51ZQ0KDQoNCmRlZiBnZXRfam9iX2luZm9zKHJlc3BvbnNlKToNCiAgICBhbWF6b25fam9icyA9IGpzb24ubG9hZHMocmVzcG9uc2UudGV4dCkNCiAgICBmb3Igd2Vic2l0ZSBpbiBhbWF6b25fam9ic1snam9icyddOg0KICAgICAgICBzaXRlID0gd2Vic2l0ZVsnY29tcGFueV9uYW1lJ10NCiAgICAgICAgdGl0bGUgPSB3ZWJzaXRlWyd0aXRsZSddDQogICAgICAgIGxvY2F0aW9uID0gd2Vic2l0ZVsnbm9ybWFsaXplZF9sb2NhdGlvbiddDQogICAgICAgIGpvYl9saW5rID0gJ2h0dHBzOi8vd3d3LmFtYXpvbi5qb2JzJyArIHdlYnNpdGVbJ2pvYl9wYXRoJ10NCiAgICAgICAgYmFzaWNfcXVhbGlmaWNhdGlvbnM9IHdlYnNpdGVbJ2Jhc2ljX3F1YWxpZmljYXRpb25zJ10NCiAgICAgICAgYnVzaW5lc3NfY2F0ZWdvcnk9IHdlYnNpdGVbJ2J1c2luZXNzX2NhdGVnb3J5J10NCiAgICAgICAgZGVzY3JpcHRpb249IHdlYnNpdGVbJ2Rlc2NyaXB0aW9uJ10NCiAgICAgICAgZGVzY3JpcHRpb25fc2hvcnQ9IHdlYnNpdGVbJ2Rlc2NyaXB0aW9uX3Nob3J0J10NCiAgICAgICAgam9iX2NhdGVnb3J5PSB3ZWJzaXRlWydqb2JfY2F0ZWdvcnknXQ0KICAgICAgICBqb2JfZmFtaWx5PSB3ZWJzaXRlWydqb2JfZmFtaWx5J10NCiAgICAgICAgcG9zdGVkX2RhdGU9IHdlYnNpdGVbJ3Bvc3RlZF9kYXRlJ10NCiAgICAgICAgcHJlZmVycmVkX3F1YWxpZmljYXRpb25zPSB3ZWJzaXRlWydwcmVmZXJyZWRfcXVhbGlmaWNhdGlvbnMnXQ0KDQogICAgICAgIHlpZWxkIHNpdGUsIHRpdGxlLCBsb2NhdGlvbiwgam9iX2xpbmssIGJhc2ljX3F1YWxpZmljYXRpb25zLGJ1c2luZXNzX2NhdGVnb3J5LGRlc2NyaXB0aW9uLGRlc2NyaXB0aW9uX3Nob3J0LGpvYl9jYXRlZ29yeSxqb2JfZmFtaWx5LHBvc3RlZF9kYXRlLHByZWZlcnJlZF9xdWFsaWZpY2F0aW9ucw0KDQoNCg0KZGVmIG1haW4oKToNCiAgICAjIFBhZ2UgcmFuZ2Ugc3RhcnRzIGZyb20gMCBhbmQgdGhlIG1pZGRsZSB2YWx1ZSBpbmNyZWFzZXMgYnkgMTAgZWFjaCBwYWdlLg0KICAgIHBhZ2VzID0gW3N0cihpKSBmb3IgaSBpbiByYW5nZSgwLCA2NjMwLCAxMCldDQoNCiAgICAjIFdyaXRlcyB0byBjc3YgZmlsZQ0KICAgIHdpdGggb3BlbignYW1hem9uX2pvYnMuY3N2JywgInciLCBuZXdsaW5lPScnLCBlbmNvZGluZz0idXRmLTgiKSBhcyBmOg0KICAgICAgICB3cml0ZXIgPSBjc3Yud3JpdGVyKGYpDQogICAgICAgIHdyaXRlci53cml0ZXJvdyhbIldlYnNpdGUiLCAiVGl0bGUiLCAiTG9jYXRpb24iLCAiSm9iIFVSTCIsIkJhc2ljIFF1YWxpZmljYXRpb25zIiwiQnVzaW5lc3MgQ2F0ZWdvcnkiLCJEZXNjcmlwdGlvbiIsIkRlc2NyaXB0aW9uX3Nob3J0IiwiSm9iIGNhdGVnb3J5IiwiSm9iIGZhbWlseSIsIlBvc3RlZCBkYXRlIiwiUHJlZmVycmVkIFF1YWxpZmljYXRpb25zIl0pDQogICAgICAgIHdyaXRlci53cml0ZXJvd3MoZ2V0X2FsbF9qb2JzKHBhZ2VzKSkNCg0KDQppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOg0KICAgIG1haW4oKQ0KDQpgYGANCg0KDQojIyMgRGF0YSBMb2FkDQpUaGUgZGF0YXNldCBpcyB0aGVuIGxvYWRlZCB0byBBV1MgZGIgZW5zdXJpbmcgdGhhdCBubyBwYXNzd29yZHMgYXJlIHN0b3JlZCBpbiB0aGUgcHJvY2Vzcy4gQWxsIHRoZSBkYXRhc2V0cyBsZXZlcmFnZWQgaW4gdGhlIHByb2plY3Qgd2VyZSBsb2FkZWQgdXNpbmcgdGhlIHNhbWUgYXBwcm9hY2guIA0KDQpgYGB7ciBMT0FELCBldmFsPSBGQUxTRX0NCg0KDQojcHJvbXB0IGZvciBpbnB1dCANCg0KcGFzc3dvcmRfZmlsZTwtIkM6XFxwYXNzd29yZC1maWxlcy1mb3ItclxcQVdTX2xvZ2luLmNzdiINCg0KcGFzc3dvcmRzPC1yZWFkLmNzdihwYXNzd29yZF9maWxlKQ0KIyByZWFkIGluIGxvZ2luIGNyZWRlbnRpYWxzDQpkZl9sb2dpbiA8LSBwYXNzd29yZHMgICAjIHJlYWQgaW4gbG9naW4gY3JlZGVudGlhbHMNCg0KdmFyZGJfdXNlciA8LSBkZl9sb2dpbiRsb2dpbl9uYW1lDQp2YXJkYl9wYXNzd29yZCA8LSBkZl9sb2dpbiRsb2dpbl9wYXNzd29yZA0KdmFyZGJfc2NoZW1hIDwtIGRmX2xvZ2luJGxvZ2luX3NjaGVtYQ0KdmFyZGJfaG9zdCA8LSBkZl9sb2dpbiRsb2dpbl9ob3N0DQoNCiNjYXQoIkhvc3Q6ICIsIHZhcmRiX2hvc3QsICIgU2NoZW1hPSIsIHZhcmRiX3NjaGVtYSwgIiB1c2VybmFtZT0iLCB2YXJkYl91c2VyLCAiIHBhc3N3b3JkPSIsIHZhcmRiX3Bhc3N3b3JkKQ0KDQoNCm15ZGIgPSBkYkNvbm5lY3QoUk15U1FMOjpNeVNRTCgpLCB1c2VyPXZhcmRiX3VzZXIsICBwYXNzd29yZD12YXJkYl9wYXNzd29yZCwgcG9ydD0zMzA2LCBkYm5hbWU9dmFyZGJfc2NoZW1hLCBob3N0PXZhcmRiX2hvc3QpDQoNCg0KI2xvYWQgYW1hem9uLmNzdiBpbnRvIFINCg0Kc2V0d2QoIkxPQ0FUSU9OIE9GIC5DU1YgT04gWU9VUiBDT01QVVRFUiIpDQphbWF6b25fdGFibGU8LXJlYWRfY3N2KCJhbWF6b25fdGFibGUuY3N2IikNCg0KIyBjb3JyZWN0IGZpbGUgZW5jb2RpbmcgZm9yIHVwbG9hZCB0byBNeVNRTA0KDQp3cml0ZS50YWJsZShhbWF6b25fdGFibGUsZmlsZT0idG1wLnR4dCIsIGZpbGVFbmNvZGluZyA9InV0ZjgiKQ0KbXlkYXRhX3V0ZjggPC0gcmVhZC50YWJsZShmaWxlPSJ0bXAudHh0IixlbmNvZGluZz0idXRmOCIpIA0KDQoNCiN1cGxvYWQgdGFibGUgdG8gTXlTUUwNCg0Kd3JpdHRlbl9kZjwtZGJXcml0ZVRhYmxlKG15ZGIsIkRFU0lSRUQgVEFCTEUgTkFNRSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbXlkYXRhX3V0ZjgsYXBwZW5kPVRSVUUscm93Lm5hbWVzPUZBTFNFKQ0KDQpgYGANCg0KDQojIyMgVGlkeWluZyBkYXRhIGZvciBBbmFseXNpcw0KDQojIyMjIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcyBzdWNoIGFzIFRpZHl2ZXJzZSwgVGlkeXRleHQsIFF1YW50ZWRhIGFuZCBDb3JwdXMgDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KHF1YW50ZWRhKQ0KbGlicmFyeShjb3JwdXMpDQpsaWJyYXJ5KHNjYWxlcykNCmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gVFJVRSwgcmVzdWx0cyA9IEZBTFNFKQ0KYW16bl9qb2JzX3JhdyA8LSByZWFkLmNzdigiYW1hem9uX2pvYnMuY3N2IikNCiNhbXpuX2pvYnNfcmF3PC1kYlJlYWRUYWJsZShteWRiLCJhbWF6b25fdGFibGUiKQ0KDQpgYGANCg0KDQpTZWxlY3Qgb25seSB0aGUgcmVxdWlyZWQgY29sdW1ucyB0byBwZXJmb3JtIHRoZSBhbmFseXNpcy4gQm90aCAnQmFzaWMgUXVhbGlmaWNhdGlvbnMnIGFuZCAnUHJlZmVycmVkIFF1YWxpZmljYXRpb25zJyBhcHBlYXIgdG8gYmUga2V5IGNvbHVtbnMoYXBhcnQgZnJvbSBqb2IgY2F0ZWdvcnkpLCBsZXQncyBzZWxlY3QgdGhlbSBib3RoIGFuZCBzdWJzZXQgdGhlIGRhdGEgdG8gdGhlIHllYXIgKioyMDIwKiogYWxvbmUuIA0KDQoNCkRhdGEgcmV2ZWFscyB0aGF0ICoqJ1NvZnR3YXJlIERldmVsb3BtZW50JyoqIGRlcGFydG1lbnQgb2ZmZXJzIHRoZSBtb3N0IGRhdGEgc2NpZW5jZSBqb2JzIGF0IEFtYXpvbiEgTGV0J3MgbG9vayBpbnRvIHRoZSB0YWJsZSBhIGJpdC4gSW4gdG90YWwsIHRoZSB0b3AgdGhyZWUgam9iIGNhdGVnb3JpZXMgc3VjaCBhcyAqKlNvZnR3YXJlIERldmVsb3BtZW50KiosICoqUHJvZHVjdCBNYW5hZ2VtZW50ICh0ZWNobmljYWwrbm9uLXRlY2huaWNhbCkqKiBhbmQgKipGaW5hbmNlICYgQWNjb3VudGluZyoqIGNvbnRyaWJ1dGUgdG8gKio2MyUqKiBvZiB0aGUgZGF0YSBzY2llbmNlIGpvYnMuICAgDQoNCmBgYHtyIHN1YnNldCwgcmVzdWx0cyA9ICJhc2lzIn0NCg0KYW16bl9qb2JzX2xhdGVzdCA8LSBhbXpuX2pvYnNfcmF3ICU+JSBzZWxlY3QoSm9iLlVSTCxCYXNpYy5RdWFsaWZpY2F0aW9ucyxKb2IuY2F0ZWdvcnksUG9zdGVkLmRhdGUsUHJlZmVycmVkLlF1YWxpZmljYXRpb25zKSU+JQ0KICBmaWx0ZXIoc3RyX3N1YihhbXpuX2pvYnNfcmF3JFBvc3RlZC5kYXRlLC00LC0xKSA9PSAiMjAyMCIpDQoNCmpvYnNfY250IDwtIGFtem5fam9ic19sYXRlc3QgJT4lIGNvdW50KEpvYi5jYXRlZ29yeSxzb3J0ID0gVFJVRSkNCmpvYnNfY250ID0gbXV0YXRlKGpvYnNfY250LCBqb2JzX3BjdCA9IHJvdW5kKChuIC8gc3VtKG4pKSoxMDApKQ0KDQprbml0cjo6IGthYmxlKGhlYWQoam9ic19jbnQsMTApKSANCg0KaGVhZChqb2JzX2NudCw1KSAlPiUgYXJyYW5nZShqb2JzX3BjdCkgJT4lICBtdXRhdGUoSm9iLmNhdGVnb3J5ID0gZmN0X3Jlb3JkZXIoSm9iLmNhdGVnb3J5LCBqb2JzX3BjdCkpICU+JQ0KZ2dwbG90KCBhZXMoeT1Kb2IuY2F0ZWdvcnksIHg9am9ic19wY3QpKSArIA0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsY29sb3I9ImJsYWNrIikrDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoam9ic19wY3QsIiUiKSksY29sb3IgPSAib3JhbmdlIixoanVzdD0xLHZqdXN0PTAuMykrDQogIHhsYWIoIlBlcmNlbnRhZ2Ugb2YgSm9icyIpKyB5bGFiKCIiKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogICBnZ3RpdGxlKCInRGF0YSBTY2llbmNlJyBKb2JzIGJ5IEpvYiBDYXRlZ29yeSIpICsgDQogICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQobGluZWhlaWdodD0uOCwgZmFjZT0iYm9sZCIpKQ0KDQoNCmBgYA0KDQoNCk9uIGNsb3NlciBsb29rLCAnUGVyZmVycmVkIFF1YWxpZmljYXRpb25zJyBzZWVuIHRvIGhhdmUgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgam9iIHRoYW4gJ0Jhc2ljIFF1YWxpZmljYXRpb25zJy4gQW5kIG5leHQsIHJlbW92aW5nIGxlZ2FsIGFuZCBQUiBjb250ZW50IGZyb20gdGhlIGNvbHVtbiB0byBwZXJmb3JtIHRleHQgYW5hbHlzaXMNCmBgYHtyIHJlbW92ZV9sZWdhbCB9DQphbXpuX2pvYnNfbGF0ZXN0JFByZWZlcnJlZC5RdWFsaWZpY2F0aW9ucyA8LSBzdWIoJ0FtYXpvbiBpcyBjb21taXR0ZWQuKiQnLCAnJywgYW16bl9qb2JzX2xhdGVzdCRQcmVmZXJyZWQuUXVhbGlmaWNhdGlvbnMpDQphbXpuX2pvYnNfbGF0ZXN0JFByZWZlcnJlZC5RdWFsaWZpY2F0aW9ucyA8LSBzdWIoJ3JhY2UuKiQnLCAnJywgYW16bl9qb2JzX2xhdGVzdCRQcmVmZXJyZWQuUXVhbGlmaWNhdGlvbnMpDQphbXpuX2pvYnNfbGF0ZXN0JFByZWZlcnJlZC5RdWFsaWZpY2F0aW9ucyA8LSBzdWIoJ2VxdWFsIG9wcG9ydHVuaXR5LiokJywgJycsIGFtem5fam9ic19sYXRlc3QkUHJlZmVycmVkLlF1YWxpZmljYXRpb25zKQ0KYGBgDQoNCiMjIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzDQoNCiMjIyMgT25lIFdvcmQgRnJlcXVlbmN5IGNvdW50DQoNCmBgYHtyICxyZXN1bHRzPSAiYXNpcyIsd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KbmdyYW1fbGlzdCA8LSBjKCdicicsICdyYWNlJywgJ25hdGlvbmFsIG9yaWdpbicsICdnZW5kZXInLCAnaWRlbnRpdHknLCAnc2V4dWFsJywnb3JpZW50YXRpb24nLCAncHJvdGVjdGVkJywgJ3ZldGVyYW4nLCAnc3RhdHVzJywgJ2Rpc2FiaWxpdHknLCAnYWdlJywgJ3Byb3RlY3RlZCBzdGF0dXMnLCfDoicsICdlcXVhbCcsJ29wcG9ydHVuaXR5JywnYW1hem9uJywnZW1wbG95ZXInLCdodHRwcycsJ2VuJywnbGVnYWxseScsJ2Rpc2NyaW1pbmF0ZScsJ3dvcmtwbGFjZScsJ25hdGlvbmFsJywnYW1hem9uw6InKQ0KDQpQcmVmX2ZyZXEgPC0gYW16bl9qb2JzX2xhdGVzdCAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBgUHJlZmVycmVkLlF1YWxpZmljYXRpb25zYCkgJT4lDQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUgIA0KICBmaWx0ZXIoDQogICAgIXN0cl9kZXRlY3Qod29yZCwgcGF0dGVybiA9ICJbWzpkaWdpdDpdXSIpLCAjIHJlbW92ZXMgYW55IHdvcmRzIHdpdGggbnVtZXJpYyBkaWdpdHMNCiAgICAhc3RyX2RldGVjdCh3b3JkLCBwYXR0ZXJuID0gIltbOnB1bmN0Ol1dIiksICMgcmVtb3ZlcyBhbnkgcmVtYWluaW5nIHB1bmN0dWF0aW9ucw0KICAgICFzdHJfZGV0ZWN0KHdvcmQsIHBhdHRlcm4gPSAiKC4pXFwxezIsfSIpLCAgIyByZW1vdmVzIGFueSB3b3JkcyB3aXRoIDMgb3IgbW9yZSByZXBlYXRlZCBsZXR0ZXJzDQogICAgIXN0cl9kZXRlY3Qod29yZCwgcGF0dGVybiA9ICJcXGIoLilcXGIiKSwgICAgIyByZW1vdmVzIGFueSByZW1haW5pbmcgc2luZ2xlIGxldHRlciB3b3Jkcw0KICAgICF3b3JkICVpbiUgbmdyYW1fbGlzdCAgICAgICAgICAgICAgICAgIyByZW1vdmUgc3RvcHdvcmRzIGZyb20gYm90aCB3b3JkcyBpbiBiaS1ncmFtDQogICAgKSAlPiUNCiAgY291bnQod29yZCkgJT4lDQogIGZpbHRlcihuID49IDEwMDApICU+JSAjIGZpbHRlciBmb3Igd29yZHMgdXNlZCAxMDAwIG9yIG1vcmUgdGltZXMNCiAgc3ByZWFkKHdvcmQsIG4pICU+JSAgICAgICAgICAgICAgICAgIyBjb252ZXJ0IHRvIHdpZGUgZm9ybWF0DQogIG1hcF9kZihyZXBsYWNlX25hLCAwKSAgICAgICAgICAgICAgICAgIyByZXBsYWNlIE5BcyB3aXRoIDANCg0KUHJlZl9mcmVxX3RhbGwgPC0gIGFzLmRhdGEuZnJhbWUodChQcmVmX2ZyZXEpKQ0KUHJlZl9mcmVxX3RhbGwgIDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKFByZWZfZnJlcV90YWxsLCAiS2V5d29yZHMiKQ0KUHJlZl9mcmVxX3RhbGwgPC0gUHJlZl9mcmVxX3RhbGxbb3JkZXIoLVByZWZfZnJlcV90YWxsJFYxKSxdDQpQcmVmX2ZyZXFfdGFsbCA9IG11dGF0ZShQcmVmX2ZyZXFfdGFsbCwgb25lX3BjdCA9IHJvdW5kKChWMSAvIHN1bShWMSkpKjEwMCkpDQoja25pdHI6OiBrYWJsZShoZWFkKFByZWZfZnJlcV90YWxsLDEwKSkgDQpoZWFkKFByZWZfZnJlcV90YWxsLDEwKSAlPiUgYXJyYW5nZShvbmVfcGN0KSAlPiUgIG11dGF0ZShLZXl3b3JkcyA9IGZjdF9yZW9yZGVyKEtleXdvcmRzLCBvbmVfcGN0KSkgJT4lDQpnZ3Bsb3QoYWVzKCB4PUtleXdvcmRzLHk9b25lX3BjdCkpICsgDQogICAgZ2VvbV9iYXIoIHN0YXQ9ImlkZW50aXR5Iixjb2xvcj0iYmxhY2siKSsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlMChvbmVfcGN0LCIlIikpLGNvbG9yID0gIm9yYW5nZSIsaGp1c3Q9MSx2anVzdD0wLjMpKw0KICB0aGVtZSggYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCBsaW5ldHlwZSA9ICJzb2xpZCIpKSsNCiAgY29vcmRfZmxpcCgpKw0KICAgIHhsYWIoIiIpKyB5bGFiKCJQZXJjZW50YWdlIG9mIEZyZXF1ZW5jeSIpKw0KICB0aGVtZSggYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCBsaW5ldHlwZSA9ICJzb2xpZCIpKSsNCiAgIGdndGl0bGUoIlBlcmNlbnRhZ2Ugb2YgT25lIFdvcmQgRnJlcXVlbmNpZXMiKSArIA0KICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQ9LjgsIGZhY2U9ImJvbGQiKSkNCg0KYGBgDQoNCiMjIyMgVHdvIFdvcmRzIEZyZXF1ZW5jeSBjb3VudA0KDQpgYGB7ciAscmVzdWx0cz0gImFzaXMiLHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNClByZWZfZnJlcV9iaSA8LSBhbXpuX2pvYnNfbGF0ZXN0ICU+JQ0KICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgYFByZWZlcnJlZC5RdWFsaWZpY2F0aW9uc2AsdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQ0KICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICANCiAgZmlsdGVyKA0KICAgICF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCwgICAgICAgICAgICAgICAgICMgcmVtb3ZlIHN0b3B3b3JkcyBmcm9tIGJvdGggd29yZHMgaW4gYmktZ3JhbQ0KICAgICF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCwNCiAgICAhc3RyX2RldGVjdCh3b3JkMSwgcGF0dGVybiA9ICJbWzpkaWdpdDpdXSIpLCAjIHJlbW92ZXMgYW55IHdvcmRzIHdpdGggbnVtZXJpYyBkaWdpdHMNCiAgICAhc3RyX2RldGVjdCh3b3JkMiwgcGF0dGVybiA9ICJbWzpkaWdpdDpdXSIpLA0KICAgICFzdHJfZGV0ZWN0KHdvcmQxLCBwYXR0ZXJuID0gIltbOnB1bmN0Ol1dIiksICMgcmVtb3ZlcyBhbnkgcmVtYWluaW5nIHB1bmN0dWF0aW9ucw0KICAgICFzdHJfZGV0ZWN0KHdvcmQyLCBwYXR0ZXJuID0gIltbOnB1bmN0Ol1dIiksDQogICAgIXN0cl9kZXRlY3Qod29yZDEsIHBhdHRlcm4gPSAiKC4pXFwxezIsfSIpLCAgIyByZW1vdmVzIGFueSB3b3JkcyB3aXRoIDMgb3IgbW9yZSByZXBlYXRlZCBsZXR0ZXJzDQogICAgIXN0cl9kZXRlY3Qod29yZDIsIHBhdHRlcm4gPSAiKC4pXFwxezIsfSIpLA0KICAgICFzdHJfZGV0ZWN0KHdvcmQxLCBwYXR0ZXJuID0gIlxcYiguKVxcYiIpLCAgICMgcmVtb3ZlcyBhbnkgcmVtYWluaW5nIHNpbmdsZSBsZXR0ZXIgd29yZHMNCiAgICAhc3RyX2RldGVjdCh3b3JkMiwgcGF0dGVybiA9ICJcXGIoLilcXGIiKSwNCiAgICAhd29yZDEgJWluJSBuZ3JhbV9saXN0LCAgICAgICAgICAgICAgICAgIyByZW1vdmUgc3RvcHdvcmRzIGZyb20gYm90aCB3b3JkcyBpbiBiaS1ncmFtDQogICAgIXdvcmQyICVpbiUgbmdyYW1fbGlzdA0KICAgICkgJT4lDQogIHVuaXRlKCJiaWdyYW0iLCBjKHdvcmQxLCB3b3JkMiksIHNlcCA9ICIgIikgJT4lDQogIGNvdW50KGJpZ3JhbSkgJT4lDQogIGZpbHRlcihuID49IDgwMCkgJT4lICMgZmlsdGVyIGZvciBiaS1ncmFtcyB1c2VkIDEwMDAgb3IgbW9yZSB0aW1lcw0KICBzcHJlYWQoYmlncmFtLCBuKSAlPiUgICAgICAgICAgICAgICAgICMgY29udmVydCB0byB3aWRlIGZvcm1hdA0KICBtYXBfZGYocmVwbGFjZV9uYSwgMCkgICAgICAgICAgICAgICAgICMgcmVwbGFjZSBOQXMgd2l0aCAwDQoNClByZWZfZnJlcV9iaV90YWxsIDwtIGFzLmRhdGEuZnJhbWUodChQcmVmX2ZyZXFfYmkpKQ0KUHJlZl9mcmVxX2JpX3RhbGwgIDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKFByZWZfZnJlcV9iaV90YWxsLCAiS2V5d29yZHMiKQ0KUHJlZl9mcmVxX2JpX3RhbGwgPC0gUHJlZl9mcmVxX2JpX3RhbGxbb3JkZXIoLVByZWZfZnJlcV9iaV90YWxsJFYxKSxdDQoja25pdHI6OiBrYWJsZShoZWFkKFByZWZfZnJlcV9iaV90YWxsLDEwKSkNClByZWZfZnJlcV9iaV90YWxsID0gbXV0YXRlKFByZWZfZnJlcV9iaV90YWxsLCBvbmVfcGN0ID0gcm91bmQoKFYxIC8gc3VtKFYxKSkqMTAwKSkNCg0KDQpoZWFkKFByZWZfZnJlcV9iaV90YWxsLDEwKSAlPiUgYXJyYW5nZShvbmVfcGN0KSAlPiUgIG11dGF0ZShLZXl3b3JkcyA9IGZjdF9yZW9yZGVyKEtleXdvcmRzLCBvbmVfcGN0KSkgJT4lDQpnZ3Bsb3QoYWVzKCB4PUtleXdvcmRzLHk9b25lX3BjdCkpICsgDQogICAgZ2VvbV9iYXIoIHN0YXQ9ImlkZW50aXR5Iixjb2xvcj0iYmxhY2siKSsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlMChvbmVfcGN0LCIlIikpLGNvbG9yID0gIm9yYW5nZSIsaGp1c3Q9MSx2anVzdD0wLjMpKw0KICB0aGVtZSggYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCBsaW5ldHlwZSA9ICJzb2xpZCIpKSsNCiAgY29vcmRfZmxpcCgpKw0KICAgIHhsYWIoIiIpKyB5bGFiKCJQZXJjZW50YWdlIG9mIEZyZXF1ZW5jeSIpKw0KICB0aGVtZSggYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCBsaW5ldHlwZSA9ICJzb2xpZCIpKSsNCiAgIGdndGl0bGUoIlBlcmNlbnRhZ2Ugb2YgVHdvIFdvcmRzIEZyZXF1ZW5jaWVzIikgKyANCiAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIikpDQpgYGANCg0KDQojIyMjIFRocmVlIFdvcmRzIEZyZXF1ZW5jeSBjb3VudA0KDQpgYGB7cixyZXN1bHRzPSAiYXNpcyIsd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KUHJlZl9mcmVxX3RyaSA8LSBhbXpuX2pvYnNfbGF0ZXN0ICU+JQ0KICB1bm5lc3RfdG9rZW5zKHRyaWdyYW0sIGBQcmVmZXJyZWQuUXVhbGlmaWNhdGlvbnNgLHRva2VuID0gIm5ncmFtcyIsIG4gPSAzKSAlPiUNCiAgc2VwYXJhdGUodHJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiLCJ3b3JkMyIpLCBzZXAgPSAiICIpICU+JSAgDQogIGZpbHRlcigNCiAgICAhd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsICAgICAgICAgICAgICAgICAjIHJlbW92ZSBzdG9wd29yZHMgZnJvbSBib3RoIHdvcmRzIGluIGJpLWdyYW0NCiAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgIXN0cl9kZXRlY3Qod29yZDEsIHBhdHRlcm4gPSAiW1s6ZGlnaXQ6XV0iKSwgIyByZW1vdmVzIGFueSB3b3JkcyB3aXRoIG51bWVyaWMgZGlnaXRzDQogICAgIXN0cl9kZXRlY3Qod29yZDIsIHBhdHRlcm4gPSAiW1s6ZGlnaXQ6XV0iKSwNCiAgICAhc3RyX2RldGVjdCh3b3JkMywgcGF0dGVybiA9ICJbWzpkaWdpdDpdXSIpLCAgICANCiAgICAhc3RyX2RldGVjdCh3b3JkMSwgcGF0dGVybiA9ICJbWzpwdW5jdDpdXSIpLCAjIHJlbW92ZXMgYW55IHJlbWFpbmluZyBwdW5jdHVhdGlvbnMNCiAgICAhc3RyX2RldGVjdCh3b3JkMiwgcGF0dGVybiA9ICJbWzpwdW5jdDpdXSIpLA0KICAgICFzdHJfZGV0ZWN0KHdvcmQzLCBwYXR0ZXJuID0gIltbOnB1bmN0Ol1dIiksICAgIA0KICAgICFzdHJfZGV0ZWN0KHdvcmQxLCBwYXR0ZXJuID0gIiguKVxcMXsyLH0iKSwgICMgcmVtb3ZlcyBhbnkgd29yZHMgd2l0aCAzIG9yIG1vcmUgcmVwZWF0ZWQgbGV0dGVycw0KICAgICFzdHJfZGV0ZWN0KHdvcmQyLCBwYXR0ZXJuID0gIiguKVxcMXsyLH0iKSwNCiAgICAhc3RyX2RldGVjdCh3b3JkMywgcGF0dGVybiA9ICIoLilcXDF7Mix9IiksICAgIA0KICAgICFzdHJfZGV0ZWN0KHdvcmQxLCBwYXR0ZXJuID0gIlxcYiguKVxcYiIpLCAgICMgcmVtb3ZlcyBhbnkgcmVtYWluaW5nIHNpbmdsZSBsZXR0ZXIgd29yZHMNCiAgICAhc3RyX2RldGVjdCh3b3JkMiwgcGF0dGVybiA9ICJcXGIoLilcXGIiKSwNCiAgICAhc3RyX2RldGVjdCh3b3JkMywgcGF0dGVybiA9ICJcXGIoLilcXGIiKSwgICAgDQogICAgIXdvcmQxICVpbiUgbmdyYW1fbGlzdCwgICAgICAgICAgICAgICAgICMgcmVtb3ZlIHN0b3B3b3JkcyBmcm9tIGJvdGggd29yZHMgaW4gYmktZ3JhbQ0KICAgICF3b3JkMiAlaW4lIG5ncmFtX2xpc3QsDQogICAgIXdvcmQzICVpbiUgbmdyYW1fbGlzdA0KICAgICkgJT4lDQogIHVuaXRlKCJ0cmlncmFtIiwgYyh3b3JkMSwgd29yZDIsIHdvcmQzKSwgc2VwID0gIiAiKSAlPiUNCiAgY291bnQodHJpZ3JhbSkgJT4lDQogIGZpbHRlcihuID49IDUwMCkgJT4lICMgZmlsdGVyIGZvciBiaS1ncmFtcyB1c2VkIDUwMCBvciBtb3JlIHRpbWVzDQogIHNwcmVhZCh0cmlncmFtLCBuKSAlPiUgICAgICAgICAgICAgICAgICMgY29udmVydCB0byB3aWRlIGZvcm1hdA0KICBtYXBfZGYocmVwbGFjZV9uYSwgMCkgICAgICAgICAgICAgICAgICMgcmVwbGFjZSBOQXMgd2l0aCAwDQoNClByZWZfZnJlcV90cmlfdGFsbCA8LSBhcy5kYXRhLmZyYW1lKHQoUHJlZl9mcmVxX3RyaSkpDQpQcmVmX2ZyZXFfdHJpX3RhbGwgIDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKFByZWZfZnJlcV90cmlfdGFsbCwgIktleXdvcmRzIikNClByZWZfZnJlcV90cmlfdGFsbCA8LSBQcmVmX2ZyZXFfdHJpX3RhbGxbb3JkZXIoLVByZWZfZnJlcV90cmlfdGFsbCRWMSksXQ0KI2tuaXRyOjoga2FibGUoaGVhZChQcmVmX2ZyZXFfdHJpX3RhbGwsMTApKQ0KDQpQcmVmX2ZyZXFfdHJpX3RhbGwgPSBtdXRhdGUoUHJlZl9mcmVxX3RyaV90YWxsLCBvbmVfcGN0ID0gcm91bmQoKFYxIC8gc3VtKFYxKSkqMTAwKSkNCg0KDQpoZWFkKFByZWZfZnJlcV90cmlfdGFsbCwxMCkgJT4lIGFycmFuZ2Uob25lX3BjdCkgJT4lICBtdXRhdGUoS2V5d29yZHMgPSBmY3RfcmVvcmRlcihLZXl3b3Jkcywgb25lX3BjdCkpICU+JQ0KZ2dwbG90KGFlcyggeD1LZXl3b3Jkcyx5PW9uZV9wY3QpKSArIA0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsY29sb3I9ImJsYWNrIikrDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAob25lX3BjdCwiJSIpKSxjb2xvciA9ICJvcmFuZ2UiLGhqdXN0PTEsdmp1c3Q9MC4zKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgICB4bGFiKCIiKSsgeWxhYigiUGVyY2VudGFnZSBvZiBGcmVxdWVuY3kiKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogICBnZ3RpdGxlKCJQZXJjZW50YWdlIG9mIFRocmVlIFdvcmRzIEZyZXF1ZW5jaWVzIikgKyANCiAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIikpDQoNCmBgYA0KDQoNCiMjIyMgQ3JlYXRpbmcgZGljdGlvbmFyaWVzIGJhc2VkIG9uIHRoZSBvYnNlcnZlZCAiT25lIFdvcmQiLCAiVHdvIFdvcmRzIiBhbmQgIlRocmVlIFdvcmRzIiBmcmVxdWVuY3kgY291bnRzLiBUaGVzZSB0cmVuZHMgYXJlIHVzZWQgdG8gY3JlYXRlIHR3byBkaWZmZXJlbnQgc2V0cyBvZiBza2lsbHMgLSAqKlRlY2huaWNhbCBza2lsbHMqKiBhbmQgKipTb2Z0IHNraWxscyoqLiBUaHVzLCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCBzdGF0aXN0aWNzIGlzIGJvdGggc2NpZW5jZSBhbmQgYXJ0IQ0KDQpgYGB7cixyZXN1bHRzPSAiYXNpcyIgfQ0Kc29mdF9za2lsbHMgPC0gZGljdGlvbmFyeShsaXN0KFByb2Zlc3Npb25hbF9FeHBlcmllbmNlID0gYygnZGVtb25zdHJhdGVkIGFiaWxpdHknLCd0cmFjayByZWNvcmQnLCdwcm92ZW4gYWJpbGl0eScsJ2V4cGVyaWVuY2UgYnVpbGRpbmcnLCdwcm9mZXNzaW9uYWwnKSxMZWFkZXJzaGlwID0gYygnbGVhZGVyc2hpcCBwcmluY2lwbGVzJywnCWFmZmlybWF0aXZlIGFjdGlvbicsJ2Zhc3QgcGFjZWQnLCdsZWFkJywnbGVhZGVyc2hpcCcsJ2d1aWRlJyksQ29tbXVuaWNhdGlvbiA9IGMoJ3dyaXR0ZW4gY29tbXVuaWNhdGlvbicsJ2NvbW11bmljYXRpb24gc2tpbGxzJywndmVyYmFsJyksIE1hbmFnZW1lbnQgPSBjKCdtYW5hZ2VtZW50JywnbWJhJyksIEJ1c2luZXNzX0tub3dsZWRnZSA9IGMoJ0J1c2luZXNzJyksIENvbW1pdG1lbnQgPSBjKCdjb21taXR0ZWQnLCdjb21taXRtZW50JyksIERlZXBfZGl2ZSA9IGMoJ2RlZXAgZGl2ZScsJ2ludmVzdGlnYXRlJywnZGl2ZScpLCBMZWFybmluZyA9IGMoJ2xlYXJuaW5nJywnbGVhcm5lcicsJ2N1cmlvdXMnLCdpbnF1aXNpdGl2ZScpLCBDcmVhdGl2aXR5ID0gYygnY3JlYXRpdmUnLCdkZXNpZ24nLCd2aXN1YWwnKSwgUHJvYmxlbV9Tb2x2ZXIgPSBjKCdzb2x2ZXInLCdwdXp6bGUnLCdwcm9ibGVtJywnZnJhbWV3b3JrJywnY29tcGxleCcsJ3RoaW5rZXInKSxDb25maWRlbmNlID0gYygnY29uZmlkZW5jZScsJ2JlbGlldmUnKSkpDQoNCnRlY2huaWNhbF9za2lsbHMgPC0gZGljdGlvbmFyeShsaXN0KEdlbmVyYWxfQ29kaW5nID0gYygnY29kZScsJ2NvZGluZyBzdGFuZGFyZHMnLCdzb3VyY2UgY29udHJvbCcsJ3Byb2dyYW1taW5nJyksU29mdHdhcmVfRGV2ZWxvcG1lbnQgPSBjKCdzb2Z0d2FyZSBkZXZlbG9wbWVudCcsJ3NvZnR3YXJlIGVuZ2luZWVyaW5nJyksU3ViamVjdF9NYXR0ZXJfRXhwZXJ0ID0gYygnc3ViamVjdCBtYXR0ZXInKSxDb21wdXRlcl9TY2llbmNlID0gYygnY29tcHV0ZXIgc2NpZW5jZScpLCBQeXRob24gPSBjKCdQeXRob24nLCdweXRob24nKSwgRGF0YWJhc2UgPSBjKCdkYXRhYmFzZScsJ1RlcmFkYXRhJyksU1FMID0gJ1NRTCcsUiA9ICdSJyxTdGF0aXN0aWNzID0gYygnc3RhdGlzdGljcycsICdzdGF0cycpLENfRmFtaWx5ID1jKCdDJywnQysrJywnQyMnKSxKYXZhID0gJ0phdmEnLFBlcmwgPSAnUGVybCcsIFNjYWxhID0gJ1NjYWxhJyxNYWNoaW5lX0xlYXJuaW5nID0gYygnbWFjaGluZSBsZWFybmluZycsJ2RlZXAgbGVhcm5pbmcnLCdUZW5zb3JmbG93JyksQUkgPSBjKCdhaScsJ2FydGlmaWNpYWwgaW50ZWxsaWdlbmNlJywnaW90JywnQUknKSxKdWxpYSA9ICdKdWxpYScsTWF0bGFiICA9ICdNYXRsYWInLEJpZ19EYXRhID0gYygnYmlnIGRhdGEnLCdBcGFjaGUgU3BhcmsnLCdBenVyZScsJ0FwYWNoZSBIaXZlJywnSGFkb29wJyksT3RoZXJfU3RhdGlzY2FsX3Rvb2xzID0gYygnU0FTJywgJ21pbml0YWInLCdTUFNTJywncHJpc20nKSxUYWJsZWF1PSBjKCdUYWJsZWF1JywncG93ZXIgYmknKSxCdXNpbmVzc19JbnRlbGxpZ2VuY2UgPSBjKCdCSScsJ2JpJywnYnVzaW5lc3MgaW50ZWxsaWdlbmNlJykpKQ0KDQphbGxfc2tpbGxzID0gIGRpY3Rpb25hcnkobGlzdChQcm9mZXNzaW9uYWxfRXhwZXJpZW5jZSA9IGMoJ2RlbW9uc3RyYXRlZCBhYmlsaXR5JywndHJhY2sgcmVjb3JkJywncHJvdmVuIGFiaWxpdHknLCdleHBlcmllbmNlIGJ1aWxkaW5nJywncHJvZmVzc2lvbmFsJyksTGVhZGVyc2hpcCA9IGMoJ2xlYWRlcnNoaXAgcHJpbmNpcGxlcycsJwlhZmZpcm1hdGl2ZSBhY3Rpb24nLCdmYXN0IHBhY2VkJywnbGVhZCcsJ2xlYWRlcnNoaXAnLCdndWlkZScpLENvbW11bmljYXRpb24gPSBjKCd3cml0dGVuIGNvbW11bmljYXRpb24nLCdjb21tdW5pY2F0aW9uIHNraWxscycsJ3ZlcmJhbCcpLCBNYW5hZ2VtZW50ID0gYygnbWFuYWdlbWVudCcsJ21iYScpLCBCdXNpbmVzc19Lbm93bGVkZ2UgPSBjKCdCdXNpbmVzcycpLCBDb21taXRtZW50ID0gYygnY29tbWl0dGVkJywnY29tbWl0bWVudCcpLCBEZWVwX2RpdmUgPSBjKCdkZWVwIGRpdmUnLCdpbnZlc3RpZ2F0ZScsJ2RpdmUnKSwgTGVhcm5pbmcgPSBjKCdsZWFybmluZycsJ2xlYXJuZXInLCdjdXJpb3VzJywnaW5xdWlzaXRpdmUnKSwgQ3JlYXRpdml0eSA9IGMoJ2NyZWF0aXZlJywnZGVzaWduJywndmlzdWFsJyksIFByb2JsZW1fU29sdmVyID0gYygnc29sdmVyJywncHV6emxlJywncHJvYmxlbScsJ2ZyYW1ld29yaycsJ2NvbXBsZXgnLCd0aGlua2VyJyksQ29uZmlkZW5jZSA9IGMoJ2NvbmZpZGVuY2UnLCdiZWxpZXZlJyksR2VuZXJhbF9Db2RpbmcgPSBjKCdjb2RlJywnY29kaW5nIHN0YW5kYXJkcycsJ3NvdXJjZSBjb250cm9sJywncHJvZ3JhbW1pbmcnKSxTb2Z0d2FyZV9EZXZlbG9wbWVudCA9IGMoJ3NvZnR3YXJlIGRldmVsb3BtZW50Jywnc29mdHdhcmUgZW5naW5lZXJpbmcnKSxTdWJqZWN0X01hdHRlcl9FeHBlcnQgPSBjKCdzdWJqZWN0IG1hdHRlcicpLENvbXB1dGVyX1NjaWVuY2UgPSBjKCdjb21wdXRlciBzY2llbmNlJyksIFB5dGhvbiA9IGMoJ1B5dGhvbicsJ3B5dGhvbicpLCBEYXRhYmFzZSA9IGMoJ2RhdGFiYXNlJywnVGVyYWRhdGEnKSxTUUwgPSAnU1FMJyxSID0gJ1InLFN0YXRpc3RpY3MgPSBjKCdzdGF0aXN0aWNzJywnTWluaXRhYicsICdzdGF0cycpLENfZmFtaWx5ID1jKCdDJywnQysrJywnQyMnKSxKYXZhID0gJ0phdmEnLFBlcmwgPSAnUGVybCcsIFNjYWxhID0gJ1NjYWxhJyxNYWNoaW5lX0xlYXJuaW5nID0gYygnbWFjaGluZSBsZWFybmluZycsJ2RlZXAgbGVhcm5pbmcnLCdUZW5zb3JmbG93JyksQUkgPSBjKCdhaScsJ2FydGlmaWNpYWwgaW50ZWxsaWdlbmNlJywnaW90JywnQUknKSxKdWxpYSA9ICdKdWxpYScsTWF0bGFiICA9ICdNYXRsYWInLEJpZ19kYXRhID0gYygnYmlnIGRhdGEnLCdBcGFjaGUgU3BhcmsnLCdBenVyZScsJ0FwYWNoZSBIaXZlJywnSGFkb29wJyksT3RoZXJfU3RhdGlzY2FsX3Rvb2xzID0gYygnU0FTJywgJ21pbml0YWInLCdTUFNTJywncHJpc20nKSxUYWJsZWF1PSBjKCdUYWJsZWF1JywncG93ZXIgYmknKSxCdXNpbmVzc19JbnRlbGxpZ2VuY2UgPSBjKCdCSScsJ2JpJywnYnVzaW5lc3MgaW50ZWxsaWdlbmNlJykpKQ0KDQpSX3B5dGhvbl9NYXRsYWJfb3RoZXIgPC0gZGljdGlvbmFyeShsaXN0KFIgPSAnUicsUHl0aG9uID0gYygnUHl0aG9uJywncHl0aG9uJyksTWF0bGFiID0nbWF0bGFiJywgT3RoZXJfU3RhdGlzY2FsX3Rvb2xzID0gYygnU0FTJywgJ21pbml0YWInLCdTUFNTJywncHJpc20nKSkpDQpgYGANCg0KDQojIyMjIFRoZSBtb3N0IGltcG9ydGFudCAqKlNvZnQgc2tpbGxzKiogYXJlOiANCmBgYHtyLHJlc3VsdHM9ICJhc2lzIn0NCg0KZGF0YV9kaWN0PC1kZm0oYW16bl9qb2JzX2xhdGVzdCRQcmVmZXJyZWQuUXVhbGlmaWNhdGlvbnMsIGRpY3Rpb25hcnk9c29mdF9za2lsbHMpDQp0b3Bfc29mdCA8LSBhcy5kYXRhLmZyYW1lKHRvcGZlYXR1cmVzKGRhdGFfZGljdCkpDQp0b3Bfc29mdCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih0b3Bfc29mdCwgIktleXdvcmRzIikNCmNvbG5hbWVzKHRvcF9zb2Z0KSA8LSBjKCJLZXl3b3JkcyIsImZyZXEiKQ0KdG9wX3NvZnQgPSBtdXRhdGUodG9wX3NvZnQsIHNraWxsc19wY3QgPSByb3VuZCgoZnJlcSAvIHN1bShmcmVxKSkqMTAwKSkNCg0KdG9wX3NvZnQgJT4lIGFycmFuZ2Uoc2tpbGxzX3BjdCkgJT4lICBtdXRhdGUoS2V5d29yZHMgPSBmY3RfcmVvcmRlcihLZXl3b3Jkcywgc2tpbGxzX3BjdCkpICU+JQ0KZ2dwbG90KGFlcyggeD1LZXl3b3Jkcyx5PXNraWxsc19wY3QpKSArIA0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsY29sb3I9ImJsYWNrIikrDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoc2tpbGxzX3BjdCwiJSIpKSxjb2xvciA9ICJvcmFuZ2UiLGhqdXN0PTEsdmp1c3Q9MC4zKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgICB4bGFiKCIiKSsgeWxhYigiIikrDQogIHRoZW1lKCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEsIGxpbmV0eXBlID0gInNvbGlkIikpKw0KICAgZ2d0aXRsZSgiVG9wIFNvZnQgU2tpbGxzIGZvciAnRGF0YSBTY2llbmNlJyBhdCBBbWF6b24iKSArIA0KICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQ9LjgsIGZhY2U9ImJvbGQiKSkNCg0KYGBgDQoNCg0KIyMjIyBUaGUgbW9zdCBpbXBvcnRhbnQgKipUZWNobmljYWwgc2tpbGxzKiogYXJlOg0KYGBge3IscmVzdWx0cz0gImFzaXMifQ0KDQpkYXRhX2RpY3QyPC1kZm0oYW16bl9qb2JzX2xhdGVzdCRQcmVmZXJyZWQuUXVhbGlmaWNhdGlvbnMsIGRpY3Rpb25hcnk9dGVjaG5pY2FsX3NraWxscykNCnRvcF90ZWNoIDwtIGFzLmRhdGEuZnJhbWUodG9wZmVhdHVyZXMoZGF0YV9kaWN0MikpDQp0b3BfdGVjaCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih0b3BfdGVjaCwgIktleXdvcmRzIikNCmNvbG5hbWVzKHRvcF90ZWNoKSA8LSBjKCJLZXl3b3JkcyIsImZyZXEiKQ0KdG9wX3RlY2ggPSBtdXRhdGUodG9wX3RlY2gsIHNraWxsc19wY3QgPSByb3VuZCgoZnJlcSAvIHN1bShmcmVxKSkqMTAwKSkNCg0KdG9wX3RlY2ggJT4lIGFycmFuZ2Uoc2tpbGxzX3BjdCkgJT4lICBtdXRhdGUoS2V5d29yZHMgPSBmY3RfcmVvcmRlcihLZXl3b3Jkcywgc2tpbGxzX3BjdCkpICU+JQ0KZ2dwbG90KGFlcyggeD1LZXl3b3Jkcyx5PXNraWxsc19wY3QpKSArIA0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsY29sb3I9ImJsYWNrIikrDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoc2tpbGxzX3BjdCwiJSIpKSxjb2xvciA9ICJvcmFuZ2UiLGhqdXN0PTEsdmp1c3Q9MC4zKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgICB4bGFiKCIiKSsgeWxhYigiIikrDQogIHRoZW1lKCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEsIGxpbmV0eXBlID0gInNvbGlkIikpKw0KICAgZ2d0aXRsZSgiVG9wIFRlY2huaWNhbCBTa2lsbHMgZm9yICdEYXRhIFNjaWVuY2UnIGF0IEFtYXpvbiIpICsgDQogICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQobGluZWhlaWdodD0uOCwgZmFjZT0iYm9sZCIpKQ0KYGBgDQoNCiMjIyBDb25jbHVzaW9uDQoNCg0KIyMjIyBOb3csIGxldCdzIGNvbWJpbmUgYm90aCBzb2Z0IHNraWxscyBhbmQgdGVjaG5pY2FsIHNraWxscyBhbmQgZmluZCB0aGUgdG9wIERhdGEgU2NpZW5jZSBza2lsbHMgYXQgKipBbWF6b24qKi4gV2UgZmluZCB0aGF0ICoqOCBvdXQgb2YgdG9wIDEwKiogc2tpbGxzIHJlcXVpcmVkIGF0IEFtYXpvbiBhcmUgKipTb2Z0IFNraWxscyoqLiBTbywgd2hlbiB5b3UgcHJlcGFyZSBmb3IgYW4gaW50ZXJ2aWV3IGF0IEFtYXpvbiBuZXh0IHRpbWUsIGJlIHN1cmUgdG8gZm9jdXMgb24gaGlnaGxpZ2h0aW5nIHlvdXIgKioiU29mdCBTa2lsbHMiKiogDQoNCmBgYHtyLHJlc3VsdHM9ICJhc2lzIn0NCg0KZGF0YV9kaWN0NDwtZGZtKGFtem5fam9ic19sYXRlc3QkUHJlZmVycmVkLlF1YWxpZmljYXRpb25zLCBkaWN0aW9uYXJ5PWFsbF9za2lsbHMpDQp0b3Bfc2tpbGwgPC0gYXMuZGF0YS5mcmFtZSh0b3BmZWF0dXJlcyhkYXRhX2RpY3Q0KSkNCnRvcF9za2lsbCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih0b3Bfc2tpbGwsICJLZXl3b3JkcyIpDQpjb2xuYW1lcyh0b3Bfc2tpbGwpIDwtIGMoIktleXdvcmRzIiwiZnJlcSIpDQp0b3Bfc2tpbGwgPSBtdXRhdGUodG9wX3NraWxsLCBza2lsbHNfcGN0ID0gcm91bmQoKGZyZXEgLyBzdW0oZnJlcSkpKjEwMCkpDQoNCnRvcF9za2lsbCAlPiUgYXJyYW5nZShza2lsbHNfcGN0KSAlPiUgIG11dGF0ZShLZXl3b3JkcyA9IGZjdF9yZW9yZGVyKEtleXdvcmRzLCBza2lsbHNfcGN0KSkgJT4lDQpnZ3Bsb3QoYWVzKCB4PUtleXdvcmRzLHk9c2tpbGxzX3BjdCkpICsgDQogICAgZ2VvbV9iYXIoIHN0YXQ9ImlkZW50aXR5Iixjb2xvcj0iYmxhY2siKSsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlMChza2lsbHNfcGN0LCIlIikpLGNvbG9yID0gIm9yYW5nZSIsaGp1c3Q9MSx2anVzdD0wLjMpKw0KICB0aGVtZSggYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCBsaW5ldHlwZSA9ICJzb2xpZCIpKSsNCiAgY29vcmRfZmxpcCgpKw0KICAgIHhsYWIoIiIpKyB5bGFiKCIiKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogICBnZ3RpdGxlKCJUb3AgVGVjaG5pY2FsIFNraWxscyBmb3IgJ0RhdGEgU2NpZW5jZScgYXQgQW1hem9uIikgKyANCiAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIikpDQoNCmBgYA0KDQpgYGB7ciBza2lsbHNfaW1hZ2UsIHJlc3VsdHM9ImFzaXMiLCBlY2hvID1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3d3dy53b21lbi1pbi10ZWNobm9sb2d5LmNvbS9ocy1mcy9odWJmcy9Tb2Z0LXNraWxscy12cy10ZWNobmljYWwtc2tpbGxzLWNvbXByZXNzb3IuanBlZz93aWR0aD04MjcmbmFtZT1Tb2Z0LXNraWxscy12cy10ZWNobmljYWwtc2tpbGxzLWNvbXByZXNzb3IuanBlZyIpDQpgYGANCg0KDQoNCiMjIyMgQm9udXM6IExldCdzIGZpbmQgdGhlIG1vc3QgcG9wdWxhciBzdGF0aXN0aWNhbCB0b29sIGFtb25nICoqUHl0aG9uKiosICoqUioqLCAqKk1hdGxhYioqIGFuZCBvdGhlcnMgYXQgKipBbWF6b24qKg0KYGBge3IscmVzdWx0cz0gImFzaXMifQ0KDQpkYXRhX2RpY3QzPC1kZm0oYW16bl9qb2JzX2xhdGVzdCRQcmVmZXJyZWQuUXVhbGlmaWNhdGlvbnMsIGRpY3Rpb25hcnk9Ul9weXRob25fTWF0bGFiX290aGVyKQ0KdG9wX3N0YXRzIDwtIGFzLmRhdGEuZnJhbWUodG9wZmVhdHVyZXMoZGF0YV9kaWN0MykpDQp0b3Bfc3RhdHMgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odG9wX3N0YXRzLCAiS2V5d29yZHMiKQ0KY29sbmFtZXModG9wX3N0YXRzKSA8LSBjKCJLZXl3b3JkcyIsImZyZXEiKQ0KdG9wX3N0YXRzID0gbXV0YXRlKHRvcF9zdGF0cywgc2tpbGxzX3BjdCA9IHJvdW5kKChmcmVxIC8gc3VtKGZyZXEpKSoxMDApKQ0KDQp0b3Bfc3RhdHMgJT4lIGFycmFuZ2Uoc2tpbGxzX3BjdCkgJT4lICBtdXRhdGUoS2V5d29yZHMgPSBmY3RfcmVvcmRlcihLZXl3b3Jkcywgc2tpbGxzX3BjdCkpICU+JQ0KZ2dwbG90KGFlcyggeD1LZXl3b3Jkcyx5PXNraWxsc19wY3QpKSArIA0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsY29sb3I9ImJsYWNrIikrDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoc2tpbGxzX3BjdCwiJSIpKSxjb2xvciA9ICJvcmFuZ2UiLGhqdXN0PTEsdmp1c3Q9MC4zKSsNCiAgdGhlbWUoIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgbGluZXR5cGUgPSAic29saWQiKSkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgICB4bGFiKCIiKSsgeWxhYigiIikrDQogIHRoZW1lKCBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEsIGxpbmV0eXBlID0gInNvbGlkIikpKw0KICAgZ2d0aXRsZSgiVG9wIFN0YXRpc3RpY2FsIFRvb2xzIGZvciAnRGF0YSBTY2llbmNlJyBhdCBBbWF6b24iKSArIA0KICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQ9LjgsIGZhY2U9ImJvbGQiKSkNCmBgYA0KDQo8ZGl2IGNsYXNzPSJ0b2NpZnktZXh0ZW5kLXBhZ2UiIGRhdGEtdW5pcXVlPSJ0b2NpZnktZXh0ZW5kLXBhZ2UiIHN0eWxlPSJoZWlnaHQ6IDA7Ij48L2Rpdj4NCg==