Preparation
This tool uses automated webbrowsing using RSelenium
. We
first need to install this package, along with some other packages:
netstat
and pingr
: find port
information
rvest
: harvest webpages
tidyverse
: data wrangling
stringr
: string operations
clean up
rm(list=ls())
gc()
general custom
functions
fpackage.check
: Check if packages are installed (and
install if not) in R
fgeneratepass
: Password generator function
fsave
: save data with time stamp in correct
directory
fload
: load R-objects under new names
fpackage.check <- function(packages) {
lapply(packages, FUN = function(x) {
if (!require(x, character.only = TRUE)) {
install.packages(x, dependencies = TRUE)
library(x, character.only = TRUE)
}
})
}
fgeneratepass <- function(nchar=7, nupper=1, nspecial=1, nnumber=2) {
special <- c("!","@","#","$","%","^","&","*","(",")","_","+")
number <- c(1:9)
chars <- c(sample(special,nspecial,replace=T),
sample(number,nnumber,replace=T),
sample(LETTERS,nupper,replace=T),
sample(letters,nchar-(nupper+nspecial+nnumber),
replace=T))
pass <- paste0(sample(chars),collapse="")
return(pass)
}
fsave <- function(x, file, location = "./local/", ...) {
if (!dir.exists(location))
dir.create(location)
datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
totalname <- paste(location, datename, file, sep = "")
print(paste("SAVED: ", totalname, sep = ""))
save(x, file = totalname)
}
fload <- function(fileName){
load(fileName)
get(ls()[ls() != "fileName"])
}
necessary
packages
packages = c("RSelenium", "rvest", "tidyverse", "netstat", "pingr", "stringr")
fpackage.check(packages)
VPN
It may be important to use a VPN. Make sure to regularly change your
public IP adress. I used HMA.
set up RSelenium
#find a free port
port <- netstat::free_port(random=TRUE)
#ping port for confirmation
pingr::ping_port("localhost", port)
#set up selenium server and browser
rD <- rsDriver(browser="firefox", port=port, chromever=NULL, verbose=F)
remDr <- rD[["client"]]
NOTE: after using RSelenium::rsDriver
for a while without issue, I recently got error messages. The root cause
of the problem is that rsDriver()
is configured to launch
the Selenium server with the latest versions of Chrome and
Firefox drivers, even if you have specified the other browser to
run. To solve this, just pass in NULL
in the Chrome
driver version chromever
… (see also this
and this
stackoverflow post).
If at any time you wish to terminate the process, use the code:
pid <- rD$server$process$get_pid()#get process id
system(paste0("Taskkill /F /T" ," /PID ", pid))
Create accounts
We create a dataframe of accounts to be created:
- names
- email adresses
- passwords generated using
fgeneratepass
- assign accounts to a training group
- assign groups to either a control or experimental/intervention
condition
Here, we create 5 groups of 15 accounts (thus, 75 accounts)
n=75 #accounts
N=5 #groups
#make df, athlete accounts assigned to groups
accounts <- data.frame(voornaam="A", #arbirary... A for athlete
achternaam=as.character(c(1:n)), #indicator
email=NA,
paswoord=NA,
groep=rep(1:N,each=n/N))
#groups can be given names, instead of numbers...
#i name the groups based on the course name,
#i saved this in a secret folder
load("./secret/course_names.R")
accounts$groep <- ifelse(accounts$groep==1, courses[1],
ifelse(accounts$groep==2, courses[2],
ifelse(accounts$groep==3, courses[3],
ifelse(accounts$groep==4, courses[4],
ifelse(accounts$groep==5, courses[5], NA)))))
#create email and password
for (i in 1:n) {
#create email and password
accounts$email[i] <- paste0("atleet",i,"@rsc.nl")
accounts$paswoord[i] <- fgeneratepass(7)
}
#also, randomly assign a number of groups a social wall.
n_socwall <- 3 #here, 3
accounts$socialwall <- NA
#sample 3 groups that will be assigned a social wall
socwall_group <- sample(unique(accounts$groep),n_socwall)
accounts$socialwall <- ifelse(accounts$groep %in% socwall_group, 1,0)
#we also add a variable 'occupied', which indicates whether a pre-set account
#is customized (i.e., first and lastname are assigned) and ready for use.
accounts$occupied <- F
#and a variable id which denotes participants email adress, to ultimately link FSurge data with survey (and behavior) data
accounts$id <- NA
#save to local folder using fsave
#######fsave(accounts, "accounts.R")
Next up, we use RSelenium to create the accounts.
We customize the account settings, and the workout page:
#load in the accounts
load("./local/accounts.R")
n <- nrow(accounts)
for (i in 1:n) {
Sys.sleep(1)
print(paste("creating account:", accounts$email[i]))
#navigate to create account page
remDr$navigate("https://log.finalsurge.com/register.cshtml?page_redirect=%2f")
#insert name
remDr$findElement(using = "id", value = "create_first")$sendKeysToElement(list(accounts$voornaam[i]))
remDr$findElement(using = "id", value = "create_last")$sendKeysToElement(list(accounts$achternaam[i]))
#and email
remDr$findElement(using = "id", value = "create_email")$sendKeysToElement(list(accounts$email[i]))
#adjust timezone
remDr$findElement(using = "id", value = "create_timezone")$clickElement()
#get inner html
inner <- remDr$findElement(using = "id", value = "create_timezone")$getElementAttribute("innerHTML")[[1]]
inner %>%
read_html() %>% #read html
html_nodes("option") -> options #get all option tags
#pick (GMT+01:00) Amsterdam, Berlin, ...
remDr$findElements(using = "tag name", value ="option")[[which(grepl("Amsterdam", options))]]$clickElement()
#insert password
remDr$findElement(using = "id", value = "password_meter")$sendKeysToElement(list(accounts$paswoord[i]))
remDr$findElement(using = "id", value = "create_passwordmatch")$sendKeysToElement(list(accounts$paswoord[i]))
#create account
remDr$findElement("class name", "btn")$clickElement()
Sys.sleep(1)
#adjust user settings
remDr$navigate("https://log.finalsurge.com/UserProfile.cshtml?edit=s#settings")
#select language: Dutch
remDr$findElements(using = "class name", value = "radio")[[5]]$clickElement()
Sys.sleep(.5)
#convert to metric system
remDr$findElement(using = "id", value = "UnitMetric")$clickElement()
#24 hours display
remDr$findElement(using = "id", value = "TDisplay24")$clickElement()
#week start on monday
remDr$findElement(using = "id", value = "WSM")$clickElement()
#save
remDr$findElement("id", "saveButtonSettings")$clickElement()
Sys.sleep(1)
#I also use the workout customization page to adjust what activity types athletes can upload. now it is too laborious, and there are way to many subtypes of activities.
#note: apparently, i can only edit/delete the subtypes.
#the activity sections/fields (eg, planned distance, heart rate, etc.) can be hidden, but this does not translate to the app...
remDr$navigate("https://log.finalsurge.com/WorkoutCustomize.cshtml")
#i just hide most pre-coded activity types... and just add all activity-types via customization
#i need to open the box of activity type j (if not already opened)
#and select the 'hide activity type'-checkbox.
#and save
#first, i put the ids of these checkboxes, and savebuttons in vectors
hidebox <- c(1,11,18,19,20,25,33,34,35,40)
savebtn <- c("saveButton_00000001-0001-0001-0001-000000000001",
"saveButton_00000003-0003-0003-0003-000000000003",
"saveButton_00000010-0010-0010-0010-000000000010",
"saveButton_00000005-0005-0005-0005-000000000005",
"saveButton_00000008-0008-0008-0008-000000000008",
"saveButton_00000002-0002-0002-0002-000000000002",
"saveButton_00000004-0004-0004-0004-000000000004",
"saveButton_00000006-0006-0006-0006-000000000006",
"saveButton_00000007-0007-0007-0007-000000000007",
"saveButton_00000009-0009-0009-0009-000000000009")
for (j in c(4,5,7,8,9,10)){ #for pre-set activity types
#(but keep running, swimming, walking, cycling; because of the nice symbol!)
Sys.sleep(1)
#open box of activity type j (if not already opened)
if(remDr$findElements("class name", "w-box-content")[[j]]$getElementText()[[1]] == ""){
remDr$findElements("class name", "w-box-header")[[j]]$clickElement() }
#click hide-checkbox
remDr$findElements("class name", "checkbox")[[hidebox[j]]]$clickElement()
#save
Sys.sleep(.5)
remDr$findElement("id", savebtn[j])$clickElement()
#close box
Sys.sleep(.5)
remDr$findElements("class name", "w-box-header")[[j]]$clickElement()
}
Sys.sleep(1)
#for running, swimming, cycling, hide subtype fields
#1 running
if(remDr$findElements("class name", "w-box-content")[[1]]$getElementText()[[1]] == ""){
remDr$findElements("class name", "w-box-header")[[1]]$clickElement()
}
#select all subtypes, so that they will be hidden
for (j in 2:10){
Sys.sleep(.5)
remDr$findElements("class name", "checkbox")[[j]]$clickElement()
}
#save and hide
Sys.sleep(.5)
remDr$findElement("id", savebtn[1])$clickElement()
Sys.sleep(.5)
remDr$findElements("class name", "w-box-header")[[1]]$clickElement()
#2 swimming
if(remDr$findElements("class name", "w-box-content")[[2]]$getElementText()[[1]] == ""){
remDr$findElements("class name", "w-box-header")[[2]]$clickElement()
}
for (j in 12:17){
Sys.sleep(.5)
remDr$findElements("class name", "checkbox")[[j]]$clickElement()
}
remDr$findElement("id", savebtn[2])$clickElement()
Sys.sleep(1)
remDr$findElements("class name", "w-box-header")[[2]]$clickElement()
#3 cycling
if(remDr$findElements("class name", "w-box-content")[[6]]$getElementText()[[1]] == ""){
remDr$findElements("class name", "w-box-header")[[6]]$clickElement()
}
for (j in 26:32){
Sys.sleep(.5)
remDr$findElements("class name", "checkbox")[[j]]$clickElement()
}
remDr$findElement("id", savebtn[6])$clickElement()
Sys.sleep(1)
remDr$findElements("class name", "w-box-header")[[6]]$clickElement()
#now add other acitivities myself
remDr$navigate("https://log.finalsurge.com/Activities.cshtml")
#this can of course be adjusted
sports <- c("RSC Fitness (vrij sporten)", "RSC Cursus", "RSC Ticketuur", "Roeien", "Tennis/Squash", "Klimmen/Boulderen", "Vechtsport", "Overige sport")
for (j in unique(sports)){
Sys.sleep(.5)
remDr$findElement("id", "ATypeName")$sendKeysToElement(list(j))
Sys.sleep(.5)
remDr$findElement("id", "saveButton")$clickElement()
}
#and subtype for 'courses'
courses <- c("Krachttraining", "Overig")
if(remDr$findElements("class", "w-box-content")[[16]]$getElementText()[[1]]==""){
remDr$findElements("class", "w-box-header")[[16]]$clickElement()
}
for (j in unique(courses)){
Sys.sleep(.5)
remDr$findElements("id", "SubTypeName")[[14]]$sendKeysToElement(list(j))
Sys.sleep(.5)
remDr$findElements("id", "saveSubType")[[14]]$clickElement()
}
#log out
Sys.sleep(.5)
remDr$findElement(using = 'link text','Uitloggen')$clickElement()
}
Link accounts to
master-account
Now that our athlete-accounts are created, we need to link them the
our coaching accounts.
send invitations
First, we send coaching invitations using our coaching account.
I saved my login credentials in my secret-folder. So, first make a
secret
-folder, in which you can store your own
credentials.
ifelse(!dir.exists("secret"), dir.create("secret"), FALSE)
#navigate to login page
remDr$navigate("https://log.finalsurge.com/login.cshtml?Assoc=&page_redirect=/")
#load master account credentials
load("./secret/master_account.R")
#login
remDr$findElement(using = "id", value = "login_name")$sendKeysToElement(list(master$email))
remDr$findElement(using = "id", value = "login_password")$sendKeysToElement(list(master$password))
remDr$findElements("class name", "btn")[[1]]$clickElement()
#invite all athletes belonging to a group/team at once
for (i in unique(accounts$groep)) {
Sys.sleep(5)
#manage athletes
remDr$navigate("https://log.finalsurge.com/CoachAthletes.cshtml")
#get emailadresses of athletes belonging to group i
emails <- accounts$email[which(accounts$groep==i)]
#send keys to element; put comma in between
#remDr$findElement("id", "AthleteEmail")$highlightElement()
remDr$findElement(using = "id", value = "AthleteEmail")$sendKeysToElement(list(
paste(emails,collapse=",\n")))
#select team
#remDr$findElement("id","AthleteTeam")$highlightElement()
Sys.sleep(1)
remDr$findElement("id","AthleteTeam")$clickElement()
#get inner hml
inner <- remDr$findElement(using = "id", value = "AthleteTeam")$getElementAttribute("innerHTML")[[1]]
inner %>%
read_html() %>% #read html
html_nodes("option") -> teams #get all option tags
#selection option corresponding to team i, using xpath
#remDr$findElement("xpath", paste0("//select[@id = 'AthleteTeam']/option[", which(grepl(i, teams)), "]"))$highlightElement()
remDr$findElement("xpath", paste0("//select[@id = 'AthleteTeam']/option[", which(grepl(i, teams)), "]"))$clickElement()
#continue
#remDr$findElement("id", "saveButton")$highlightElement()
Sys.sleep(1)
remDr$findElement("id", "saveButton")$clickElement()
Sys.sleep(2)
#invite
#remDr$findElement("id", "multiButton")$highlightElement()
remDr$findElement("id", "multiButton")$clickElement()
}
activate social
walls
Now that all athletes are assigned to (invited for) a team, we
activate a Social Wall for teams that are assigned the experimental
condition.
This must be done via the beta-platform of FSurge.
#navigate to the beta-platform
remDr$findElements("class name", "ptip_s")[[9]]$clickElement()
#navigate to the social wall page
remDr$navigate("https://beta.finalsurge.com/social-wall")
for (i in unique(accounts$groep)) { # for groups/teams
#that were assigned the social wall condition
if(accounts$socialwall[accounts$groep==i][1]==1) {
remDr$findElement("class", "rich-select__trigger")$clickElement()
Sys.sleep(.5)
remDr$findElement("class", "rich-list__action")$clickElement()
#name the wall. I just call all walls "Social wall"
#remDr$findElements("class", "fs-form-input")[[2]]$highlightElement()
remDr$findElements("class", "fs-form-input")[[2]]$sendKeysToElement(list("Social Wall"))
#add team
Sys.sleep(.5)
#remDr$findElements("class", "rich-select__trigger")[[2]]$highlightElement()
remDr$findElements("class", "rich-select__trigger")[[2]]$clickElement()
#this is rather complicated... i extract inner html of element of class "el-popover",
#but since the id changes each iteration (don't know why...), first extract this id
inner <- remDr$findElement("class", "fs-modal__content")$getElementAttribute("innerHTML")[[1]]
inner %>%
str_split("el-popover") %>%
.[[1]] %>%
.[length(.)] %>%
str_split(" ") %>%
.[[1]] %>%
.[1] %>%
substr(2,nchar(.)-1) -> id
#now get inner html of element with id el-popover-'id'
inner <-remDr$findElement("id", paste0("el-popover-",id))$getElementAttribute("innerHTML")[[1]]
#extract options (teams); based on item-text
inner %>%
str_split( "rich-list-item__text") %>%
.[[1]] -> options
#get indicator of item-text that corresponds to team-name of i
ind <- which(grepl(i,options))
#click checkbox;
#note the "minus 1" (because the "all teams"-checkbox is not a rich-list-item__checkbox)...
#remDr$findElements("class", "rich-list-item__checkbox")[[ind-1]]$highlightElement()
remDr$findElements("class", "rich-list-item__checkbox")[[ind-1]]$clickElement()
#create wall
#there are multiple elements of class button__content.
#but the last one (thus element equal to length of list of these elements) is
#the create social wall button
Sys.sleep(.5)
list <- remDr$findElements("class", "button__border")
#remDr$findElements("class", "button__border")[[length(list)]]$highlightElement()
remDr$findElements("class", "button__border")[[length(list)]]$clickElement()
Sys.sleep(2)
}
}
#DONE! now log out
#remDr$findElement("class", "profile-info")$highlightElement()
remDr$findElement("class", "profile-info")$clickElement()
#remDr$findElements("class", "menu-collapsed")[[10]]$highlightElement()
remDr$findElements("class", "menu-collapsed")[[10]]$clickElement()
#there seems to be a bug: if i navigate to the 'regular'/non-beta version of FSurge, i am still logged in. so also logout via regular version
remDr$navigate("https://log.finalsurge.com/")
remDr$findElement(using = 'link text','Uitloggen')$clickElement()
remDr$findElement(using = 'link text','Log in account')$clickElement()
accept
invitations
As a last step, I accept the coaching invitations via the
athlete-accounts.
for (i in 1:n) {
print(paste("accepting invitation:", accounts$email[i]))
#navigate to login page
remDr$navigate("https://log.finalsurge.com/")
#login
remDr$findElement(using = "id", value = "login_name")$sendKeysToElement(list(accounts$email[i]))
remDr$findElement(using = "id", value = "login_password")$sendKeysToElement(list(accounts$paswoord[i]))
remDr$findElements("class name", "btn")[[1]]$clickElement()
Sys.sleep(1)
#navigate to invitation
remDr$navigate(paste0("https://log.finalsurge.com/", "invitation.cshtml"))
#accept request
Sys.sleep(.5)
#remDr$findElements("class", "btn")[[1]]$highlightElement()
remDr$findElements("class", "btn")[[1]]$clickElement()
Sys.sleep(.5)
#log out
#remDr$findElement(using = 'link text','Uitloggen')$highlightElement()
remDr$findElement(using = 'link text','Uitloggen')$clickElement()
}
Personalize
accounts
Recruited participants fill out a short (pre-intervention) survey, in
which they give - among others - their first and last name. We use these
data to ‘personalize’ the accounts.
Setup: at the end of each recruitment day (and the week after
recruitment):
- load (updated) pre-set
accounts
df
- load (updated) pre-intervention survey responses
- get all of today’s responses (i.e., completed before midnight)
- for each participant, ‘personalize’ the account using first and
lastname
- update (and
fsave
) the accounts
df by
setting occupied=TRUE
, allowing me to: a. assign new
participants to non-occupied accounts and b. identify accounts that need
to be looped over for the experiment. I also attach participants’ email
to the dataframe, so that I can link FSurge data with survey (and
behavioral trace) data.
#1 load newest version of accounts df
#with occupied=TRUE for pre-set accounts that are personalized/ready for use.
#if newest version was saved yesterday, this works:
#accounts <- fload(paste0("./local/", substr(gsub("[:-]", "", Sys.Date()-1), 1, 8), "accounts.R"))
# or just: load("./local/accounts.R") #if I save the updated version with the base-R save function.
accounts <- fload("./local/accounts.R")
fix(accounts)
#2 load (most recent) pre-intervention responses
#a new version/export should be copied to the local folder each day/iteration...
df <- read.csv("./local/voormeting/results-survey119535.csv")
#these are *complete* responses!
#3 get today's responses (bc previous responses are already handled!)
df <- df[which(as.Date(df$submitdate)==Sys.Date()),]
#or last few... df <- tail(df,X)
#also remove 'test' responses, which were indicated with firstname 'test'
df <- df[which(!tolower(df$emails.3.)=="test"),]
#4 personalize accounts for these respondents
#5 update accounts df by setting occupied=TRUE
#preparation:
#make groups names in survey df resemble those of the 'accounts' df
df$S0 <- ifelse(df$S0 == "Krachttraining basis beginner - maandag (17:45-19:00)", "Krachttraining basis - maandag",
ifelse(df$S0 == "Krachttraining sportspecifiek - maandag (21:00-22:15)", "Krachttraining sportspecifiek - maandag",
ifelse(df$S0 == "Krachttraining basis beginner - woensdag (20:30-21:45)","Krachttraining basis - woensdag",
ifelse(df$S0 == "Krachttraining vrouwen - woensdag (18:00-19:15)", "Krachttraining vrouwen - woensdag",
ifelse(df$S0 == "Krachttraining vrouwen - donderdag (15:45-17:00)", "Krachttraining vrouwen - donderdag", NA)))))
#also make sure Rselenium browser is (still) active.
for (i in unique(df$S0)) { #for each unique group
#retrieve the survey respondents that reported to be part of this group
df_sub <- df[which(df$S0==i),]
#how many are there (ie, new ones)?
n <- nrow(df_sub)
#for each athlete j belonging to this group, in 1:n,
#'personalize' the account
for (j in 1:n) {
Sys.sleep(1)
#get personal data from survey
firstname <- df_sub$emails.3.[j]
lastname <- substr(df_sub$emails.4.[j],1,1)
email <- df_sub$emails.1.[j]
Sys.sleep(1)
#navigate log in page
remDr$navigate("https://log.finalsurge.com/login.cshtml")
#this respondent gets assigned the first entry from the `accounts` df, with group==i & occupied==F)
entry <- accounts[which(accounts$groep==i & accounts$occupied==F)[1],]
#login with the corresponding account
remDr$findElement(using = "id", value = "login_name")$sendKeysToElement(list(entry$email))
remDr$findElement(using = "id", value = "login_password")$sendKeysToElement(list(entry$paswoord))
remDr$findElements("class name", "btn")[[1]]$clickElement()
#navigate edit settings
remDr$navigate("https://log.finalsurge.com/UserProfile.cshtml?edit=p#profile")
#remDr$findElement("id", "fname")$highlightElement()
remDr$findElement("id", "fname")$clearElement()
Sys.sleep(1)
remDr$findElement("id", "fname")$sendKeysToElement(list(firstname))
Sys.sleep(.5)
remDr$findElement("id", "lname")$clearElement()
Sys.sleep(1)
remDr$findElement("id", "lname")$sendKeysToElement(list(lastname, key="enter"))
Sys.sleep(2)
#logout
remDr$findElement(using = 'link text','Uitloggen')$clickElement()
#account is now occupied, so set to TRUE (so that it will be skipped for the next respondent)
#accounts[which(accounts$groep==i & accounts$occupied==F)[1],]
#but first add the email adress of the corresponding pariticpants, as an identifier
accounts$id[which(accounts$groep==i & accounts$occupied==F)[1]] <- email
accounts$occupied[which(accounts$groep==i & accounts$occupied==F)[1]] <- TRUE
}
}
fix(accounts)
#and make sure to save the accounts df as a new version
fsave(accounts,"accounts.R")
#and/or just as `accounts.R`, bc then it feeds into the socialwall updater...?
save(accounts,file="./local/accounts.R")
LS0tDQp0aXRsZTogIlNldCB1cCBGU3VyZ2UiDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY3NzOiB0d2Vha3MuY3NzDQogICAgdG9jOiAgdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMg0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KGtuaXRyKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KYGBgDQoNCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KLS0tICANCg0KU2NyaXB0cyBvbiB0aGlzIHBhZ2UgYWxsb3cgdXNlcnMgdG86DQoNCjEuIFtDcmVhdGVdKCNjcmVhdGUpIGEgbnVtYmVyIG9mIEZTdXJnZSBhdGhsZXRlIGFjY291bnRzIGFuZCBhc3NpZ24gdGhlbSBhIHRlYW0gYW5kIGFuIGV4cGVyaW1lbnRhbCBjb25kaXRpb24uIA0KMi4gW0xpbmtdKCNsaW5rKSB0aGVtIHRvIGEgbWFzdGVyIG9yIGNvYWNoIGFjY291bnQgKGkuZS4sIHNlbmRpbmcgY29hY2hpbmcgaW52aXRhdGlvbnMgdmlhIHRoZSBtYXN0ZXItYWNjb3VudCBhbmQgYWNjZXB0aW5nIHRoZW0gdmlhIHRoZSBhdGhsZXRlLWFjY291bnRzKSBhbmQgYWN0aXZhdGUgU29jaWFsIFdhbGxzLg0KMy4gW1BlcnNvbmFsaXplXSgjcGVycykgdGhlIGFjY291bnRzIChpLmUuLCBhZGp1c3RpbmcgYXRobGV0ZSBuYW1lKQ0KNC4gW1JlbW92ZV0oI3JlbW92ZSkgYWNjb3VudHMgZnJvbSBjb2FjaGluZy1hY2NvdW50IC8gZGVsZXRlIGF0aGxldGUtYWNjb3VudHMuIA0KDQo8YnI+DQoNCiMgUHJlcGFyYXRpb24NCg0KVGhpcyB0b29sIHVzZXMgYXV0b21hdGVkIHdlYmJyb3dzaW5nIHVzaW5nIGBSU2VsZW5pdW1gLg0KV2UgZmlyc3QgbmVlZCB0byBpbnN0YWxsIHRoaXMgcGFja2FnZSwgYWxvbmcgd2l0aCBzb21lIG90aGVyIHBhY2thZ2VzOg0KDQotIGBuZXRzdGF0YCBhbmQgYHBpbmdyYDogZmluZCBwb3J0IGluZm9ybWF0aW9uDQotIGBydmVzdGA6IGhhcnZlc3Qgd2VicGFnZXMNCi0gYHRpZHl2ZXJzZWA6IGRhdGEgd3JhbmdsaW5nDQotIGBzdHJpbmdyYDogc3RyaW5nIG9wZXJhdGlvbnMNCg0KDQo8YnI+DQoNCiMjIGNsZWFuIHVwDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQpybShsaXN0PWxzKCkpDQpnYygpDQpgYGANCg0KPGJyPg0KDQojIyBnZW5lcmFsIGN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFINCi0gYGZnZW5lcmF0ZXBhc3NgOiBQYXNzd29yZCBnZW5lcmF0b3IgZnVuY3Rpb24NCi0gYGZzYXZlYDogc2F2ZSBkYXRhIHdpdGggdGltZSBzdGFtcCBpbiBjb3JyZWN0IGRpcmVjdG9yeQ0KLSBgZmxvYWRgOiBsb2FkIFItb2JqZWN0cyB1bmRlciBuZXcgbmFtZXMNCg0KYGBge3J9DQpmcGFja2FnZS5jaGVjayA8LSBmdW5jdGlvbihwYWNrYWdlcykgew0KICAgIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBpZiAoIXJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgew0KICAgICAgICAgICAgaW5zdGFsbC5wYWNrYWdlcyh4LCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgICAgICAgICAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQogICAgICAgIH0NCiAgICB9KQ0KfQ0KDQpmZ2VuZXJhdGVwYXNzIDwtIGZ1bmN0aW9uKG5jaGFyPTcsIG51cHBlcj0xLCBuc3BlY2lhbD0xLCBubnVtYmVyPTIpIHsNCiAgc3BlY2lhbCA8LSBjKCIhIiwiQCIsIiMiLCIkIiwiJSIsIl4iLCImIiwiKiIsIigiLCIpIiwiXyIsIisiKQ0KICBudW1iZXIgPC0gYygxOjkpDQogIGNoYXJzIDwtIGMoc2FtcGxlKHNwZWNpYWwsbnNwZWNpYWwscmVwbGFjZT1UKSwNCiAgICAgICAgICAgICBzYW1wbGUobnVtYmVyLG5udW1iZXIscmVwbGFjZT1UKSwNCiAgICAgICAgICAgICBzYW1wbGUoTEVUVEVSUyxudXBwZXIscmVwbGFjZT1UKSwNCiAgICAgICAgICAgICBzYW1wbGUobGV0dGVycyxuY2hhci0obnVwcGVyK25zcGVjaWFsK25udW1iZXIpLA0KICAgICAgICAgICAgICAgICAgICByZXBsYWNlPVQpKQ0KICBwYXNzIDwtIHBhc3RlMChzYW1wbGUoY2hhcnMpLGNvbGxhcHNlPSIiKQ0KICByZXR1cm4ocGFzcykNCn0NCg0KZnNhdmUgPC0gZnVuY3Rpb24oeCwgZmlsZSwgbG9jYXRpb24gPSAiLi9sb2NhbC8iLCAuLi4pIHsNCiAgICBpZiAoIWRpci5leGlzdHMobG9jYXRpb24pKQ0KICAgICAgICBkaXIuY3JlYXRlKGxvY2F0aW9uKQ0KICAgIGRhdGVuYW1lIDwtIHN1YnN0cihnc3ViKCJbOi1dIiwgIiIsIFN5cy50aW1lKCkpLCAxLCA4KQ0KICAgIHRvdGFsbmFtZSA8LSBwYXN0ZShsb2NhdGlvbiwgZGF0ZW5hbWUsIGZpbGUsIHNlcCA9ICIiKQ0KICAgIHByaW50KHBhc3RlKCJTQVZFRDogIiwgdG90YWxuYW1lLCBzZXAgPSAiIikpDQogICAgc2F2ZSh4LCBmaWxlID0gdG90YWxuYW1lKQ0KfQ0KDQoNCmZsb2FkICA8LSBmdW5jdGlvbihmaWxlTmFtZSl7DQogIGxvYWQoZmlsZU5hbWUpDQogIGdldChscygpW2xzKCkgIT0gImZpbGVOYW1lIl0pDQp9DQpgYGANCg0KPGJyPg0KDQojIyBuZWNlc3NhcnkgcGFja2FnZXMNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGFja2FnZXMgPSBjKCJSU2VsZW5pdW0iLCAicnZlc3QiLCAidGlkeXZlcnNlIiwgIm5ldHN0YXQiLCAicGluZ3IiLCAic3RyaW5nciIpDQpmcGFja2FnZS5jaGVjayhwYWNrYWdlcykNCmBgYA0KDQo8YnI+DQoNCiMjIFZQTg0KDQpJdCBtYXkgYmUgaW1wb3J0YW50IHRvIHVzZSBhIFZQTi4gTWFrZSBzdXJlIHRvIHJlZ3VsYXJseSBjaGFuZ2UgeW91ciBwdWJsaWMgSVAgYWRyZXNzLiBJIHVzZWQgW0hNQV0oaHR0cHM6Ly93d3cuaGlkZW15YXNzLmNvbS9lbi11cy9pbmRleCkuDQoNCjxicj4NCg0KIyMgc2V0IHVwIFJTZWxlbml1bQ0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiNmaW5kIGEgZnJlZSBwb3J0DQpwb3J0IDwtIG5ldHN0YXQ6OmZyZWVfcG9ydChyYW5kb209VFJVRSkNCg0KI3BpbmcgcG9ydCBmb3IgY29uZmlybWF0aW9uDQpwaW5ncjo6cGluZ19wb3J0KCJsb2NhbGhvc3QiLCBwb3J0KQ0KDQojc2V0IHVwIHNlbGVuaXVtIHNlcnZlciBhbmQgYnJvd3Nlcg0KckQgPC0gcnNEcml2ZXIoYnJvd3Nlcj0iZmlyZWZveCIsIHBvcnQ9cG9ydCwgY2hyb21ldmVyPU5VTEwsIHZlcmJvc2U9RikNCnJlbURyIDwtIHJEW1siY2xpZW50Il1dDQpgYGAgDQoNCjxicj4NCg0KKipOT1RFKio6IGFmdGVyIHVzaW5nIGBSU2VsZW5pdW06OnJzRHJpdmVyYCBmb3IgYSB3aGlsZSB3aXRob3V0IGlzc3VlLCBJIHJlY2VudGx5IGdvdCBlcnJvciBtZXNzYWdlcy4gVGhlIHJvb3QgY2F1c2Ugb2YgdGhlIHByb2JsZW0gaXMgdGhhdCBgcnNEcml2ZXIoKWAgaXMgY29uZmlndXJlZCB0byBsYXVuY2ggdGhlIFNlbGVuaXVtIHNlcnZlciB3aXRoIHRoZSBsYXRlc3QgdmVyc2lvbnMgb2YgQ2hyb21lICphbmQqIEZpcmVmb3ggZHJpdmVycywgKmV2ZW4gaWYgeW91IGhhdmUgc3BlY2lmaWVkIHRoZSBvdGhlciBicm93c2VyIHRvIHJ1biouIFRvIHNvbHZlIHRoaXMsIGp1c3QgcGFzcyBpbiBgTlVMTGAgaW4gdGhlIENocm9tZSBkcml2ZXIgdmVyc2lvbiBgY2hyb21ldmVyYC4uLiAoc2VlIGFsc28gW3RoaXNdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzUxMjU2NTc4L3JzZWxlbml1bS11bmFibGUtdG8tY3JlYXRlLW5ldy1zZXJ2aWNlLWNocm9tZWRyaXZlcnNlcnZpY2UpIGFuZCBbdGhpc10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDUzOTU4NDkvY2FudC1leGVjdXRlLXJzZHJpdmVyLWNvbm5lY3Rpb24tcmVmdXNlZCkgc3RhY2tvdmVyZmxvdyBwb3N0KS4NCg0KDQpJZiBhdCBhbnkgdGltZSB5b3Ugd2lzaCB0byB0ZXJtaW5hdGUgdGhlIHByb2Nlc3MsIHVzZSB0aGUgY29kZToNCmBgYHtyLCBldmFsPUZ9DQpwaWQgPC0gckQkc2VydmVyJHByb2Nlc3MkZ2V0X3BpZCgpI2dldCBwcm9jZXNzIGlkDQpzeXN0ZW0ocGFzdGUwKCJUYXNra2lsbCAvRiAvVCIgLCIgL1BJRCAiLCBwaWQpKQ0KYGBgDQoNCi0tLS0NCg0KPGJyPg0KDQojIENyZWF0ZSBhY2NvdW50cyB7I2NyZWF0ZX0NCg0KV2UgY3JlYXRlIGEgZGF0YWZyYW1lIG9mIGFjY291bnRzIHRvIGJlIGNyZWF0ZWQ6DQoNCi0gbmFtZXMgIA0KLSBlbWFpbCBhZHJlc3Nlcw0KLSBwYXNzd29yZHMgZ2VuZXJhdGVkIHVzaW5nIGBmZ2VuZXJhdGVwYXNzYA0KLSBhc3NpZ24gYWNjb3VudHMgdG8gYSB0cmFpbmluZyBncm91cA0KLSBhc3NpZ24gZ3JvdXBzIHRvIGVpdGhlciBhIGNvbnRyb2wgb3IgZXhwZXJpbWVudGFsL2ludGVydmVudGlvbiBjb25kaXRpb24NCg0KSGVyZSwgd2UgY3JlYXRlIDUgZ3JvdXBzIG9mIDE1IGFjY291bnRzICh0aHVzLCA3NSBhY2NvdW50cykNCg0KYGBge3IsIGV2YWw9Rn0NCm49NzUgI2FjY291bnRzDQpOPTUgI2dyb3Vwcw0KDQojbWFrZSBkZiwgYXRobGV0ZSBhY2NvdW50cyBhc3NpZ25lZCB0byBncm91cHMNCmFjY291bnRzIDwtIGRhdGEuZnJhbWUodm9vcm5hYW09IkEiLCAjYXJiaXJhcnkuLi4gQSBmb3IgYXRobGV0ZQ0KICAgICAgICAgICAgICAgICAgICAgICBhY2h0ZXJuYWFtPWFzLmNoYXJhY3RlcihjKDE6bikpLCAjaW5kaWNhdG9yDQogICAgICAgICAgICAgICAgICAgICAgIGVtYWlsPU5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgcGFzd29vcmQ9TkEsDQogICAgICAgICAgICAgICAgICAgICAgIGdyb2VwPXJlcCgxOk4sZWFjaD1uL04pKQ0KDQojZ3JvdXBzIGNhbiBiZSBnaXZlbiBuYW1lcywgaW5zdGVhZCBvZiBudW1iZXJzLi4uDQojaSBuYW1lIHRoZSBncm91cHMgYmFzZWQgb24gdGhlIGNvdXJzZSBuYW1lLA0KI2kgc2F2ZWQgdGhpcyBpbiBhIHNlY3JldCBmb2xkZXINCmxvYWQoIi4vc2VjcmV0L2NvdXJzZV9uYW1lcy5SIikNCmFjY291bnRzJGdyb2VwIDwtIGlmZWxzZShhY2NvdW50cyRncm9lcD09MSwgY291cnNlc1sxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYWNjb3VudHMkZ3JvZXA9PTIsIGNvdXJzZXNbMl0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShhY2NvdW50cyRncm9lcD09MywgY291cnNlc1szXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShhY2NvdW50cyRncm9lcD09NCwgY291cnNlc1s0XSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYWNjb3VudHMkZ3JvZXA9PTUsIGNvdXJzZXNbNV0sIE5BKSkpKSkNCg0KI2NyZWF0ZSBlbWFpbCBhbmQgcGFzc3dvcmQNCmZvciAoaSBpbiAxOm4pIHsNCiAgI2NyZWF0ZSBlbWFpbCBhbmQgcGFzc3dvcmQNCiAgYWNjb3VudHMkZW1haWxbaV0gPC0gcGFzdGUwKCJhdGxlZXQiLGksIkByc2MubmwiKQ0KICBhY2NvdW50cyRwYXN3b29yZFtpXSA8LSBmZ2VuZXJhdGVwYXNzKDcpDQp9DQoNCiNhbHNvLCByYW5kb21seSBhc3NpZ24gYSBudW1iZXIgb2YgZ3JvdXBzIGEgc29jaWFsIHdhbGwuDQpuX3NvY3dhbGwgPC0gMyAjaGVyZSwgMw0KYWNjb3VudHMkc29jaWFsd2FsbCA8LSBOQQ0KI3NhbXBsZSAzIGdyb3VwcyB0aGF0IHdpbGwgYmUgYXNzaWduZWQgYSBzb2NpYWwgd2FsbA0Kc29jd2FsbF9ncm91cCA8LSBzYW1wbGUodW5pcXVlKGFjY291bnRzJGdyb2VwKSxuX3NvY3dhbGwpDQphY2NvdW50cyRzb2NpYWx3YWxsIDwtIGlmZWxzZShhY2NvdW50cyRncm9lcCAlaW4lIHNvY3dhbGxfZ3JvdXAsIDEsMCkNCg0KI3dlIGFsc28gYWRkIGEgdmFyaWFibGUgJ29jY3VwaWVkJywgd2hpY2ggaW5kaWNhdGVzIHdoZXRoZXIgYSBwcmUtc2V0IGFjY291bnQNCiNpcyBjdXN0b21pemVkIChpLmUuLCBmaXJzdCBhbmQgbGFzdG5hbWUgYXJlIGFzc2lnbmVkKSBhbmQgcmVhZHkgZm9yIHVzZS4NCmFjY291bnRzJG9jY3VwaWVkIDwtIEYNCiNhbmQgYSB2YXJpYWJsZSBpZCB3aGljaCBkZW5vdGVzIHBhcnRpY2lwYW50cyBlbWFpbCBhZHJlc3MsIHRvIHVsdGltYXRlbHkgbGluayBGU3VyZ2UgZGF0YSB3aXRoIHN1cnZleSAoYW5kIGJlaGF2aW9yKSBkYXRhDQphY2NvdW50cyRpZCA8LSBOQQ0KDQojc2F2ZSB0byBsb2NhbCBmb2xkZXIgdXNpbmcgZnNhdmUNCiMjIyMjIyNmc2F2ZShhY2NvdW50cywgImFjY291bnRzLlIiKQ0KYGBgIA0KDQo8YnI+DQoNCk5leHQgdXAsIHdlIHVzZSBSU2VsZW5pdW0gdG8gY3JlYXRlIHRoZSBhY2NvdW50cy4NCg0KV2UgY3VzdG9taXplIHRoZSBhY2NvdW50IHNldHRpbmdzLCBhbmQgdGhlIHdvcmtvdXQgcGFnZToNCg0KYGBge3IsIGV2YWw9Rn0NCiNsb2FkIGluIHRoZSBhY2NvdW50cw0KbG9hZCgiLi9sb2NhbC9hY2NvdW50cy5SIikNCm4gPC0gbnJvdyhhY2NvdW50cykNCg0KDQpmb3IgKGkgaW4gMTpuKSB7DQogIFN5cy5zbGVlcCgxKQ0KICBwcmludChwYXN0ZSgiY3JlYXRpbmcgYWNjb3VudDoiLCBhY2NvdW50cyRlbWFpbFtpXSkpDQogIA0KICAjbmF2aWdhdGUgdG8gY3JlYXRlIGFjY291bnQgcGFnZQ0KICByZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vcmVnaXN0ZXIuY3NodG1sP3BhZ2VfcmVkaXJlY3Q9JTJmIikNCg0KICAjaW5zZXJ0IG5hbWUNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJjcmVhdGVfZmlyc3QiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGFjY291bnRzJHZvb3JuYWFtW2ldKSkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJjcmVhdGVfbGFzdCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkYWNodGVybmFhbVtpXSkpDQoNCiAgI2FuZCBlbWFpbA0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImNyZWF0ZV9lbWFpbCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkZW1haWxbaV0pKQ0KIA0KICAjYWRqdXN0IHRpbWV6b25lDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAiY3JlYXRlX3RpbWV6b25lIikkY2xpY2tFbGVtZW50KCkNCiAgDQogICNnZXQgaW5uZXIgaHRtbA0KICBpbm5lciA8LSByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImNyZWF0ZV90aW1lem9uZSIpJGdldEVsZW1lbnRBdHRyaWJ1dGUoImlubmVySFRNTCIpW1sxXV0NCiAgaW5uZXIgJT4lDQogICAgcmVhZF9odG1sKCkgJT4lICNyZWFkIGh0bWwNCiAgICBodG1sX25vZGVzKCJvcHRpb24iKSAtPiBvcHRpb25zICNnZXQgYWxsIG9wdGlvbiB0YWdzDQoNCiAgI3BpY2sgKEdNVCswMTowMCkgQW1zdGVyZGFtLCBCZXJsaW4sIC4uLg0KICByZW1EciRmaW5kRWxlbWVudHModXNpbmcgPSAidGFnIG5hbWUiLCB2YWx1ZSA9Im9wdGlvbiIpW1t3aGljaChncmVwbCgiQW1zdGVyZGFtIiwgb3B0aW9ucykpXV0kY2xpY2tFbGVtZW50KCkNCiAgDQogICNpbnNlcnQgcGFzc3dvcmQNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJwYXNzd29yZF9tZXRlciIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkcGFzd29vcmRbaV0pKQ0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImNyZWF0ZV9wYXNzd29yZG1hdGNoIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChhY2NvdW50cyRwYXN3b29yZFtpXSkpDQogIA0KICAjY3JlYXRlIGFjY291bnQNCiAgcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIG5hbWUiLCAiYnRuIikkY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKDEpDQogIA0KICAjYWRqdXN0IHVzZXIgc2V0dGluZ3MNCiAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1VzZXJQcm9maWxlLmNzaHRtbD9lZGl0PXMjc2V0dGluZ3MiKQ0KIA0KICAgI3NlbGVjdCBsYW5ndWFnZTogRHV0Y2gNCiAgcmVtRHIkZmluZEVsZW1lbnRzKHVzaW5nID0gImNsYXNzIG5hbWUiLCB2YWx1ZSA9ICJyYWRpbyIpW1s1XV0kY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKC41KQ0KICANCiAgI2NvbnZlcnQgdG8gbWV0cmljIHN5c3RlbQ0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gIlVuaXRNZXRyaWMiKSRjbGlja0VsZW1lbnQoKQ0KICANCiAgIzI0IGhvdXJzIGRpc3BsYXkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJURGlzcGxheTI0IikkY2xpY2tFbGVtZW50KCkNCiAgDQogICN3ZWVrIHN0YXJ0IG9uIG1vbmRheQ0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gIldTTSIpJGNsaWNrRWxlbWVudCgpDQogIA0KICAjc2F2ZQ0KICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAic2F2ZUJ1dHRvblNldHRpbmdzIikkY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKDEpDQogIA0KICAjSSBhbHNvIHVzZSB0aGUgd29ya291dCBjdXN0b21pemF0aW9uIHBhZ2UgdG8gYWRqdXN0IHdoYXQgYWN0aXZpdHkgdHlwZXMgYXRobGV0ZXMgY2FuIHVwbG9hZC4gbm93IGl0IGlzIHRvbyBsYWJvcmlvdXMsIGFuZCB0aGVyZSBhcmUgd2F5IHRvIG1hbnkgc3VidHlwZXMgb2YgYWN0aXZpdGllcy4NCiAgI25vdGU6IGFwcGFyZW50bHksIGkgY2FuIG9ubHkgZWRpdC9kZWxldGUgdGhlIHN1YnR5cGVzLg0KICAjdGhlIGFjdGl2aXR5IHNlY3Rpb25zL2ZpZWxkcyAoZWcsIHBsYW5uZWQgZGlzdGFuY2UsIGhlYXJ0IHJhdGUsIGV0Yy4pIGNhbiBiZSBoaWRkZW4sIGJ1dCB0aGlzIGRvZXMgbm90IHRyYW5zbGF0ZSB0byB0aGUgYXBwLi4uDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Xb3Jrb3V0Q3VzdG9taXplLmNzaHRtbCIpDQogIA0KICAjaSBqdXN0IGhpZGUgbW9zdCBwcmUtY29kZWQgYWN0aXZpdHkgdHlwZXMuLi4gYW5kIGp1c3QgYWRkIGFsbCBhY3Rpdml0eS10eXBlcyB2aWEgY3VzdG9taXphdGlvbg0KICAjaSBuZWVkIHRvIG9wZW4gdGhlIGJveCBvZiBhY3Rpdml0eSB0eXBlIGogKGlmIG5vdCBhbHJlYWR5IG9wZW5lZCkNCiAgI2FuZCBzZWxlY3QgdGhlICdoaWRlIGFjdGl2aXR5IHR5cGUnLWNoZWNrYm94Lg0KICAjYW5kIHNhdmUNCiAgI2ZpcnN0LCBpIHB1dCB0aGUgaWRzIG9mIHRoZXNlIGNoZWNrYm94ZXMsIGFuZCBzYXZlYnV0dG9ucyBpbiB2ZWN0b3JzDQogIA0KICBoaWRlYm94IDwtIGMoMSwxMSwxOCwxOSwyMCwyNSwzMywzNCwzNSw0MCkNCiAgc2F2ZWJ0biA8LSBjKCJzYXZlQnV0dG9uXzAwMDAwMDAxLTAwMDEtMDAwMS0wMDAxLTAwMDAwMDAwMDAwMSIsDQogICAgICAgICAgICAgICAic2F2ZUJ1dHRvbl8wMDAwMDAwMy0wMDAzLTAwMDMtMDAwMy0wMDAwMDAwMDAwMDMiLA0KICAgICAgICAgICAgICAgInNhdmVCdXR0b25fMDAwMDAwMTAtMDAxMC0wMDEwLTAwMTAtMDAwMDAwMDAwMDEwIiwNCiAgICAgICAgICAgICAgICJzYXZlQnV0dG9uXzAwMDAwMDA1LTAwMDUtMDAwNS0wMDA1LTAwMDAwMDAwMDAwNSIsDQogICAgICAgICAgICAgICAic2F2ZUJ1dHRvbl8wMDAwMDAwOC0wMDA4LTAwMDgtMDAwOC0wMDAwMDAwMDAwMDgiLA0KICAgICAgICAgICAgICAgInNhdmVCdXR0b25fMDAwMDAwMDItMDAwMi0wMDAyLTAwMDItMDAwMDAwMDAwMDAyIiwNCiAgICAgICAgICAgICAgICJzYXZlQnV0dG9uXzAwMDAwMDA0LTAwMDQtMDAwNC0wMDA0LTAwMDAwMDAwMDAwNCIsDQogICAgICAgICAgICAgICAic2F2ZUJ1dHRvbl8wMDAwMDAwNi0wMDA2LTAwMDYtMDAwNi0wMDAwMDAwMDAwMDYiLA0KICAgICAgICAgICAgICAgInNhdmVCdXR0b25fMDAwMDAwMDctMDAwNy0wMDA3LTAwMDctMDAwMDAwMDAwMDA3IiwNCiAgICAgICAgICAgICAgICJzYXZlQnV0dG9uXzAwMDAwMDA5LTAwMDktMDAwOS0wMDA5LTAwMDAwMDAwMDAwOSIpDQogICAgICAgICAgDQogIGZvciAoaiBpbiBjKDQsNSw3LDgsOSwxMCkpeyAjZm9yIHByZS1zZXQgYWN0aXZpdHkgdHlwZXMgDQogICAgIyhidXQga2VlcCBydW5uaW5nLCBzd2ltbWluZywgd2Fsa2luZywgY3ljbGluZzsgYmVjYXVzZSBvZiB0aGUgbmljZSBzeW1ib2whKQ0KICAgIFN5cy5zbGVlcCgxKQ0KICAgICNvcGVuIGJveCBvZiBhY3Rpdml0eSB0eXBlIGogKGlmIG5vdCBhbHJlYWR5IG9wZW5lZCkNCiAgICBpZihyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtY29udGVudCIpW1tqXV0kZ2V0RWxlbWVudFRleHQoKVtbMV1dID09ICIiKXsNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJ3LWJveC1oZWFkZXIiKVtbal1dJGNsaWNrRWxlbWVudCgpICB9DQogICAgI2NsaWNrIGhpZGUtY2hlY2tib3gNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiY2hlY2tib3giKVtbaGlkZWJveFtqXV1dJGNsaWNrRWxlbWVudCgpDQogICAgI3NhdmUNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgc2F2ZWJ0bltqXSkkY2xpY2tFbGVtZW50KCkNCiAgICAjY2xvc2UgYm94DQogICAgU3lzLnNsZWVwKC41KQ0KICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJ3LWJveC1oZWFkZXIiKVtbal1dJGNsaWNrRWxlbWVudCgpIA0KICB9DQogIFN5cy5zbGVlcCgxKQ0KIA0KICAjZm9yIHJ1bm5pbmcsIHN3aW1taW5nLCBjeWNsaW5nLCBoaWRlIHN1YnR5cGUgZmllbGRzDQogICMxIHJ1bm5pbmcNCiAgaWYocmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInctYm94LWNvbnRlbnQiKVtbMV1dJGdldEVsZW1lbnRUZXh0KClbWzFdXSA9PSAiIil7DQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInctYm94LWhlYWRlciIpW1sxXV0kY2xpY2tFbGVtZW50KCkgIA0KICB9DQogICNzZWxlY3QgYWxsIHN1YnR5cGVzLCBzbyB0aGF0IHRoZXkgd2lsbCBiZSBoaWRkZW4NCiAgZm9yIChqIGluIDI6MTApew0KICAgIFN5cy5zbGVlcCguNSkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiY2hlY2tib3giKVtbal1dJGNsaWNrRWxlbWVudCgpDQogIH0NCiAgI3NhdmUgYW5kIGhpZGUNCiAgU3lzLnNsZWVwKC41KQ0KICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCBzYXZlYnRuWzFdKSRjbGlja0VsZW1lbnQoKQ0KICBTeXMuc2xlZXAoLjUpDQogIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJ3LWJveC1oZWFkZXIiKVtbMV1dJGNsaWNrRWxlbWVudCgpDQogIA0KICAjMiBzd2ltbWluZw0KICBpZihyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtY29udGVudCIpW1syXV0kZ2V0RWxlbWVudFRleHQoKVtbMV1dID09ICIiKXsNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzJdXSRjbGlja0VsZW1lbnQoKSAgDQogIH0NCiAgZm9yIChqIGluIDEyOjE3KXsNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImNoZWNrYm94IilbW2pdXSRjbGlja0VsZW1lbnQoKQ0KICB9DQogIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsIHNhdmVidG5bMl0pJGNsaWNrRWxlbWVudCgpDQogIFN5cy5zbGVlcCgxKQ0KICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzJdXSRjbGlja0VsZW1lbnQoKQ0KICANCiAgIzMgY3ljbGluZw0KICBpZihyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtY29udGVudCIpW1s2XV0kZ2V0RWxlbWVudFRleHQoKVtbMV1dID09ICIiKXsNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzZdXSRjbGlja0VsZW1lbnQoKSAgDQogIH0NCiAgZm9yIChqIGluIDI2OjMyKXsNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImNoZWNrYm94IilbW2pdXSRjbGlja0VsZW1lbnQoKQ0KICB9DQogIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsIHNhdmVidG5bNl0pJGNsaWNrRWxlbWVudCgpDQogIFN5cy5zbGVlcCgxKQ0KICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzZdXSRjbGlja0VsZW1lbnQoKQ0KICANCiAgI25vdyBhZGQgb3RoZXIgYWNpdGl2aXRpZXMgbXlzZWxmDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9BY3Rpdml0aWVzLmNzaHRtbCIpDQogIA0KICAjdGhpcyBjYW4gb2YgY291cnNlIGJlIGFkanVzdGVkDQogIHNwb3J0cyA8LSBjKCJSU0MgRml0bmVzcyAodnJpaiBzcG9ydGVuKSIsICJSU0MgQ3Vyc3VzIiwgIlJTQyBUaWNrZXR1dXIiLCAiUm9laWVuIiwgIlRlbm5pcy9TcXVhc2giLCAiS2xpbW1lbi9Cb3VsZGVyZW4iLCAiVmVjaHRzcG9ydCIsICJPdmVyaWdlIHNwb3J0IikNCiAgDQogIGZvciAoaiBpbiB1bmlxdWUoc3BvcnRzKSl7DQogICAgU3lzLnNsZWVwKC41KQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsICJBVHlwZU5hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGopKQ0KICAgIFN5cy5zbGVlcCguNSkNCiAgICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAic2F2ZUJ1dHRvbiIpJGNsaWNrRWxlbWVudCgpDQogIH0NCiAgDQogICNhbmQgc3VidHlwZSBmb3IgJ2NvdXJzZXMnDQogIGNvdXJzZXMgPC0gYygiS3JhY2h0dHJhaW5pbmciLCAiT3ZlcmlnIikNCiAgDQogIGlmKHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAidy1ib3gtY29udGVudCIpW1sxNl1dJGdldEVsZW1lbnRUZXh0KClbWzFdXT09IiIpew0KICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAidy1ib3gtaGVhZGVyIilbWzE2XV0kY2xpY2tFbGVtZW50KCkNCiAgfQ0KICBmb3IgKGogaW4gdW5pcXVlKGNvdXJzZXMpKXsNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJpZCIsICJTdWJUeXBlTmFtZSIpW1sxNF1dJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoaikpDQogICAgU3lzLnNsZWVwKC41KQ0KICAgIHJlbURyJGZpbmRFbGVtZW50cygiaWQiLCAic2F2ZVN1YlR5cGUiKVtbMTRdXSRjbGlja0VsZW1lbnQoKQ0KICB9DQogIA0KICAjbG9nIG91dA0KICBTeXMuc2xlZXAoLjUpDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ1VpdGxvZ2dlbicpJGNsaWNrRWxlbWVudCgpDQp9DQpgYGANCg0KPGJyPg0KDQojIExpbmsgYWNjb3VudHMgdG8gbWFzdGVyLWFjY291bnQgeyNsaW5rfQ0KDQpOb3cgdGhhdCBvdXIgYXRobGV0ZS1hY2NvdW50cyBhcmUgY3JlYXRlZCwgd2UgbmVlZCB0byBsaW5rIHRoZW0gdGhlIG91ciBjb2FjaGluZyBhY2NvdW50cy4NCg0KIyMgc2VuZCBpbnZpdGF0aW9ucw0KRmlyc3QsIHdlIHNlbmQgY29hY2hpbmcgaW52aXRhdGlvbnMgdXNpbmcgb3VyIGNvYWNoaW5nIGFjY291bnQuDQoNCkkgc2F2ZWQgbXkgbG9naW4gY3JlZGVudGlhbHMgaW4gbXkgc2VjcmV0LWZvbGRlci4gU28sIGZpcnN0IG1ha2UgYSBgc2VjcmV0YC1mb2xkZXIsIGluIHdoaWNoIHlvdSBjYW4gc3RvcmUgeW91ciBvd24gY3JlZGVudGlhbHMuDQoNCmBgYHtyLCBldmFsPUZ9DQppZmVsc2UoIWRpci5leGlzdHMoInNlY3JldCIpLCBkaXIuY3JlYXRlKCJzZWNyZXQiKSwgRkFMU0UpDQpgYGANCg0KYGBge3IsIGV2YWw9Rn0NCiNuYXZpZ2F0ZSB0byBsb2dpbiBwYWdlDQpyZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vbG9naW4uY3NodG1sP0Fzc29jPSZwYWdlX3JlZGlyZWN0PS8iKQ0KDQojbG9hZCBtYXN0ZXIgYWNjb3VudCBjcmVkZW50aWFscw0KbG9hZCgiLi9zZWNyZXQvbWFzdGVyX2FjY291bnQuUiIpDQoNCiNsb2dpbg0KcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9uYW1lIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChtYXN0ZXIkZW1haWwpKQ0KcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9wYXNzd29yZCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QobWFzdGVyJHBhc3N3b3JkKSkNCnJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJidG4iKVtbMV1dJGNsaWNrRWxlbWVudCgpDQoNCiNpbnZpdGUgYWxsIGF0aGxldGVzIGJlbG9uZ2luZyB0byBhIGdyb3VwL3RlYW0gYXQgb25jZQ0KZm9yIChpIGluIHVuaXF1ZShhY2NvdW50cyRncm9lcCkpIHsgDQogIFN5cy5zbGVlcCg1KQ0KICAjbWFuYWdlIGF0aGxldGVzDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Db2FjaEF0aGxldGVzLmNzaHRtbCIpDQogIA0KICAjZ2V0IGVtYWlsYWRyZXNzZXMgb2YgYXRobGV0ZXMgYmVsb25naW5nIHRvIGdyb3VwIGkNCiAgZW1haWxzIDwtIGFjY291bnRzJGVtYWlsW3doaWNoKGFjY291bnRzJGdyb2VwPT1pKV0NCiAgDQogICNzZW5kIGtleXMgdG8gZWxlbWVudDsgcHV0IGNvbW1hIGluIGJldHdlZW4NCiAgI3JlbURyJGZpbmRFbGVtZW50KCJpZCIsICJBdGhsZXRlRW1haWwiKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJBdGhsZXRlRW1haWwiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KA0KICAgIHBhc3RlKGVtYWlscyxjb2xsYXBzZT0iLFxuIikpKQ0KDQogICNzZWxlY3QgdGVhbQ0KICAjcmVtRHIkZmluZEVsZW1lbnQoImlkIiwiQXRobGV0ZVRlYW0iKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgU3lzLnNsZWVwKDEpDQogIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsIkF0aGxldGVUZWFtIikkY2xpY2tFbGVtZW50KCkNCiAgDQogICNnZXQgaW5uZXIgaG1sDQogIGlubmVyIDwtIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAiQXRobGV0ZVRlYW0iKSRnZXRFbGVtZW50QXR0cmlidXRlKCJpbm5lckhUTUwiKVtbMV1dDQogIGlubmVyICU+JQ0KICAgIHJlYWRfaHRtbCgpICU+JSAjcmVhZCBodG1sDQogICAgaHRtbF9ub2Rlcygib3B0aW9uIikgLT4gdGVhbXMgI2dldCBhbGwgb3B0aW9uIHRhZ3MNCg0KICAjc2VsZWN0aW9uIG9wdGlvbiBjb3JyZXNwb25kaW5nIHRvIHRlYW0gaSwgdXNpbmcgeHBhdGgNCiAgI3JlbURyJGZpbmRFbGVtZW50KCJ4cGF0aCIsIHBhc3RlMCgiLy9zZWxlY3RbQGlkID0gJ0F0aGxldGVUZWFtJ10vb3B0aW9uWyIsIHdoaWNoKGdyZXBsKGksIHRlYW1zKSksICJdIikpJGhpZ2hsaWdodEVsZW1lbnQoKQ0KICByZW1EciRmaW5kRWxlbWVudCgieHBhdGgiLCBwYXN0ZTAoIi8vc2VsZWN0W0BpZCA9ICdBdGhsZXRlVGVhbSddL29wdGlvblsiLCB3aGljaChncmVwbChpLCB0ZWFtcykpLCAiXSIpKSRjbGlja0VsZW1lbnQoKQ0KICANCiAgI2NvbnRpbnVlDQogICNyZW1EciRmaW5kRWxlbWVudCgiaWQiLCAic2F2ZUJ1dHRvbiIpJGhpZ2hsaWdodEVsZW1lbnQoKQ0KICBTeXMuc2xlZXAoMSkNCiAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgInNhdmVCdXR0b24iKSRjbGlja0VsZW1lbnQoKQ0KICBTeXMuc2xlZXAoMikNCiAgDQogICNpbnZpdGUgDQogICNyZW1EciRmaW5kRWxlbWVudCgiaWQiLCAibXVsdGlCdXR0b24iKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgIm11bHRpQnV0dG9uIikkY2xpY2tFbGVtZW50KCkNCn0NCg0KYGBgDQoNCjxicj4NCg0KIyMgYWN0aXZhdGUgc29jaWFsIHdhbGxzDQpOb3cgdGhhdCBhbGwgYXRobGV0ZXMgYXJlIGFzc2lnbmVkIHRvIChpbnZpdGVkIGZvcikgYSB0ZWFtLCB3ZSBhY3RpdmF0ZSBhIFNvY2lhbCBXYWxsIGZvciB0ZWFtcyB0aGF0IGFyZSBhc3NpZ25lZCB0aGUgZXhwZXJpbWVudGFsIGNvbmRpdGlvbi4NCg0KVGhpcyBtdXN0IGJlIGRvbmUgdmlhIHRoZSBiZXRhLXBsYXRmb3JtIG9mIEZTdXJnZS4NCg0KYGBge3IsIGV2YWw9Rn0NCiNuYXZpZ2F0ZSB0byB0aGUgYmV0YS1wbGF0Zm9ybQ0KcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInB0aXBfcyIpW1s5XV0kY2xpY2tFbGVtZW50KCkNCg0KI25hdmlnYXRlIHRvIHRoZSBzb2NpYWwgd2FsbCBwYWdlDQpyZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9iZXRhLmZpbmFsc3VyZ2UuY29tL3NvY2lhbC13YWxsIikNCg0KZm9yIChpIGluIHVuaXF1ZShhY2NvdW50cyRncm9lcCkpIHsgIyBmb3IgZ3JvdXBzL3RlYW1zDQoNCiAgI3RoYXQgd2VyZSBhc3NpZ25lZCB0aGUgc29jaWFsIHdhbGwgY29uZGl0aW9uDQogIGlmKGFjY291bnRzJHNvY2lhbHdhbGxbYWNjb3VudHMkZ3JvZXA9PWldWzFdPT0xKSB7DQogICAgDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIiwgInJpY2gtc2VsZWN0X190cmlnZ2VyIikkY2xpY2tFbGVtZW50KCkNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIiwgInJpY2gtbGlzdF9fYWN0aW9uIikkY2xpY2tFbGVtZW50KCkNCiAgICANCiAgICAjbmFtZSB0aGUgd2FsbC4gSSBqdXN0IGNhbGwgYWxsIHdhbGxzICJTb2NpYWwgd2FsbCINCiAgICAjcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJmcy1mb3JtLWlucHV0IilbWzJdXSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgImZzLWZvcm0taW5wdXQiKVtbMl1dJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoIlNvY2lhbCBXYWxsIikpDQogICAgDQogICAgI2FkZCB0ZWFtDQogICAgU3lzLnNsZWVwKC41KQ0KICAgICNyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgInJpY2gtc2VsZWN0X190cmlnZ2VyIilbWzJdXSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgInJpY2gtc2VsZWN0X190cmlnZ2VyIilbWzJdXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICN0aGlzIGlzIHJhdGhlciBjb21wbGljYXRlZC4uLiBpIGV4dHJhY3QgaW5uZXIgaHRtbCBvZiBlbGVtZW50IG9mIGNsYXNzICJlbC1wb3BvdmVyIiwNCiAgICAjYnV0IHNpbmNlIHRoZSBpZCBjaGFuZ2VzIGVhY2ggaXRlcmF0aW9uIChkb24ndCBrbm93IHdoeS4uLiksIGZpcnN0IGV4dHJhY3QgdGhpcyBpZA0KICAgIGlubmVyIDwtIHJlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJmcy1tb2RhbF9fY29udGVudCIpJGdldEVsZW1lbnRBdHRyaWJ1dGUoImlubmVySFRNTCIpW1sxXV0NCiAgICBpbm5lciAlPiUNCiAgICAgIHN0cl9zcGxpdCgiZWwtcG9wb3ZlciIpICU+JQ0KICAgICAgLltbMV1dICU+JQ0KICAgICAgLltsZW5ndGgoLildICU+JQ0KICAgICAgc3RyX3NwbGl0KCIgIikgJT4lDQogICAgICAuW1sxXV0gJT4lDQogICAgICAuWzFdICU+JQ0KICAgICAgc3Vic3RyKDIsbmNoYXIoLiktMSkgLT4gaWQNCiAgICANCiAgICAjbm93IGdldCBpbm5lciBodG1sIG9mIGVsZW1lbnQgd2l0aCBpZCBlbC1wb3BvdmVyLSdpZCcNCiAgICBpbm5lciA8LXJlbURyJGZpbmRFbGVtZW50KCJpZCIsIHBhc3RlMCgiZWwtcG9wb3Zlci0iLGlkKSkkZ2V0RWxlbWVudEF0dHJpYnV0ZSgiaW5uZXJIVE1MIilbWzFdXQ0KICAgIA0KICAgICNleHRyYWN0IG9wdGlvbnMgKHRlYW1zKTsgYmFzZWQgb24gaXRlbS10ZXh0DQogICAgaW5uZXIgJT4lDQogICAgICBzdHJfc3BsaXQoICJyaWNoLWxpc3QtaXRlbV9fdGV4dCIpICU+JQ0KICAgICAgLltbMV1dIC0+IG9wdGlvbnMNCiAgICANCiAgICAjZ2V0IGluZGljYXRvciBvZiBpdGVtLXRleHQgdGhhdCBjb3JyZXNwb25kcyB0byB0ZWFtLW5hbWUgb2YgaQ0KICAgIGluZCA8LSB3aGljaChncmVwbChpLG9wdGlvbnMpKQ0KICAgIA0KICAgICNjbGljayBjaGVja2JveDsNCiAgICAjbm90ZSB0aGUgIm1pbnVzIDEiIChiZWNhdXNlIHRoZSAiYWxsIHRlYW1zIi1jaGVja2JveCBpcyBub3QgYSByaWNoLWxpc3QtaXRlbV9fY2hlY2tib3gpLi4uDQogICAgI3JlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAicmljaC1saXN0LWl0ZW1fX2NoZWNrYm94IilbW2luZC0xXV0kaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJyaWNoLWxpc3QtaXRlbV9fY2hlY2tib3giKVtbaW5kLTFdXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICNjcmVhdGUgd2FsbA0KICAgICN0aGVyZSBhcmUgbXVsdGlwbGUgZWxlbWVudHMgb2YgY2xhc3MgYnV0dG9uX19jb250ZW50Lg0KICAgICNidXQgdGhlIGxhc3Qgb25lICh0aHVzIGVsZW1lbnQgZXF1YWwgdG8gbGVuZ3RoIG9mIGxpc3Qgb2YgdGhlc2UgZWxlbWVudHMpIGlzDQogICAgI3RoZSBjcmVhdGUgc29jaWFsIHdhbGwgYnV0dG9uDQogICAgU3lzLnNsZWVwKC41KQ0KICAgIGxpc3QgPC0gcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidXR0b25fX2JvcmRlciIpDQogICAgI3JlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAiYnV0dG9uX19ib3JkZXIiKVtbbGVuZ3RoKGxpc3QpXV0kaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidXR0b25fX2JvcmRlciIpW1tsZW5ndGgobGlzdCldXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgIFN5cy5zbGVlcCgyKQ0KICB9DQp9DQoNCiNET05FISBub3cgbG9nIG91dA0KI3JlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJwcm9maWxlLWluZm8iKSRoaWdobGlnaHRFbGVtZW50KCkNCnJlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJwcm9maWxlLWluZm8iKSRjbGlja0VsZW1lbnQoKQ0KI3JlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAibWVudS1jb2xsYXBzZWQiKVtbMTBdXSRoaWdobGlnaHRFbGVtZW50KCkNCnJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAibWVudS1jb2xsYXBzZWQiKVtbMTBdXSRjbGlja0VsZW1lbnQoKQ0KDQojdGhlcmUgc2VlbXMgdG8gYmUgYSBidWc6IGlmIGkgbmF2aWdhdGUgdG8gdGhlICdyZWd1bGFyJy9ub24tYmV0YSB2ZXJzaW9uIG9mIEZTdXJnZSwgaSBhbSBzdGlsbCBsb2dnZWQgaW4uIHNvIGFsc28gbG9nb3V0IHZpYSByZWd1bGFyIHZlcnNpb24NCnJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS8iKQ0KcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnVWl0bG9nZ2VuJykkY2xpY2tFbGVtZW50KCkNCnJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ0xvZyBpbiBhY2NvdW50JykkY2xpY2tFbGVtZW50KCkNCmBgYA0KDQo8YnI+DQoNCiMjIGFjY2VwdCBpbnZpdGF0aW9ucw0KQXMgYSBsYXN0IHN0ZXAsIEkgYWNjZXB0IHRoZSBjb2FjaGluZyBpbnZpdGF0aW9ucyB2aWEgdGhlIGF0aGxldGUtYWNjb3VudHMuDQoNCmBgYHtyLCBldmFsPUZ9DQpmb3IgKGkgaW4gMTpuKSB7DQogIA0KICBwcmludChwYXN0ZSgiYWNjZXB0aW5nIGludml0YXRpb246IiwgYWNjb3VudHMkZW1haWxbaV0pKQ0KICANCiAgI25hdmlnYXRlIHRvIGxvZ2luIHBhZ2UNCiAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tLyIpDQogIA0KICAjbG9naW4NCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9uYW1lIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChhY2NvdW50cyRlbWFpbFtpXSkpDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fcGFzc3dvcmQiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGFjY291bnRzJHBhc3dvb3JkW2ldKSkNCiAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgDQogIFN5cy5zbGVlcCgxKQ0KICANCiAgI25hdmlnYXRlIHRvIGludml0YXRpb24NCiAgcmVtRHIkbmF2aWdhdGUocGFzdGUwKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS8iLCAiaW52aXRhdGlvbi5jc2h0bWwiKSkNCg0KICAjYWNjZXB0IHJlcXVlc3QNCiAgU3lzLnNsZWVwKC41KQ0KICAjcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidG4iKVtbMV1dJGhpZ2hsaWdodEVsZW1lbnQoKQ0KICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKC41KQ0KICAjbG9nIG91dA0KICAjcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnVWl0bG9nZ2VuJykkaGlnaGxpZ2h0RWxlbWVudCgpDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ1VpdGxvZ2dlbicpJGNsaWNrRWxlbWVudCgpDQoNCn0NCg0KYGBgDQoNCi0tLS0NCg0KPGJyPg0KDQojIFBlcnNvbmFsaXplIGFjY291bnRzIHsjcGVyc30NCg0KPCEtLS0NCg0KQFJGOiBkb24ndCBmb3JnZXQgdG8gdHVybiBvZiB0aGUgd2FsbC11cGRhdGVyLi4uDQoNCi0tPg0KDQpSZWNydWl0ZWQgcGFydGljaXBhbnRzIGZpbGwgb3V0IGEgc2hvcnQgKHByZS1pbnRlcnZlbnRpb24pIHN1cnZleSwgaW4gd2hpY2ggdGhleSBnaXZlIC0gYW1vbmcgb3RoZXJzIC0gdGhlaXIgZmlyc3QgYW5kIGxhc3QgbmFtZS4gV2UgdXNlIHRoZXNlIGRhdGEgdG8gJ3BlcnNvbmFsaXplJyB0aGUgYWNjb3VudHMuDQoNClNldHVwOg0KYXQgdGhlIGVuZCBvZiBlYWNoIHJlY3J1aXRtZW50IGRheSAoYW5kIHRoZSB3ZWVrIGFmdGVyIHJlY3J1aXRtZW50KToNCg0KMS4gbG9hZCAodXBkYXRlZCkgcHJlLXNldCBgYWNjb3VudHNgIGRmDQoyLiBsb2FkICh1cGRhdGVkKSBwcmUtaW50ZXJ2ZW50aW9uIHN1cnZleSByZXNwb25zZXMNCjMuIGdldCBhbGwgb2YgdG9kYXkncyByZXNwb25zZXMgKGkuZS4sIGNvbXBsZXRlZCBiZWZvcmUgbWlkbmlnaHQpDQo0LiBmb3IgZWFjaCBwYXJ0aWNpcGFudCwgJ3BlcnNvbmFsaXplJyB0aGUgYWNjb3VudCB1c2luZyBmaXJzdCBhbmQgbGFzdG5hbWUNCjUuIHVwZGF0ZSAoYW5kIGBmc2F2ZWApIHRoZSBgYWNjb3VudHNgIGRmIGJ5IHNldHRpbmcgYG9jY3VwaWVkPVRSVUVgLCBhbGxvd2luZyBtZSB0bzogYS4gYXNzaWduIG5ldyBwYXJ0aWNpcGFudHMgdG8gbm9uLW9jY3VwaWVkIGFjY291bnRzIGFuZCBiLiBpZGVudGlmeSBhY2NvdW50cyB0aGF0IG5lZWQgdG8gYmUgbG9vcGVkIG92ZXIgZm9yIHRoZSBleHBlcmltZW50LiBJIGFsc28gYXR0YWNoIHBhcnRpY2lwYW50cycgZW1haWwgdG8gdGhlIGRhdGFmcmFtZSwgc28gdGhhdCBJIGNhbiBsaW5rIEZTdXJnZSBkYXRhIHdpdGggc3VydmV5IChhbmQgYmVoYXZpb3JhbCB0cmFjZSkgZGF0YS4NCg0KDQpgYGB7ciBldmFsPUZ9DQojMSBsb2FkIG5ld2VzdCB2ZXJzaW9uIG9mIGFjY291bnRzIGRmDQojd2l0aCBvY2N1cGllZD1UUlVFIGZvciBwcmUtc2V0IGFjY291bnRzIHRoYXQgYXJlIHBlcnNvbmFsaXplZC9yZWFkeSBmb3IgdXNlLiANCg0KI2lmIG5ld2VzdCB2ZXJzaW9uIHdhcyBzYXZlZCB5ZXN0ZXJkYXksIHRoaXMgd29ya3M6DQojYWNjb3VudHMgPC0gZmxvYWQocGFzdGUwKCIuL2xvY2FsLyIsIHN1YnN0cihnc3ViKCJbOi1dIiwgIiIsIFN5cy5EYXRlKCktMSksIDEsIDgpLCAiYWNjb3VudHMuUiIpKQ0KIyBvciBqdXN0OiBsb2FkKCIuL2xvY2FsL2FjY291bnRzLlIiKSAjaWYgSSBzYXZlIHRoZSB1cGRhdGVkIHZlcnNpb24gd2l0aCB0aGUgYmFzZS1SIHNhdmUgZnVuY3Rpb24uDQphY2NvdW50cyA8LSBmbG9hZCgiLi9sb2NhbC9hY2NvdW50cy5SIikNCmZpeChhY2NvdW50cykNCg0KIzIgbG9hZCAobW9zdCByZWNlbnQpIHByZS1pbnRlcnZlbnRpb24gcmVzcG9uc2VzDQojYSBuZXcgdmVyc2lvbi9leHBvcnQgc2hvdWxkIGJlIGNvcGllZCB0byB0aGUgbG9jYWwgZm9sZGVyIGVhY2ggZGF5L2l0ZXJhdGlvbi4uLg0KZGYgPC0gcmVhZC5jc3YoIi4vbG9jYWwvdm9vcm1ldGluZy9yZXN1bHRzLXN1cnZleTExOTUzNS5jc3YiKQ0KI3RoZXNlIGFyZSAqY29tcGxldGUqIHJlc3BvbnNlcyENCg0KIzMgZ2V0IHRvZGF5J3MgcmVzcG9uc2VzIChiYyBwcmV2aW91cyByZXNwb25zZXMgYXJlIGFscmVhZHkgaGFuZGxlZCEpDQpkZiA8LSBkZlt3aGljaChhcy5EYXRlKGRmJHN1Ym1pdGRhdGUpPT1TeXMuRGF0ZSgpKSxdDQojb3IgbGFzdCBmZXcuLi4gZGYgPC0gdGFpbChkZixYKQ0KI2Fsc28gcmVtb3ZlICd0ZXN0JyByZXNwb25zZXMsIHdoaWNoIHdlcmUgaW5kaWNhdGVkIHdpdGggZmlyc3RuYW1lICd0ZXN0Jw0KZGYgPC0gZGZbd2hpY2goIXRvbG93ZXIoZGYkZW1haWxzLjMuKT09InRlc3QiKSxdDQoNCiM0IHBlcnNvbmFsaXplIGFjY291bnRzIGZvciB0aGVzZSByZXNwb25kZW50cw0KIzUgdXBkYXRlIGFjY291bnRzIGRmIGJ5IHNldHRpbmcgb2NjdXBpZWQ9VFJVRQ0KDQojcHJlcGFyYXRpb246DQojbWFrZSBncm91cHMgbmFtZXMgaW4gc3VydmV5IGRmIHJlc2VtYmxlIHRob3NlIG9mIHRoZSAnYWNjb3VudHMnIGRmDQpkZiRTMCA8LSBpZmVsc2UoZGYkUzAgPT0gIktyYWNodHRyYWluaW5nIGJhc2lzIGJlZ2lubmVyIC0gbWFhbmRhZyAoMTc6NDUtMTk6MDApIiwgIktyYWNodHRyYWluaW5nIGJhc2lzIC0gbWFhbmRhZyIsDQogICAgICAgICAgICAgICAgIGlmZWxzZShkZiRTMCA9PSAiS3JhY2h0dHJhaW5pbmcgc3BvcnRzcGVjaWZpZWsgLSBtYWFuZGFnICgyMTowMC0yMjoxNSkiLCAgIktyYWNodHRyYWluaW5nIHNwb3J0c3BlY2lmaWVrIC0gbWFhbmRhZyIsDQogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGYkUzAgPT0gIktyYWNodHRyYWluaW5nIGJhc2lzIGJlZ2lubmVyIC0gd29lbnNkYWcgKDIwOjMwLTIxOjQ1KSIsIktyYWNodHRyYWluaW5nIGJhc2lzIC0gd29lbnNkYWciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkZiRTMCA9PSAiS3JhY2h0dHJhaW5pbmcgdnJvdXdlbiAtIHdvZW5zZGFnICgxODowMC0xOToxNSkiLCAiS3JhY2h0dHJhaW5pbmcgdnJvdXdlbiAtIHdvZW5zZGFnIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGRmJFMwID09ICJLcmFjaHR0cmFpbmluZyB2cm91d2VuIC0gZG9uZGVyZGFnICgxNTo0NS0xNzowMCkiLCAiS3JhY2h0dHJhaW5pbmcgdnJvdXdlbiAtIGRvbmRlcmRhZyIsIE5BKSkpKSkNCiNhbHNvIG1ha2Ugc3VyZSBSc2VsZW5pdW0gYnJvd3NlciBpcyAoc3RpbGwpIGFjdGl2ZS4NCg0KDQpmb3IgKGkgaW4gdW5pcXVlKGRmJFMwKSkgeyAjZm9yIGVhY2ggdW5pcXVlIGdyb3VwDQoNCiAgI3JldHJpZXZlIHRoZSBzdXJ2ZXkgcmVzcG9uZGVudHMgdGhhdCByZXBvcnRlZCB0byBiZSBwYXJ0IG9mIHRoaXMgZ3JvdXANCiAgZGZfc3ViIDwtIGRmW3doaWNoKGRmJFMwPT1pKSxdDQogICNob3cgbWFueSBhcmUgdGhlcmUgKGllLCBuZXcgb25lcyk/DQogIG4gPC0gbnJvdyhkZl9zdWIpDQogIA0KICAjZm9yIGVhY2ggYXRobGV0ZSBqIGJlbG9uZ2luZyB0byB0aGlzIGdyb3VwLCBpbiAxOm4sIA0KICAjJ3BlcnNvbmFsaXplJyB0aGUgYWNjb3VudA0KICANCiAgZm9yIChqIGluIDE6bikgew0KDQogICAgU3lzLnNsZWVwKDEpDQogICAgDQogICAgI2dldCBwZXJzb25hbCBkYXRhIGZyb20gc3VydmV5DQogICAgZmlyc3RuYW1lIDwtIGRmX3N1YiRlbWFpbHMuMy5bal0NCiAgICBsYXN0bmFtZSA8LSBzdWJzdHIoZGZfc3ViJGVtYWlscy40LltqXSwxLDEpDQogICAgZW1haWwgPC0gZGZfc3ViJGVtYWlscy4xLltqXQ0KICAgIA0KICAgIFN5cy5zbGVlcCgxKQ0KDQogICAgI25hdmlnYXRlIGxvZyBpbiBwYWdlDQogICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL2xvZ2luLmNzaHRtbCIpDQogICAgDQogICAgI3RoaXMgcmVzcG9uZGVudCBnZXRzIGFzc2lnbmVkIHRoZSBmaXJzdCBlbnRyeSBmcm9tIHRoZSBgYWNjb3VudHNgIGRmLCB3aXRoIGdyb3VwPT1pICYgb2NjdXBpZWQ9PUYpDQogICAgZW50cnkgPC0gYWNjb3VudHNbd2hpY2goYWNjb3VudHMkZ3JvZXA9PWkgJiBhY2NvdW50cyRvY2N1cGllZD09RilbMV0sXQ0KICAgIA0KICAgICNsb2dpbiB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGFjY291bnQNCiAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImxvZ2luX25hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGVudHJ5JGVtYWlsKSkNCiAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImxvZ2luX3Bhc3N3b3JkIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChlbnRyeSRwYXN3b29yZCkpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgICANCiAgICAjbmF2aWdhdGUgZWRpdCBzZXR0aW5ncw0KICAgIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Vc2VyUHJvZmlsZS5jc2h0bWw/ZWRpdD1wI3Byb2ZpbGUiKQ0KICAgICNyZW1EciRmaW5kRWxlbWVudCgiaWQiLCAiZm5hbWUiKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAiZm5hbWUiKSRjbGVhckVsZW1lbnQoKQ0KICAgIFN5cy5zbGVlcCgxKQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsICJmbmFtZSIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoZmlyc3RuYW1lKSkNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgImxuYW1lIikkY2xlYXJFbGVtZW50KCkNCiAgICBTeXMuc2xlZXAoMSkNCiAgICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAibG5hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGxhc3RuYW1lLCBrZXk9ImVudGVyIikpDQogICAgDQogICAgU3lzLnNsZWVwKDIpDQogIA0KICAgICNsb2dvdXQNCiAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICdsaW5rIHRleHQnLCdVaXRsb2dnZW4nKSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICNhY2NvdW50IGlzIG5vdyBvY2N1cGllZCwgc28gc2V0IHRvIFRSVUUgKHNvIHRoYXQgaXQgd2lsbCBiZSBza2lwcGVkIGZvciB0aGUgbmV4dCByZXNwb25kZW50KQ0KICAgICNhY2NvdW50c1t3aGljaChhY2NvdW50cyRncm9lcD09aSAmIGFjY291bnRzJG9jY3VwaWVkPT1GKVsxXSxdDQogICAgI2J1dCBmaXJzdCBhZGQgdGhlIGVtYWlsIGFkcmVzcyBvZiB0aGUgY29ycmVzcG9uZGluZyBwYXJpdGljcGFudHMsIGFzIGFuIGlkZW50aWZpZXINCiAgICBhY2NvdW50cyRpZFt3aGljaChhY2NvdW50cyRncm9lcD09aSAmIGFjY291bnRzJG9jY3VwaWVkPT1GKVsxXV0gPC0gZW1haWwNCiAgICBhY2NvdW50cyRvY2N1cGllZFt3aGljaChhY2NvdW50cyRncm9lcD09aSAmIGFjY291bnRzJG9jY3VwaWVkPT1GKVsxXV0gPC0gVFJVRQ0KICB9DQp9DQpmaXgoYWNjb3VudHMpDQojYW5kIG1ha2Ugc3VyZSB0byBzYXZlIHRoZSBhY2NvdW50cyBkZiBhcyBhIG5ldyB2ZXJzaW9uDQpmc2F2ZShhY2NvdW50cywiYWNjb3VudHMuUiIpDQojYW5kL29yIGp1c3QgYXMgYGFjY291bnRzLlJgLCBiYyB0aGVuIGl0IGZlZWRzIGludG8gdGhlIHNvY2lhbHdhbGwgdXBkYXRlci4uLj8NCnNhdmUoYWNjb3VudHMsZmlsZT0iLi9sb2NhbC9hY2NvdW50cy5SIikNCmBgYA0KDQoNCjwhLS0tLQ0KQFJGOiBhdCB0aGUgZW5kIG9mIGVhY2ggZGF5LCBleHRyYWN0IGFsbCBhY3RpdmF0ZWQgKHJlYWR5LWZvci11c2UpIGFjY291bnRzLCBzYXZlIGFzIGEgZGYsIGltcG9ydCBpbiBMUywgYW5kIHNlbmQgYSBtZXNzYWdlIHRvIHBhcnRpY2lwYW50czogIlRoYW5rcyBmb3IgcGFydGljaXBhdGluZywgaGVyZSBhcmUgeW91ciBsb2dpbiBjcmVkZW50aWFscy4gRm9yIGluc3RydWN0aW9ucyBmb3IgdHJhY2tpbmcgYWN0aXZpdGllcywgY2hlY2sgb3VyIHdlYnNpdGU6IGh0dHBzOi8vd3d3LmtyYWNodGlncnNjLm5sL2luc3RydWN0aWUuaHRtbC4gR29vZCBsdWNrISIgDQotLS0tPg0KDQotLS0tDQoNCjxicj4NCg0KIyBSZW1vdmUgYWNjb3VudHMgeyNyZW1vdmV9DQoNCg0KIyMgVW5saW5rIGZyb20gY29hY2hpbmcgYWNjb3VudA0KDQpJZiB0aGUgZXhwZXJpbWVudCBpcyBkb25lLCBgciBjb2xvcml6ZSgiKHdoZW4gYWxsIGFjdGl2aXR5IGFuZCBzb2NpYWwgd2FsbCBkYXRhIGFyZSBoYXJ2ZXN0ZWQhISEpIiwgInJlZCIpYCwgd2UgcmVtb3ZlIHRoZSBhY2NvdW50cyBmcm9tIG91ciBjb2FjaGluZyBhY2NvdW50Lg0KDQpgYGB7ciwgZXZhbD1GfQ0KRklOSVNIRUQgPSBGQUxTRQ0KDQppZihGSU5JU0hFRD09VFJVRSl7DQogIA0KICByZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vbG9naW4uY3NodG1sP0Fzc29jPSZwYWdlX3JlZGlyZWN0PS8iKQ0KICANCiAgI2xvZ2luDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fbmFtZSIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QobWFzdGVyJGVtYWlsKSkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9wYXNzd29yZCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QobWFzdGVyJHBhc3N3b3JkKSkNCiAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgDQogICNnbyB0byBhdGhsZXRlcyBwYWdlDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Db2FjaEF0aGxldGVzLmNzaHRtbCIpDQogIA0KICBmb3IgKGkgaW4gMTpsZW5ndGgoYWNjb3VudHMkZW1haWwpKSB7ICNmb3IgYWxsIGFjY291bnRzDQogICAgDQogICAgcHJpbnQocGFzdGUoInVubGlua2luZyBhY2NvdW50OiIsIGFjY291bnRzJGVtYWlsW2ldKSkNCiAgDQogICAgI3NhdmUgYXRobGV0ZSB0YWJsZQ0KICAgIHBhZ2UgPC0gcmVtRHIkZ2V0UGFnZVNvdXJjZSgpDQogICAgdGFibGUgPC0gcGFnZVtbMV1dICU+JQ0KICAgICAgcmVhZF9odG1sKCkgJT4lDQogICAgICBodG1sX25vZGVzKCd0YWJsZScpICU+JSANCiAgICAgIC5bWzFdXSAlPiUNCiAgICAgIGh0bWxfdGFibGUoKSAlPiUNCiAgICAgIGFzLmRhdGEuZnJhbWUoLikgDQogICAgDQogICAgI2dldCBlbWFpbCB0byBhdGhsZXRlIGkNCiAgICBlbWFpbCA8LSBhY2NvdW50cyRlbWFpbFtpXQ0KICAgIA0KICAgICNmaW5kIHdoaWNoIGVudHJ5IGluICh1cGRhdGVkISkgdGFibGUgY29ycmVzcG9uZHMgdG8gdGhpcyBlbWFpbA0KICAgIHggPC0gd2hpY2godGFibGUkRW1haWw9PWVtYWlsKQ0KICAgIA0KICAgICNpZiB0aGVyZSBpcyBhIGNvcnJlc3BvbmRlbmNlLi4uDQogICAgaWYobGVuZ3RoKHgpPjApIHsNCiAgICAgICNlZGl0IGFjY291bnQgd2l0aCB0aGlzIGVtYWlsDQogICAgICAjcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJpY29uLXBlbmNpbCIpW1t4XV0kaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgImljb24tcGVuY2lsIilbW3hdXSRjbGlja0VsZW1lbnQoKQ0KICAgICAgDQogICAgICAjZGVsZXRlIGZyb20gY29hY2hpbmcgYWNjb3VudA0KICAgICAgI3JlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJpY29uLXRyYXNoIikkaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgICByZW1EciRmaW5kRWxlbWVudCgiY2xhc3MiLCAiaWNvbi10cmFzaCIpJGNsaWNrRWxlbWVudCgpDQogICAgICBTeXMuc2xlZXAoMSkNCiAgICAgICNjb25maXJtDQogICAgICAjcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnT0snKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ09LJykkY2xpY2tFbGVtZW50KCkNCiAgICB9DQogIH0NCg0KfQ0KYGBgDQoNCjxicj4NCg0KIyMgZGVsZXRlIGFjY291bnRzDQpXZSBtYXkgYWxzbyBkZWxldGUgdGhlIGFjY291bnRzLg0KDQpgYGB7ciwgZXZhbD1GfQ0KaWYoRklOSVNIRUQ9PVRSVUUpew0KICANCiAgZm9yIChpIGluIDE6bikgew0KICAgIA0KICAgIHByaW50KHBhc3RlKCJkZWxldGluZyBhY2NvdW50OiIsIGFjY291bnRzJGVtYWlsW2ldKSkNCiAgICANCiAgICAjbmF2aWdhdGUgdG8gbG9naW4gcGFnZQ0KICAgIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS8iKQ0KICAgIA0KICAgICNsb2dpbg0KICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fbmFtZSIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkZW1haWxbaV0pKQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fcGFzc3dvcmQiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGFjY291bnRzJHBhc3dvb3JkW2ldKSkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiYnRuIilbWzFdXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICNwcm9maWxlIHNldHRpbmdzDQogICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1VzZXJQcm9maWxlLmNzaHRtbD9lZGl0PXAjcHJvZmlsZSIpDQogICAgI2RlbGV0ZQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsImRlbC11c2VyIikkY2xpY2tFbGVtZW50KCkNCiAgICAjYWNjZXB0DQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidG4iKVtbNF1dJGNsaWNrRWxlbWVudCgpDQogIH0NCn0NCg0KYGBgDQoNCjxicj4NCg0KbWFrZSBzdXJlIHRvIGVuZCBhIHNlc3Npb24gYnkgdGVybWluYXRpbmcgdGhlIHByb2Nlc3M6DQpgYGB7ciwgZXZhbD1GfQ0KcGlkIDwtIHJEJHNlcnZlciRwcm9jZXNzJGdldF9waWQoKQ0Kc3lzdGVtKHBhc3RlMCgiVGFza2tpbGwgL0YgL1QiICwiIC9QSUQgIiwgcGlkKSkNCmBgYA0KDQo=