Scripts on this page allow users to:

  1. Create a number of FSurge athlete accounts and assign them a team and an experimental condition.
  2. Link them to a master or coach account (i.e., sending coaching invitations via the master-account and accepting them via the athlete-accounts) and activate Social Walls.
  3. Personalize the accounts (i.e., adjusting athlete name)
  4. Remove accounts from coaching-account / delete athlete-accounts.


1 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


1.1 clean up

rm(list=ls())
gc()


1.2 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"])
}


1.3 necessary packages

packages = c("RSelenium", "rvest", "tidyverse", "netstat", "pingr", "stringr")
fpackage.check(packages)


1.4 VPN

It may be important to use a VPN. Make sure to regularly change your public IP adress. I used HMA.


1.5 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))


2 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()
}


4 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):

  1. load (updated) pre-set accounts df
  2. load (updated) pre-intervention survey responses
  3. get all of today’s responses (i.e., completed before midnight)
  4. for each participant, ‘personalize’ the account using first and lastname
  5. 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")


5 Remove accounts

5.2 delete accounts

We may also delete the accounts.

if(FINISHED==TRUE){
  
  for (i in 1:n) {
    
    print(paste("deleting account:", 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()
    
    #profile settings
    remDr$navigate("https://log.finalsurge.com/UserProfile.cshtml?edit=p#profile")
    #delete
    remDr$findElement("id","del-user")$clickElement()
    #accept
    remDr$findElements("class", "btn")[[4]]$clickElement()
  }
}


make sure to end a session by terminating the process:

pid <- rD$server$process$get_pid()
system(paste0("Taskkill /F /T" ," /PID ", pid))
LS0tDQp0aXRsZTogIlNldCB1cCBGU3VyZ2UiDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY3NzOiB0d2Vha3MuY3NzDQogICAgdG9jOiAgdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMg0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KGtuaXRyKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KYGBgDQoNCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KLS0tICANCg0KU2NyaXB0cyBvbiB0aGlzIHBhZ2UgYWxsb3cgdXNlcnMgdG86DQoNCjEuIFtDcmVhdGVdKCNjcmVhdGUpIGEgbnVtYmVyIG9mIEZTdXJnZSBhdGhsZXRlIGFjY291bnRzIGFuZCBhc3NpZ24gdGhlbSBhIHRlYW0gYW5kIGFuIGV4cGVyaW1lbnRhbCBjb25kaXRpb24uIA0KMi4gW0xpbmtdKCNsaW5rKSB0aGVtIHRvIGEgbWFzdGVyIG9yIGNvYWNoIGFjY291bnQgKGkuZS4sIHNlbmRpbmcgY29hY2hpbmcgaW52aXRhdGlvbnMgdmlhIHRoZSBtYXN0ZXItYWNjb3VudCBhbmQgYWNjZXB0aW5nIHRoZW0gdmlhIHRoZSBhdGhsZXRlLWFjY291bnRzKSBhbmQgYWN0aXZhdGUgU29jaWFsIFdhbGxzLg0KMy4gW1BlcnNvbmFsaXplXSgjcGVycykgdGhlIGFjY291bnRzIChpLmUuLCBhZGp1c3RpbmcgYXRobGV0ZSBuYW1lKQ0KNC4gW1JlbW92ZV0oI3JlbW92ZSkgYWNjb3VudHMgZnJvbSBjb2FjaGluZy1hY2NvdW50IC8gZGVsZXRlIGF0aGxldGUtYWNjb3VudHMuIA0KDQo8YnI+DQoNCiMgUHJlcGFyYXRpb24NCg0KVGhpcyB0b29sIHVzZXMgYXV0b21hdGVkIHdlYmJyb3dzaW5nIHVzaW5nIGBSU2VsZW5pdW1gLg0KV2UgZmlyc3QgbmVlZCB0byBpbnN0YWxsIHRoaXMgcGFja2FnZSwgYWxvbmcgd2l0aCBzb21lIG90aGVyIHBhY2thZ2VzOg0KDQotIGBuZXRzdGF0YCBhbmQgYHBpbmdyYDogZmluZCBwb3J0IGluZm9ybWF0aW9uDQotIGBydmVzdGA6IGhhcnZlc3Qgd2VicGFnZXMNCi0gYHRpZHl2ZXJzZWA6IGRhdGEgd3JhbmdsaW5nDQotIGBzdHJpbmdyYDogc3RyaW5nIG9wZXJhdGlvbnMNCg0KDQo8YnI+DQoNCiMjIGNsZWFuIHVwDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQpybShsaXN0PWxzKCkpDQpnYygpDQpgYGANCg0KPGJyPg0KDQojIyBnZW5lcmFsIGN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFINCi0gYGZnZW5lcmF0ZXBhc3NgOiBQYXNzd29yZCBnZW5lcmF0b3IgZnVuY3Rpb24NCi0gYGZzYXZlYDogc2F2ZSBkYXRhIHdpdGggdGltZSBzdGFtcCBpbiBjb3JyZWN0IGRpcmVjdG9yeQ0KLSBgZmxvYWRgOiBsb2FkIFItb2JqZWN0cyB1bmRlciBuZXcgbmFtZXMNCg0KYGBge3J9DQpmcGFja2FnZS5jaGVjayA8LSBmdW5jdGlvbihwYWNrYWdlcykgew0KICAgIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBpZiAoIXJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgew0KICAgICAgICAgICAgaW5zdGFsbC5wYWNrYWdlcyh4LCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgICAgICAgICAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQogICAgICAgIH0NCiAgICB9KQ0KfQ0KDQpmZ2VuZXJhdGVwYXNzIDwtIGZ1bmN0aW9uKG5jaGFyPTcsIG51cHBlcj0xLCBuc3BlY2lhbD0xLCBubnVtYmVyPTIpIHsNCiAgc3BlY2lhbCA8LSBjKCIhIiwiQCIsIiMiLCIkIiwiJSIsIl4iLCImIiwiKiIsIigiLCIpIiwiXyIsIisiKQ0KICBudW1iZXIgPC0gYygxOjkpDQogIGNoYXJzIDwtIGMoc2FtcGxlKHNwZWNpYWwsbnNwZWNpYWwscmVwbGFjZT1UKSwNCiAgICAgICAgICAgICBzYW1wbGUobnVtYmVyLG5udW1iZXIscmVwbGFjZT1UKSwNCiAgICAgICAgICAgICBzYW1wbGUoTEVUVEVSUyxudXBwZXIscmVwbGFjZT1UKSwNCiAgICAgICAgICAgICBzYW1wbGUobGV0dGVycyxuY2hhci0obnVwcGVyK25zcGVjaWFsK25udW1iZXIpLA0KICAgICAgICAgICAgICAgICAgICByZXBsYWNlPVQpKQ0KICBwYXNzIDwtIHBhc3RlMChzYW1wbGUoY2hhcnMpLGNvbGxhcHNlPSIiKQ0KICByZXR1cm4ocGFzcykNCn0NCg0KZnNhdmUgPC0gZnVuY3Rpb24oeCwgZmlsZSwgbG9jYXRpb24gPSAiLi9sb2NhbC8iLCAuLi4pIHsNCiAgICBpZiAoIWRpci5leGlzdHMobG9jYXRpb24pKQ0KICAgICAgICBkaXIuY3JlYXRlKGxvY2F0aW9uKQ0KICAgIGRhdGVuYW1lIDwtIHN1YnN0cihnc3ViKCJbOi1dIiwgIiIsIFN5cy50aW1lKCkpLCAxLCA4KQ0KICAgIHRvdGFsbmFtZSA8LSBwYXN0ZShsb2NhdGlvbiwgZGF0ZW5hbWUsIGZpbGUsIHNlcCA9ICIiKQ0KICAgIHByaW50KHBhc3RlKCJTQVZFRDogIiwgdG90YWxuYW1lLCBzZXAgPSAiIikpDQogICAgc2F2ZSh4LCBmaWxlID0gdG90YWxuYW1lKQ0KfQ0KDQoNCmZsb2FkICA8LSBmdW5jdGlvbihmaWxlTmFtZSl7DQogIGxvYWQoZmlsZU5hbWUpDQogIGdldChscygpW2xzKCkgIT0gImZpbGVOYW1lIl0pDQp9DQpgYGANCg0KPGJyPg0KDQojIyBuZWNlc3NhcnkgcGFja2FnZXMNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGFja2FnZXMgPSBjKCJSU2VsZW5pdW0iLCAicnZlc3QiLCAidGlkeXZlcnNlIiwgIm5ldHN0YXQiLCAicGluZ3IiLCAic3RyaW5nciIpDQpmcGFja2FnZS5jaGVjayhwYWNrYWdlcykNCmBgYA0KDQo8YnI+DQoNCiMjIFZQTg0KDQpJdCBtYXkgYmUgaW1wb3J0YW50IHRvIHVzZSBhIFZQTi4gTWFrZSBzdXJlIHRvIHJlZ3VsYXJseSBjaGFuZ2UgeW91ciBwdWJsaWMgSVAgYWRyZXNzLiBJIHVzZWQgW0hNQV0oaHR0cHM6Ly93d3cuaGlkZW15YXNzLmNvbS9lbi11cy9pbmRleCkuDQoNCjxicj4NCg0KIyMgc2V0IHVwIFJTZWxlbml1bQ0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiNmaW5kIGEgZnJlZSBwb3J0DQpwb3J0IDwtIG5ldHN0YXQ6OmZyZWVfcG9ydChyYW5kb209VFJVRSkNCg0KI3BpbmcgcG9ydCBmb3IgY29uZmlybWF0aW9uDQpwaW5ncjo6cGluZ19wb3J0KCJsb2NhbGhvc3QiLCBwb3J0KQ0KDQojc2V0IHVwIHNlbGVuaXVtIHNlcnZlciBhbmQgYnJvd3Nlcg0KckQgPC0gcnNEcml2ZXIoYnJvd3Nlcj0iZmlyZWZveCIsIHBvcnQ9cG9ydCwgY2hyb21ldmVyPU5VTEwsIHZlcmJvc2U9RikNCnJlbURyIDwtIHJEW1siY2xpZW50Il1dDQpgYGAgDQoNCjxicj4NCg0KKipOT1RFKio6IGFmdGVyIHVzaW5nIGBSU2VsZW5pdW06OnJzRHJpdmVyYCBmb3IgYSB3aGlsZSB3aXRob3V0IGlzc3VlLCBJIHJlY2VudGx5IGdvdCBlcnJvciBtZXNzYWdlcy4gVGhlIHJvb3QgY2F1c2Ugb2YgdGhlIHByb2JsZW0gaXMgdGhhdCBgcnNEcml2ZXIoKWAgaXMgY29uZmlndXJlZCB0byBsYXVuY2ggdGhlIFNlbGVuaXVtIHNlcnZlciB3aXRoIHRoZSBsYXRlc3QgdmVyc2lvbnMgb2YgQ2hyb21lICphbmQqIEZpcmVmb3ggZHJpdmVycywgKmV2ZW4gaWYgeW91IGhhdmUgc3BlY2lmaWVkIHRoZSBvdGhlciBicm93c2VyIHRvIHJ1biouIFRvIHNvbHZlIHRoaXMsIGp1c3QgcGFzcyBpbiBgTlVMTGAgaW4gdGhlIENocm9tZSBkcml2ZXIgdmVyc2lvbiBgY2hyb21ldmVyYC4uLiAoc2VlIGFsc28gW3RoaXNdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzUxMjU2NTc4L3JzZWxlbml1bS11bmFibGUtdG8tY3JlYXRlLW5ldy1zZXJ2aWNlLWNocm9tZWRyaXZlcnNlcnZpY2UpIGFuZCBbdGhpc10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDUzOTU4NDkvY2FudC1leGVjdXRlLXJzZHJpdmVyLWNvbm5lY3Rpb24tcmVmdXNlZCkgc3RhY2tvdmVyZmxvdyBwb3N0KS4NCg0KDQpJZiBhdCBhbnkgdGltZSB5b3Ugd2lzaCB0byB0ZXJtaW5hdGUgdGhlIHByb2Nlc3MsIHVzZSB0aGUgY29kZToNCmBgYHtyLCBldmFsPUZ9DQpwaWQgPC0gckQkc2VydmVyJHByb2Nlc3MkZ2V0X3BpZCgpI2dldCBwcm9jZXNzIGlkDQpzeXN0ZW0ocGFzdGUwKCJUYXNra2lsbCAvRiAvVCIgLCIgL1BJRCAiLCBwaWQpKQ0KYGBgDQoNCi0tLS0NCg0KPGJyPg0KDQojIENyZWF0ZSBhY2NvdW50cyB7I2NyZWF0ZX0NCg0KV2UgY3JlYXRlIGEgZGF0YWZyYW1lIG9mIGFjY291bnRzIHRvIGJlIGNyZWF0ZWQ6DQoNCi0gbmFtZXMgIA0KLSBlbWFpbCBhZHJlc3Nlcw0KLSBwYXNzd29yZHMgZ2VuZXJhdGVkIHVzaW5nIGBmZ2VuZXJhdGVwYXNzYA0KLSBhc3NpZ24gYWNjb3VudHMgdG8gYSB0cmFpbmluZyBncm91cA0KLSBhc3NpZ24gZ3JvdXBzIHRvIGVpdGhlciBhIGNvbnRyb2wgb3IgZXhwZXJpbWVudGFsL2ludGVydmVudGlvbiBjb25kaXRpb24NCg0KSGVyZSwgd2UgY3JlYXRlIDUgZ3JvdXBzIG9mIDE1IGFjY291bnRzICh0aHVzLCA3NSBhY2NvdW50cykNCg0KYGBge3IsIGV2YWw9Rn0NCm49NzUgI2FjY291bnRzDQpOPTUgI2dyb3Vwcw0KDQojbWFrZSBkZiwgYXRobGV0ZSBhY2NvdW50cyBhc3NpZ25lZCB0byBncm91cHMNCmFjY291bnRzIDwtIGRhdGEuZnJhbWUodm9vcm5hYW09IkEiLCAjYXJiaXJhcnkuLi4gQSBmb3IgYXRobGV0ZQ0KICAgICAgICAgICAgICAgICAgICAgICBhY2h0ZXJuYWFtPWFzLmNoYXJhY3RlcihjKDE6bikpLCAjaW5kaWNhdG9yDQogICAgICAgICAgICAgICAgICAgICAgIGVtYWlsPU5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgcGFzd29vcmQ9TkEsDQogICAgICAgICAgICAgICAgICAgICAgIGdyb2VwPXJlcCgxOk4sZWFjaD1uL04pKQ0KDQojZ3JvdXBzIGNhbiBiZSBnaXZlbiBuYW1lcywgaW5zdGVhZCBvZiBudW1iZXJzLi4uDQojaSBuYW1lIHRoZSBncm91cHMgYmFzZWQgb24gdGhlIGNvdXJzZSBuYW1lLA0KI2kgc2F2ZWQgdGhpcyBpbiBhIHNlY3JldCBmb2xkZXINCmxvYWQoIi4vc2VjcmV0L2NvdXJzZV9uYW1lcy5SIikNCmFjY291bnRzJGdyb2VwIDwtIGlmZWxzZShhY2NvdW50cyRncm9lcD09MSwgY291cnNlc1sxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYWNjb3VudHMkZ3JvZXA9PTIsIGNvdXJzZXNbMl0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShhY2NvdW50cyRncm9lcD09MywgY291cnNlc1szXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShhY2NvdW50cyRncm9lcD09NCwgY291cnNlc1s0XSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYWNjb3VudHMkZ3JvZXA9PTUsIGNvdXJzZXNbNV0sIE5BKSkpKSkNCg0KI2NyZWF0ZSBlbWFpbCBhbmQgcGFzc3dvcmQNCmZvciAoaSBpbiAxOm4pIHsNCiAgI2NyZWF0ZSBlbWFpbCBhbmQgcGFzc3dvcmQNCiAgYWNjb3VudHMkZW1haWxbaV0gPC0gcGFzdGUwKCJhdGxlZXQiLGksIkByc2MubmwiKQ0KICBhY2NvdW50cyRwYXN3b29yZFtpXSA8LSBmZ2VuZXJhdGVwYXNzKDcpDQp9DQoNCiNhbHNvLCByYW5kb21seSBhc3NpZ24gYSBudW1iZXIgb2YgZ3JvdXBzIGEgc29jaWFsIHdhbGwuDQpuX3NvY3dhbGwgPC0gMyAjaGVyZSwgMw0KYWNjb3VudHMkc29jaWFsd2FsbCA8LSBOQQ0KI3NhbXBsZSAzIGdyb3VwcyB0aGF0IHdpbGwgYmUgYXNzaWduZWQgYSBzb2NpYWwgd2FsbA0Kc29jd2FsbF9ncm91cCA8LSBzYW1wbGUodW5pcXVlKGFjY291bnRzJGdyb2VwKSxuX3NvY3dhbGwpDQphY2NvdW50cyRzb2NpYWx3YWxsIDwtIGlmZWxzZShhY2NvdW50cyRncm9lcCAlaW4lIHNvY3dhbGxfZ3JvdXAsIDEsMCkNCg0KI3dlIGFsc28gYWRkIGEgdmFyaWFibGUgJ29jY3VwaWVkJywgd2hpY2ggaW5kaWNhdGVzIHdoZXRoZXIgYSBwcmUtc2V0IGFjY291bnQNCiNpcyBjdXN0b21pemVkIChpLmUuLCBmaXJzdCBhbmQgbGFzdG5hbWUgYXJlIGFzc2lnbmVkKSBhbmQgcmVhZHkgZm9yIHVzZS4NCmFjY291bnRzJG9jY3VwaWVkIDwtIEYNCiNhbmQgYSB2YXJpYWJsZSBpZCB3aGljaCBkZW5vdGVzIHBhcnRpY2lwYW50cyBlbWFpbCBhZHJlc3MsIHRvIHVsdGltYXRlbHkgbGluayBGU3VyZ2UgZGF0YSB3aXRoIHN1cnZleSAoYW5kIGJlaGF2aW9yKSBkYXRhDQphY2NvdW50cyRpZCA8LSBOQQ0KDQojc2F2ZSB0byBsb2NhbCBmb2xkZXIgdXNpbmcgZnNhdmUNCiMjIyMjIyNmc2F2ZShhY2NvdW50cywgImFjY291bnRzLlIiKQ0KYGBgIA0KDQo8YnI+DQoNCk5leHQgdXAsIHdlIHVzZSBSU2VsZW5pdW0gdG8gY3JlYXRlIHRoZSBhY2NvdW50cy4NCg0KV2UgY3VzdG9taXplIHRoZSBhY2NvdW50IHNldHRpbmdzLCBhbmQgdGhlIHdvcmtvdXQgcGFnZToNCg0KYGBge3IsIGV2YWw9Rn0NCiNsb2FkIGluIHRoZSBhY2NvdW50cw0KbG9hZCgiLi9sb2NhbC9hY2NvdW50cy5SIikNCm4gPC0gbnJvdyhhY2NvdW50cykNCg0KDQpmb3IgKGkgaW4gMTpuKSB7DQogIFN5cy5zbGVlcCgxKQ0KICBwcmludChwYXN0ZSgiY3JlYXRpbmcgYWNjb3VudDoiLCBhY2NvdW50cyRlbWFpbFtpXSkpDQogIA0KICAjbmF2aWdhdGUgdG8gY3JlYXRlIGFjY291bnQgcGFnZQ0KICByZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vcmVnaXN0ZXIuY3NodG1sP3BhZ2VfcmVkaXJlY3Q9JTJmIikNCg0KICAjaW5zZXJ0IG5hbWUNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJjcmVhdGVfZmlyc3QiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGFjY291bnRzJHZvb3JuYWFtW2ldKSkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJjcmVhdGVfbGFzdCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkYWNodGVybmFhbVtpXSkpDQoNCiAgI2FuZCBlbWFpbA0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImNyZWF0ZV9lbWFpbCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkZW1haWxbaV0pKQ0KIA0KICAjYWRqdXN0IHRpbWV6b25lDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAiY3JlYXRlX3RpbWV6b25lIikkY2xpY2tFbGVtZW50KCkNCiAgDQogICNnZXQgaW5uZXIgaHRtbA0KICBpbm5lciA8LSByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImNyZWF0ZV90aW1lem9uZSIpJGdldEVsZW1lbnRBdHRyaWJ1dGUoImlubmVySFRNTCIpW1sxXV0NCiAgaW5uZXIgJT4lDQogICAgcmVhZF9odG1sKCkgJT4lICNyZWFkIGh0bWwNCiAgICBodG1sX25vZGVzKCJvcHRpb24iKSAtPiBvcHRpb25zICNnZXQgYWxsIG9wdGlvbiB0YWdzDQoNCiAgI3BpY2sgKEdNVCswMTowMCkgQW1zdGVyZGFtLCBCZXJsaW4sIC4uLg0KICByZW1EciRmaW5kRWxlbWVudHModXNpbmcgPSAidGFnIG5hbWUiLCB2YWx1ZSA9Im9wdGlvbiIpW1t3aGljaChncmVwbCgiQW1zdGVyZGFtIiwgb3B0aW9ucykpXV0kY2xpY2tFbGVtZW50KCkNCiAgDQogICNpbnNlcnQgcGFzc3dvcmQNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJwYXNzd29yZF9tZXRlciIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkcGFzd29vcmRbaV0pKQ0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImNyZWF0ZV9wYXNzd29yZG1hdGNoIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChhY2NvdW50cyRwYXN3b29yZFtpXSkpDQogIA0KICAjY3JlYXRlIGFjY291bnQNCiAgcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIG5hbWUiLCAiYnRuIikkY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKDEpDQogIA0KICAjYWRqdXN0IHVzZXIgc2V0dGluZ3MNCiAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1VzZXJQcm9maWxlLmNzaHRtbD9lZGl0PXMjc2V0dGluZ3MiKQ0KIA0KICAgI3NlbGVjdCBsYW5ndWFnZTogRHV0Y2gNCiAgcmVtRHIkZmluZEVsZW1lbnRzKHVzaW5nID0gImNsYXNzIG5hbWUiLCB2YWx1ZSA9ICJyYWRpbyIpW1s1XV0kY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKC41KQ0KICANCiAgI2NvbnZlcnQgdG8gbWV0cmljIHN5c3RlbQ0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gIlVuaXRNZXRyaWMiKSRjbGlja0VsZW1lbnQoKQ0KICANCiAgIzI0IGhvdXJzIGRpc3BsYXkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJURGlzcGxheTI0IikkY2xpY2tFbGVtZW50KCkNCiAgDQogICN3ZWVrIHN0YXJ0IG9uIG1vbmRheQ0KICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gIldTTSIpJGNsaWNrRWxlbWVudCgpDQogIA0KICAjc2F2ZQ0KICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAic2F2ZUJ1dHRvblNldHRpbmdzIikkY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKDEpDQogIA0KICAjSSBhbHNvIHVzZSB0aGUgd29ya291dCBjdXN0b21pemF0aW9uIHBhZ2UgdG8gYWRqdXN0IHdoYXQgYWN0aXZpdHkgdHlwZXMgYXRobGV0ZXMgY2FuIHVwbG9hZC4gbm93IGl0IGlzIHRvbyBsYWJvcmlvdXMsIGFuZCB0aGVyZSBhcmUgd2F5IHRvIG1hbnkgc3VidHlwZXMgb2YgYWN0aXZpdGllcy4NCiAgI25vdGU6IGFwcGFyZW50bHksIGkgY2FuIG9ubHkgZWRpdC9kZWxldGUgdGhlIHN1YnR5cGVzLg0KICAjdGhlIGFjdGl2aXR5IHNlY3Rpb25zL2ZpZWxkcyAoZWcsIHBsYW5uZWQgZGlzdGFuY2UsIGhlYXJ0IHJhdGUsIGV0Yy4pIGNhbiBiZSBoaWRkZW4sIGJ1dCB0aGlzIGRvZXMgbm90IHRyYW5zbGF0ZSB0byB0aGUgYXBwLi4uDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Xb3Jrb3V0Q3VzdG9taXplLmNzaHRtbCIpDQogIA0KICAjaSBqdXN0IGhpZGUgbW9zdCBwcmUtY29kZWQgYWN0aXZpdHkgdHlwZXMuLi4gYW5kIGp1c3QgYWRkIGFsbCBhY3Rpdml0eS10eXBlcyB2aWEgY3VzdG9taXphdGlvbg0KICAjaSBuZWVkIHRvIG9wZW4gdGhlIGJveCBvZiBhY3Rpdml0eSB0eXBlIGogKGlmIG5vdCBhbHJlYWR5IG9wZW5lZCkNCiAgI2FuZCBzZWxlY3QgdGhlICdoaWRlIGFjdGl2aXR5IHR5cGUnLWNoZWNrYm94Lg0KICAjYW5kIHNhdmUNCiAgI2ZpcnN0LCBpIHB1dCB0aGUgaWRzIG9mIHRoZXNlIGNoZWNrYm94ZXMsIGFuZCBzYXZlYnV0dG9ucyBpbiB2ZWN0b3JzDQogIA0KICBoaWRlYm94IDwtIGMoMSwxMSwxOCwxOSwyMCwyNSwzMywzNCwzNSw0MCkNCiAgc2F2ZWJ0biA8LSBjKCJzYXZlQnV0dG9uXzAwMDAwMDAxLTAwMDEtMDAwMS0wMDAxLTAwMDAwMDAwMDAwMSIsDQogICAgICAgICAgICAgICAic2F2ZUJ1dHRvbl8wMDAwMDAwMy0wMDAzLTAwMDMtMDAwMy0wMDAwMDAwMDAwMDMiLA0KICAgICAgICAgICAgICAgInNhdmVCdXR0b25fMDAwMDAwMTAtMDAxMC0wMDEwLTAwMTAtMDAwMDAwMDAwMDEwIiwNCiAgICAgICAgICAgICAgICJzYXZlQnV0dG9uXzAwMDAwMDA1LTAwMDUtMDAwNS0wMDA1LTAwMDAwMDAwMDAwNSIsDQogICAgICAgICAgICAgICAic2F2ZUJ1dHRvbl8wMDAwMDAwOC0wMDA4LTAwMDgtMDAwOC0wMDAwMDAwMDAwMDgiLA0KICAgICAgICAgICAgICAgInNhdmVCdXR0b25fMDAwMDAwMDItMDAwMi0wMDAyLTAwMDItMDAwMDAwMDAwMDAyIiwNCiAgICAgICAgICAgICAgICJzYXZlQnV0dG9uXzAwMDAwMDA0LTAwMDQtMDAwNC0wMDA0LTAwMDAwMDAwMDAwNCIsDQogICAgICAgICAgICAgICAic2F2ZUJ1dHRvbl8wMDAwMDAwNi0wMDA2LTAwMDYtMDAwNi0wMDAwMDAwMDAwMDYiLA0KICAgICAgICAgICAgICAgInNhdmVCdXR0b25fMDAwMDAwMDctMDAwNy0wMDA3LTAwMDctMDAwMDAwMDAwMDA3IiwNCiAgICAgICAgICAgICAgICJzYXZlQnV0dG9uXzAwMDAwMDA5LTAwMDktMDAwOS0wMDA5LTAwMDAwMDAwMDAwOSIpDQogICAgICAgICAgDQogIGZvciAoaiBpbiBjKDQsNSw3LDgsOSwxMCkpeyAjZm9yIHByZS1zZXQgYWN0aXZpdHkgdHlwZXMgDQogICAgIyhidXQga2VlcCBydW5uaW5nLCBzd2ltbWluZywgd2Fsa2luZywgY3ljbGluZzsgYmVjYXVzZSBvZiB0aGUgbmljZSBzeW1ib2whKQ0KICAgIFN5cy5zbGVlcCgxKQ0KICAgICNvcGVuIGJveCBvZiBhY3Rpdml0eSB0eXBlIGogKGlmIG5vdCBhbHJlYWR5IG9wZW5lZCkNCiAgICBpZihyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtY29udGVudCIpW1tqXV0kZ2V0RWxlbWVudFRleHQoKVtbMV1dID09ICIiKXsNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJ3LWJveC1oZWFkZXIiKVtbal1dJGNsaWNrRWxlbWVudCgpICB9DQogICAgI2NsaWNrIGhpZGUtY2hlY2tib3gNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiY2hlY2tib3giKVtbaGlkZWJveFtqXV1dJGNsaWNrRWxlbWVudCgpDQogICAgI3NhdmUNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgc2F2ZWJ0bltqXSkkY2xpY2tFbGVtZW50KCkNCiAgICAjY2xvc2UgYm94DQogICAgU3lzLnNsZWVwKC41KQ0KICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJ3LWJveC1oZWFkZXIiKVtbal1dJGNsaWNrRWxlbWVudCgpIA0KICB9DQogIFN5cy5zbGVlcCgxKQ0KIA0KICAjZm9yIHJ1bm5pbmcsIHN3aW1taW5nLCBjeWNsaW5nLCBoaWRlIHN1YnR5cGUgZmllbGRzDQogICMxIHJ1bm5pbmcNCiAgaWYocmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInctYm94LWNvbnRlbnQiKVtbMV1dJGdldEVsZW1lbnRUZXh0KClbWzFdXSA9PSAiIil7DQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInctYm94LWhlYWRlciIpW1sxXV0kY2xpY2tFbGVtZW50KCkgIA0KICB9DQogICNzZWxlY3QgYWxsIHN1YnR5cGVzLCBzbyB0aGF0IHRoZXkgd2lsbCBiZSBoaWRkZW4NCiAgZm9yIChqIGluIDI6MTApew0KICAgIFN5cy5zbGVlcCguNSkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiY2hlY2tib3giKVtbal1dJGNsaWNrRWxlbWVudCgpDQogIH0NCiAgI3NhdmUgYW5kIGhpZGUNCiAgU3lzLnNsZWVwKC41KQ0KICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCBzYXZlYnRuWzFdKSRjbGlja0VsZW1lbnQoKQ0KICBTeXMuc2xlZXAoLjUpDQogIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJ3LWJveC1oZWFkZXIiKVtbMV1dJGNsaWNrRWxlbWVudCgpDQogIA0KICAjMiBzd2ltbWluZw0KICBpZihyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtY29udGVudCIpW1syXV0kZ2V0RWxlbWVudFRleHQoKVtbMV1dID09ICIiKXsNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzJdXSRjbGlja0VsZW1lbnQoKSAgDQogIH0NCiAgZm9yIChqIGluIDEyOjE3KXsNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImNoZWNrYm94IilbW2pdXSRjbGlja0VsZW1lbnQoKQ0KICB9DQogIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsIHNhdmVidG5bMl0pJGNsaWNrRWxlbWVudCgpDQogIFN5cy5zbGVlcCgxKQ0KICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzJdXSRjbGlja0VsZW1lbnQoKQ0KICANCiAgIzMgY3ljbGluZw0KICBpZihyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtY29udGVudCIpW1s2XV0kZ2V0RWxlbWVudFRleHQoKVtbMV1dID09ICIiKXsNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzZdXSRjbGlja0VsZW1lbnQoKSAgDQogIH0NCiAgZm9yIChqIGluIDI2OjMyKXsNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImNoZWNrYm94IilbW2pdXSRjbGlja0VsZW1lbnQoKQ0KICB9DQogIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsIHNhdmVidG5bNl0pJGNsaWNrRWxlbWVudCgpDQogIFN5cy5zbGVlcCgxKQ0KICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzZdXSRjbGlja0VsZW1lbnQoKQ0KICANCiAgI25vdyBhZGQgb3RoZXIgYWNpdGl2aXRpZXMgbXlzZWxmDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9BY3Rpdml0aWVzLmNzaHRtbCIpDQogIA0KICAjdGhpcyBjYW4gb2YgY291cnNlIGJlIGFkanVzdGVkDQogIHNwb3J0cyA8LSBjKCJSU0MgRml0bmVzcyAodnJpaiBzcG9ydGVuKSIsICJSU0MgQ3Vyc3VzIiwgIlJTQyBUaWNrZXR1dXIiLCAiUm9laWVuIiwgIlRlbm5pcy9TcXVhc2giLCAiS2xpbW1lbi9Cb3VsZGVyZW4iLCAiVmVjaHRzcG9ydCIsICJPdmVyaWdlIHNwb3J0IikNCiAgDQogIGZvciAoaiBpbiB1bmlxdWUoc3BvcnRzKSl7DQogICAgU3lzLnNsZWVwKC41KQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsICJBVHlwZU5hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGopKQ0KICAgIFN5cy5zbGVlcCguNSkNCiAgICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAic2F2ZUJ1dHRvbiIpJGNsaWNrRWxlbWVudCgpDQogIH0NCiAgDQogICNhbmQgc3VidHlwZSBmb3IgJ2NvdXJzZXMnDQogIGNvdXJzZXMgPC0gYygiS3JhY2h0dHJhaW5pbmciLCAiT3ZlcmlnIikNCiAgDQogIGlmKHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAidy1ib3gtY29udGVudCIpW1sxNl1dJGdldEVsZW1lbnRUZXh0KClbWzFdXT09IiIpew0KICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAidy1ib3gtaGVhZGVyIilbWzE2XV0kY2xpY2tFbGVtZW50KCkNCiAgfQ0KICBmb3IgKGogaW4gdW5pcXVlKGNvdXJzZXMpKXsNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJpZCIsICJTdWJUeXBlTmFtZSIpW1sxNF1dJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoaikpDQogICAgU3lzLnNsZWVwKC41KQ0KICAgIHJlbURyJGZpbmRFbGVtZW50cygiaWQiLCAic2F2ZVN1YlR5cGUiKVtbMTRdXSRjbGlja0VsZW1lbnQoKQ0KICB9DQogIA0KICAjbG9nIG91dA0KICBTeXMuc2xlZXAoLjUpDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ1VpdGxvZ2dlbicpJGNsaWNrRWxlbWVudCgpDQp9DQpgYGANCg0KPGJyPg0KDQojIExpbmsgYWNjb3VudHMgdG8gbWFzdGVyLWFjY291bnQgeyNsaW5rfQ0KDQpOb3cgdGhhdCBvdXIgYXRobGV0ZS1hY2NvdW50cyBhcmUgY3JlYXRlZCwgd2UgbmVlZCB0byBsaW5rIHRoZW0gdGhlIG91ciBjb2FjaGluZyBhY2NvdW50cy4NCg0KIyMgc2VuZCBpbnZpdGF0aW9ucw0KRmlyc3QsIHdlIHNlbmQgY29hY2hpbmcgaW52aXRhdGlvbnMgdXNpbmcgb3VyIGNvYWNoaW5nIGFjY291bnQuDQoNCkkgc2F2ZWQgbXkgbG9naW4gY3JlZGVudGlhbHMgaW4gbXkgc2VjcmV0LWZvbGRlci4gU28sIGZpcnN0IG1ha2UgYSBgc2VjcmV0YC1mb2xkZXIsIGluIHdoaWNoIHlvdSBjYW4gc3RvcmUgeW91ciBvd24gY3JlZGVudGlhbHMuDQoNCmBgYHtyLCBldmFsPUZ9DQppZmVsc2UoIWRpci5leGlzdHMoInNlY3JldCIpLCBkaXIuY3JlYXRlKCJzZWNyZXQiKSwgRkFMU0UpDQpgYGANCg0KYGBge3IsIGV2YWw9Rn0NCiNuYXZpZ2F0ZSB0byBsb2dpbiBwYWdlDQpyZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vbG9naW4uY3NodG1sP0Fzc29jPSZwYWdlX3JlZGlyZWN0PS8iKQ0KDQojbG9hZCBtYXN0ZXIgYWNjb3VudCBjcmVkZW50aWFscw0KbG9hZCgiLi9zZWNyZXQvbWFzdGVyX2FjY291bnQuUiIpDQoNCiNsb2dpbg0KcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9uYW1lIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChtYXN0ZXIkZW1haWwpKQ0KcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9wYXNzd29yZCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QobWFzdGVyJHBhc3N3b3JkKSkNCnJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJidG4iKVtbMV1dJGNsaWNrRWxlbWVudCgpDQoNCiNpbnZpdGUgYWxsIGF0aGxldGVzIGJlbG9uZ2luZyB0byBhIGdyb3VwL3RlYW0gYXQgb25jZQ0KZm9yIChpIGluIHVuaXF1ZShhY2NvdW50cyRncm9lcCkpIHsgDQogIFN5cy5zbGVlcCg1KQ0KICAjbWFuYWdlIGF0aGxldGVzDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Db2FjaEF0aGxldGVzLmNzaHRtbCIpDQogIA0KICAjZ2V0IGVtYWlsYWRyZXNzZXMgb2YgYXRobGV0ZXMgYmVsb25naW5nIHRvIGdyb3VwIGkNCiAgZW1haWxzIDwtIGFjY291bnRzJGVtYWlsW3doaWNoKGFjY291bnRzJGdyb2VwPT1pKV0NCiAgDQogICNzZW5kIGtleXMgdG8gZWxlbWVudDsgcHV0IGNvbW1hIGluIGJldHdlZW4NCiAgI3JlbURyJGZpbmRFbGVtZW50KCJpZCIsICJBdGhsZXRlRW1haWwiKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJBdGhsZXRlRW1haWwiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KA0KICAgIHBhc3RlKGVtYWlscyxjb2xsYXBzZT0iLFxuIikpKQ0KDQogICNzZWxlY3QgdGVhbQ0KICAjcmVtRHIkZmluZEVsZW1lbnQoImlkIiwiQXRobGV0ZVRlYW0iKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgU3lzLnNsZWVwKDEpDQogIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsIkF0aGxldGVUZWFtIikkY2xpY2tFbGVtZW50KCkNCiAgDQogICNnZXQgaW5uZXIgaG1sDQogIGlubmVyIDwtIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAiQXRobGV0ZVRlYW0iKSRnZXRFbGVtZW50QXR0cmlidXRlKCJpbm5lckhUTUwiKVtbMV1dDQogIGlubmVyICU+JQ0KICAgIHJlYWRfaHRtbCgpICU+JSAjcmVhZCBodG1sDQogICAgaHRtbF9ub2Rlcygib3B0aW9uIikgLT4gdGVhbXMgI2dldCBhbGwgb3B0aW9uIHRhZ3MNCg0KICAjc2VsZWN0aW9uIG9wdGlvbiBjb3JyZXNwb25kaW5nIHRvIHRlYW0gaSwgdXNpbmcgeHBhdGgNCiAgI3JlbURyJGZpbmRFbGVtZW50KCJ4cGF0aCIsIHBhc3RlMCgiLy9zZWxlY3RbQGlkID0gJ0F0aGxldGVUZWFtJ10vb3B0aW9uWyIsIHdoaWNoKGdyZXBsKGksIHRlYW1zKSksICJdIikpJGhpZ2hsaWdodEVsZW1lbnQoKQ0KICByZW1EciRmaW5kRWxlbWVudCgieHBhdGgiLCBwYXN0ZTAoIi8vc2VsZWN0W0BpZCA9ICdBdGhsZXRlVGVhbSddL29wdGlvblsiLCB3aGljaChncmVwbChpLCB0ZWFtcykpLCAiXSIpKSRjbGlja0VsZW1lbnQoKQ0KICANCiAgI2NvbnRpbnVlDQogICNyZW1EciRmaW5kRWxlbWVudCgiaWQiLCAic2F2ZUJ1dHRvbiIpJGhpZ2hsaWdodEVsZW1lbnQoKQ0KICBTeXMuc2xlZXAoMSkNCiAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgInNhdmVCdXR0b24iKSRjbGlja0VsZW1lbnQoKQ0KICBTeXMuc2xlZXAoMikNCiAgDQogICNpbnZpdGUgDQogICNyZW1EciRmaW5kRWxlbWVudCgiaWQiLCAibXVsdGlCdXR0b24iKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgIm11bHRpQnV0dG9uIikkY2xpY2tFbGVtZW50KCkNCn0NCg0KYGBgDQoNCjxicj4NCg0KIyMgYWN0aXZhdGUgc29jaWFsIHdhbGxzDQpOb3cgdGhhdCBhbGwgYXRobGV0ZXMgYXJlIGFzc2lnbmVkIHRvIChpbnZpdGVkIGZvcikgYSB0ZWFtLCB3ZSBhY3RpdmF0ZSBhIFNvY2lhbCBXYWxsIGZvciB0ZWFtcyB0aGF0IGFyZSBhc3NpZ25lZCB0aGUgZXhwZXJpbWVudGFsIGNvbmRpdGlvbi4NCg0KVGhpcyBtdXN0IGJlIGRvbmUgdmlhIHRoZSBiZXRhLXBsYXRmb3JtIG9mIEZTdXJnZS4NCg0KYGBge3IsIGV2YWw9Rn0NCiNuYXZpZ2F0ZSB0byB0aGUgYmV0YS1wbGF0Zm9ybQ0KcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInB0aXBfcyIpW1s5XV0kY2xpY2tFbGVtZW50KCkNCg0KI25hdmlnYXRlIHRvIHRoZSBzb2NpYWwgd2FsbCBwYWdlDQpyZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9iZXRhLmZpbmFsc3VyZ2UuY29tL3NvY2lhbC13YWxsIikNCg0KZm9yIChpIGluIHVuaXF1ZShhY2NvdW50cyRncm9lcCkpIHsgIyBmb3IgZ3JvdXBzL3RlYW1zDQoNCiAgI3RoYXQgd2VyZSBhc3NpZ25lZCB0aGUgc29jaWFsIHdhbGwgY29uZGl0aW9uDQogIGlmKGFjY291bnRzJHNvY2lhbHdhbGxbYWNjb3VudHMkZ3JvZXA9PWldWzFdPT0xKSB7DQogICAgDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIiwgInJpY2gtc2VsZWN0X190cmlnZ2VyIikkY2xpY2tFbGVtZW50KCkNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIiwgInJpY2gtbGlzdF9fYWN0aW9uIikkY2xpY2tFbGVtZW50KCkNCiAgICANCiAgICAjbmFtZSB0aGUgd2FsbC4gSSBqdXN0IGNhbGwgYWxsIHdhbGxzICJTb2NpYWwgd2FsbCINCiAgICAjcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJmcy1mb3JtLWlucHV0IilbWzJdXSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgImZzLWZvcm0taW5wdXQiKVtbMl1dJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoIlNvY2lhbCBXYWxsIikpDQogICAgDQogICAgI2FkZCB0ZWFtDQogICAgU3lzLnNsZWVwKC41KQ0KICAgICNyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgInJpY2gtc2VsZWN0X190cmlnZ2VyIilbWzJdXSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgInJpY2gtc2VsZWN0X190cmlnZ2VyIilbWzJdXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICN0aGlzIGlzIHJhdGhlciBjb21wbGljYXRlZC4uLiBpIGV4dHJhY3QgaW5uZXIgaHRtbCBvZiBlbGVtZW50IG9mIGNsYXNzICJlbC1wb3BvdmVyIiwNCiAgICAjYnV0IHNpbmNlIHRoZSBpZCBjaGFuZ2VzIGVhY2ggaXRlcmF0aW9uIChkb24ndCBrbm93IHdoeS4uLiksIGZpcnN0IGV4dHJhY3QgdGhpcyBpZA0KICAgIGlubmVyIDwtIHJlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJmcy1tb2RhbF9fY29udGVudCIpJGdldEVsZW1lbnRBdHRyaWJ1dGUoImlubmVySFRNTCIpW1sxXV0NCiAgICBpbm5lciAlPiUNCiAgICAgIHN0cl9zcGxpdCgiZWwtcG9wb3ZlciIpICU+JQ0KICAgICAgLltbMV1dICU+JQ0KICAgICAgLltsZW5ndGgoLildICU+JQ0KICAgICAgc3RyX3NwbGl0KCIgIikgJT4lDQogICAgICAuW1sxXV0gJT4lDQogICAgICAuWzFdICU+JQ0KICAgICAgc3Vic3RyKDIsbmNoYXIoLiktMSkgLT4gaWQNCiAgICANCiAgICAjbm93IGdldCBpbm5lciBodG1sIG9mIGVsZW1lbnQgd2l0aCBpZCBlbC1wb3BvdmVyLSdpZCcNCiAgICBpbm5lciA8LXJlbURyJGZpbmRFbGVtZW50KCJpZCIsIHBhc3RlMCgiZWwtcG9wb3Zlci0iLGlkKSkkZ2V0RWxlbWVudEF0dHJpYnV0ZSgiaW5uZXJIVE1MIilbWzFdXQ0KICAgIA0KICAgICNleHRyYWN0IG9wdGlvbnMgKHRlYW1zKTsgYmFzZWQgb24gaXRlbS10ZXh0DQogICAgaW5uZXIgJT4lDQogICAgICBzdHJfc3BsaXQoICJyaWNoLWxpc3QtaXRlbV9fdGV4dCIpICU+JQ0KICAgICAgLltbMV1dIC0+IG9wdGlvbnMNCiAgICANCiAgICAjZ2V0IGluZGljYXRvciBvZiBpdGVtLXRleHQgdGhhdCBjb3JyZXNwb25kcyB0byB0ZWFtLW5hbWUgb2YgaQ0KICAgIGluZCA8LSB3aGljaChncmVwbChpLG9wdGlvbnMpKQ0KICAgIA0KICAgICNjbGljayBjaGVja2JveDsNCiAgICAjbm90ZSB0aGUgIm1pbnVzIDEiIChiZWNhdXNlIHRoZSAiYWxsIHRlYW1zIi1jaGVja2JveCBpcyBub3QgYSByaWNoLWxpc3QtaXRlbV9fY2hlY2tib3gpLi4uDQogICAgI3JlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAicmljaC1saXN0LWl0ZW1fX2NoZWNrYm94IilbW2luZC0xXV0kaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJyaWNoLWxpc3QtaXRlbV9fY2hlY2tib3giKVtbaW5kLTFdXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICNjcmVhdGUgd2FsbA0KICAgICN0aGVyZSBhcmUgbXVsdGlwbGUgZWxlbWVudHMgb2YgY2xhc3MgYnV0dG9uX19jb250ZW50Lg0KICAgICNidXQgdGhlIGxhc3Qgb25lICh0aHVzIGVsZW1lbnQgZXF1YWwgdG8gbGVuZ3RoIG9mIGxpc3Qgb2YgdGhlc2UgZWxlbWVudHMpIGlzDQogICAgI3RoZSBjcmVhdGUgc29jaWFsIHdhbGwgYnV0dG9uDQogICAgU3lzLnNsZWVwKC41KQ0KICAgIGxpc3QgPC0gcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidXR0b25fX2JvcmRlciIpDQogICAgI3JlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAiYnV0dG9uX19ib3JkZXIiKVtbbGVuZ3RoKGxpc3QpXV0kaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidXR0b25fX2JvcmRlciIpW1tsZW5ndGgobGlzdCldXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgIFN5cy5zbGVlcCgyKQ0KICB9DQp9DQoNCiNET05FISBub3cgbG9nIG91dA0KI3JlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJwcm9maWxlLWluZm8iKSRoaWdobGlnaHRFbGVtZW50KCkNCnJlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJwcm9maWxlLWluZm8iKSRjbGlja0VsZW1lbnQoKQ0KI3JlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAibWVudS1jb2xsYXBzZWQiKVtbMTBdXSRoaWdobGlnaHRFbGVtZW50KCkNCnJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MiLCAibWVudS1jb2xsYXBzZWQiKVtbMTBdXSRjbGlja0VsZW1lbnQoKQ0KDQojdGhlcmUgc2VlbXMgdG8gYmUgYSBidWc6IGlmIGkgbmF2aWdhdGUgdG8gdGhlICdyZWd1bGFyJy9ub24tYmV0YSB2ZXJzaW9uIG9mIEZTdXJnZSwgaSBhbSBzdGlsbCBsb2dnZWQgaW4uIHNvIGFsc28gbG9nb3V0IHZpYSByZWd1bGFyIHZlcnNpb24NCnJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS8iKQ0KcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnVWl0bG9nZ2VuJykkY2xpY2tFbGVtZW50KCkNCnJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ0xvZyBpbiBhY2NvdW50JykkY2xpY2tFbGVtZW50KCkNCmBgYA0KDQo8YnI+DQoNCiMjIGFjY2VwdCBpbnZpdGF0aW9ucw0KQXMgYSBsYXN0IHN0ZXAsIEkgYWNjZXB0IHRoZSBjb2FjaGluZyBpbnZpdGF0aW9ucyB2aWEgdGhlIGF0aGxldGUtYWNjb3VudHMuDQoNCmBgYHtyLCBldmFsPUZ9DQpmb3IgKGkgaW4gMTpuKSB7DQogIA0KICBwcmludChwYXN0ZSgiYWNjZXB0aW5nIGludml0YXRpb246IiwgYWNjb3VudHMkZW1haWxbaV0pKQ0KICANCiAgI25hdmlnYXRlIHRvIGxvZ2luIHBhZ2UNCiAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tLyIpDQogIA0KICAjbG9naW4NCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9uYW1lIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChhY2NvdW50cyRlbWFpbFtpXSkpDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fcGFzc3dvcmQiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGFjY291bnRzJHBhc3dvb3JkW2ldKSkNCiAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgDQogIFN5cy5zbGVlcCgxKQ0KICANCiAgI25hdmlnYXRlIHRvIGludml0YXRpb24NCiAgcmVtRHIkbmF2aWdhdGUocGFzdGUwKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS8iLCAiaW52aXRhdGlvbi5jc2h0bWwiKSkNCg0KICAjYWNjZXB0IHJlcXVlc3QNCiAgU3lzLnNsZWVwKC41KQ0KICAjcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidG4iKVtbMV1dJGhpZ2hsaWdodEVsZW1lbnQoKQ0KICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgU3lzLnNsZWVwKC41KQ0KICAjbG9nIG91dA0KICAjcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnVWl0bG9nZ2VuJykkaGlnaGxpZ2h0RWxlbWVudCgpDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ1VpdGxvZ2dlbicpJGNsaWNrRWxlbWVudCgpDQoNCn0NCg0KYGBgDQoNCi0tLS0NCg0KPGJyPg0KDQojIFBlcnNvbmFsaXplIGFjY291bnRzIHsjcGVyc30NCg0KPCEtLS0NCg0KQFJGOiBkb24ndCBmb3JnZXQgdG8gdHVybiBvZiB0aGUgd2FsbC11cGRhdGVyLi4uDQoNCi0tPg0KDQpSZWNydWl0ZWQgcGFydGljaXBhbnRzIGZpbGwgb3V0IGEgc2hvcnQgKHByZS1pbnRlcnZlbnRpb24pIHN1cnZleSwgaW4gd2hpY2ggdGhleSBnaXZlIC0gYW1vbmcgb3RoZXJzIC0gdGhlaXIgZmlyc3QgYW5kIGxhc3QgbmFtZS4gV2UgdXNlIHRoZXNlIGRhdGEgdG8gJ3BlcnNvbmFsaXplJyB0aGUgYWNjb3VudHMuDQoNClNldHVwOg0KYXQgdGhlIGVuZCBvZiBlYWNoIHJlY3J1aXRtZW50IGRheSAoYW5kIHRoZSB3ZWVrIGFmdGVyIHJlY3J1aXRtZW50KToNCg0KMS4gbG9hZCAodXBkYXRlZCkgcHJlLXNldCBgYWNjb3VudHNgIGRmDQoyLiBsb2FkICh1cGRhdGVkKSBwcmUtaW50ZXJ2ZW50aW9uIHN1cnZleSByZXNwb25zZXMNCjMuIGdldCBhbGwgb2YgdG9kYXkncyByZXNwb25zZXMgKGkuZS4sIGNvbXBsZXRlZCBiZWZvcmUgbWlkbmlnaHQpDQo0LiBmb3IgZWFjaCBwYXJ0aWNpcGFudCwgJ3BlcnNvbmFsaXplJyB0aGUgYWNjb3VudCB1c2luZyBmaXJzdCBhbmQgbGFzdG5hbWUNCjUuIHVwZGF0ZSAoYW5kIGBmc2F2ZWApIHRoZSBgYWNjb3VudHNgIGRmIGJ5IHNldHRpbmcgYG9jY3VwaWVkPVRSVUVgLCBhbGxvd2luZyBtZSB0bzogYS4gYXNzaWduIG5ldyBwYXJ0aWNpcGFudHMgdG8gbm9uLW9jY3VwaWVkIGFjY291bnRzIGFuZCBiLiBpZGVudGlmeSBhY2NvdW50cyB0aGF0IG5lZWQgdG8gYmUgbG9vcGVkIG92ZXIgZm9yIHRoZSBleHBlcmltZW50LiBJIGFsc28gYXR0YWNoIHBhcnRpY2lwYW50cycgZW1haWwgdG8gdGhlIGRhdGFmcmFtZSwgc28gdGhhdCBJIGNhbiBsaW5rIEZTdXJnZSBkYXRhIHdpdGggc3VydmV5IChhbmQgYmVoYXZpb3JhbCB0cmFjZSkgZGF0YS4NCg0KDQpgYGB7ciBldmFsPUZ9DQojMSBsb2FkIG5ld2VzdCB2ZXJzaW9uIG9mIGFjY291bnRzIGRmDQojd2l0aCBvY2N1cGllZD1UUlVFIGZvciBwcmUtc2V0IGFjY291bnRzIHRoYXQgYXJlIHBlcnNvbmFsaXplZC9yZWFkeSBmb3IgdXNlLiANCg0KI2lmIG5ld2VzdCB2ZXJzaW9uIHdhcyBzYXZlZCB5ZXN0ZXJkYXksIHRoaXMgd29ya3M6DQojYWNjb3VudHMgPC0gZmxvYWQocGFzdGUwKCIuL2xvY2FsLyIsIHN1YnN0cihnc3ViKCJbOi1dIiwgIiIsIFN5cy5EYXRlKCktMSksIDEsIDgpLCAiYWNjb3VudHMuUiIpKQ0KIyBvciBqdXN0OiBsb2FkKCIuL2xvY2FsL2FjY291bnRzLlIiKSAjaWYgSSBzYXZlIHRoZSB1cGRhdGVkIHZlcnNpb24gd2l0aCB0aGUgYmFzZS1SIHNhdmUgZnVuY3Rpb24uDQphY2NvdW50cyA8LSBmbG9hZCgiLi9sb2NhbC9hY2NvdW50cy5SIikNCmZpeChhY2NvdW50cykNCg0KIzIgbG9hZCAobW9zdCByZWNlbnQpIHByZS1pbnRlcnZlbnRpb24gcmVzcG9uc2VzDQojYSBuZXcgdmVyc2lvbi9leHBvcnQgc2hvdWxkIGJlIGNvcGllZCB0byB0aGUgbG9jYWwgZm9sZGVyIGVhY2ggZGF5L2l0ZXJhdGlvbi4uLg0KZGYgPC0gcmVhZC5jc3YoIi4vbG9jYWwvdm9vcm1ldGluZy9yZXN1bHRzLXN1cnZleTExOTUzNS5jc3YiKQ0KI3RoZXNlIGFyZSAqY29tcGxldGUqIHJlc3BvbnNlcyENCg0KIzMgZ2V0IHRvZGF5J3MgcmVzcG9uc2VzIChiYyBwcmV2aW91cyByZXNwb25zZXMgYXJlIGFscmVhZHkgaGFuZGxlZCEpDQpkZiA8LSBkZlt3aGljaChhcy5EYXRlKGRmJHN1Ym1pdGRhdGUpPT1TeXMuRGF0ZSgpKSxdDQojb3IgbGFzdCBmZXcuLi4gZGYgPC0gdGFpbChkZixYKQ0KI2Fsc28gcmVtb3ZlICd0ZXN0JyByZXNwb25zZXMsIHdoaWNoIHdlcmUgaW5kaWNhdGVkIHdpdGggZmlyc3RuYW1lICd0ZXN0Jw0KZGYgPC0gZGZbd2hpY2goIXRvbG93ZXIoZGYkZW1haWxzLjMuKT09InRlc3QiKSxdDQoNCiM0IHBlcnNvbmFsaXplIGFjY291bnRzIGZvciB0aGVzZSByZXNwb25kZW50cw0KIzUgdXBkYXRlIGFjY291bnRzIGRmIGJ5IHNldHRpbmcgb2NjdXBpZWQ9VFJVRQ0KDQojcHJlcGFyYXRpb246DQojbWFrZSBncm91cHMgbmFtZXMgaW4gc3VydmV5IGRmIHJlc2VtYmxlIHRob3NlIG9mIHRoZSAnYWNjb3VudHMnIGRmDQpkZiRTMCA8LSBpZmVsc2UoZGYkUzAgPT0gIktyYWNodHRyYWluaW5nIGJhc2lzIGJlZ2lubmVyIC0gbWFhbmRhZyAoMTc6NDUtMTk6MDApIiwgIktyYWNodHRyYWluaW5nIGJhc2lzIC0gbWFhbmRhZyIsDQogICAgICAgICAgICAgICAgIGlmZWxzZShkZiRTMCA9PSAiS3JhY2h0dHJhaW5pbmcgc3BvcnRzcGVjaWZpZWsgLSBtYWFuZGFnICgyMTowMC0yMjoxNSkiLCAgIktyYWNodHRyYWluaW5nIHNwb3J0c3BlY2lmaWVrIC0gbWFhbmRhZyIsDQogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGYkUzAgPT0gIktyYWNodHRyYWluaW5nIGJhc2lzIGJlZ2lubmVyIC0gd29lbnNkYWcgKDIwOjMwLTIxOjQ1KSIsIktyYWNodHRyYWluaW5nIGJhc2lzIC0gd29lbnNkYWciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkZiRTMCA9PSAiS3JhY2h0dHJhaW5pbmcgdnJvdXdlbiAtIHdvZW5zZGFnICgxODowMC0xOToxNSkiLCAiS3JhY2h0dHJhaW5pbmcgdnJvdXdlbiAtIHdvZW5zZGFnIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGRmJFMwID09ICJLcmFjaHR0cmFpbmluZyB2cm91d2VuIC0gZG9uZGVyZGFnICgxNTo0NS0xNzowMCkiLCAiS3JhY2h0dHJhaW5pbmcgdnJvdXdlbiAtIGRvbmRlcmRhZyIsIE5BKSkpKSkNCiNhbHNvIG1ha2Ugc3VyZSBSc2VsZW5pdW0gYnJvd3NlciBpcyAoc3RpbGwpIGFjdGl2ZS4NCg0KDQpmb3IgKGkgaW4gdW5pcXVlKGRmJFMwKSkgeyAjZm9yIGVhY2ggdW5pcXVlIGdyb3VwDQoNCiAgI3JldHJpZXZlIHRoZSBzdXJ2ZXkgcmVzcG9uZGVudHMgdGhhdCByZXBvcnRlZCB0byBiZSBwYXJ0IG9mIHRoaXMgZ3JvdXANCiAgZGZfc3ViIDwtIGRmW3doaWNoKGRmJFMwPT1pKSxdDQogICNob3cgbWFueSBhcmUgdGhlcmUgKGllLCBuZXcgb25lcyk/DQogIG4gPC0gbnJvdyhkZl9zdWIpDQogIA0KICAjZm9yIGVhY2ggYXRobGV0ZSBqIGJlbG9uZ2luZyB0byB0aGlzIGdyb3VwLCBpbiAxOm4sIA0KICAjJ3BlcnNvbmFsaXplJyB0aGUgYWNjb3VudA0KICANCiAgZm9yIChqIGluIDE6bikgew0KDQogICAgU3lzLnNsZWVwKDEpDQogICAgDQogICAgI2dldCBwZXJzb25hbCBkYXRhIGZyb20gc3VydmV5DQogICAgZmlyc3RuYW1lIDwtIGRmX3N1YiRlbWFpbHMuMy5bal0NCiAgICBsYXN0bmFtZSA8LSBzdWJzdHIoZGZfc3ViJGVtYWlscy40LltqXSwxLDEpDQogICAgZW1haWwgPC0gZGZfc3ViJGVtYWlscy4xLltqXQ0KICAgIA0KICAgIFN5cy5zbGVlcCgxKQ0KDQogICAgI25hdmlnYXRlIGxvZyBpbiBwYWdlDQogICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL2xvZ2luLmNzaHRtbCIpDQogICAgDQogICAgI3RoaXMgcmVzcG9uZGVudCBnZXRzIGFzc2lnbmVkIHRoZSBmaXJzdCBlbnRyeSBmcm9tIHRoZSBgYWNjb3VudHNgIGRmLCB3aXRoIGdyb3VwPT1pICYgb2NjdXBpZWQ9PUYpDQogICAgZW50cnkgPC0gYWNjb3VudHNbd2hpY2goYWNjb3VudHMkZ3JvZXA9PWkgJiBhY2NvdW50cyRvY2N1cGllZD09RilbMV0sXQ0KICAgIA0KICAgICNsb2dpbiB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGFjY291bnQNCiAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImxvZ2luX25hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGVudHJ5JGVtYWlsKSkNCiAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImxvZ2luX3Bhc3N3b3JkIikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChlbnRyeSRwYXN3b29yZCkpDQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgICANCiAgICAjbmF2aWdhdGUgZWRpdCBzZXR0aW5ncw0KICAgIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Vc2VyUHJvZmlsZS5jc2h0bWw/ZWRpdD1wI3Byb2ZpbGUiKQ0KICAgICNyZW1EciRmaW5kRWxlbWVudCgiaWQiLCAiZm5hbWUiKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAiZm5hbWUiKSRjbGVhckVsZW1lbnQoKQ0KICAgIFN5cy5zbGVlcCgxKQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsICJmbmFtZSIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoZmlyc3RuYW1lKSkNCiAgICBTeXMuc2xlZXAoLjUpDQogICAgcmVtRHIkZmluZEVsZW1lbnQoImlkIiwgImxuYW1lIikkY2xlYXJFbGVtZW50KCkNCiAgICBTeXMuc2xlZXAoMSkNCiAgICByZW1EciRmaW5kRWxlbWVudCgiaWQiLCAibG5hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGxhc3RuYW1lLCBrZXk9ImVudGVyIikpDQogICAgDQogICAgU3lzLnNsZWVwKDIpDQogIA0KICAgICNsb2dvdXQNCiAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICdsaW5rIHRleHQnLCdVaXRsb2dnZW4nKSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICNhY2NvdW50IGlzIG5vdyBvY2N1cGllZCwgc28gc2V0IHRvIFRSVUUgKHNvIHRoYXQgaXQgd2lsbCBiZSBza2lwcGVkIGZvciB0aGUgbmV4dCByZXNwb25kZW50KQ0KICAgICNhY2NvdW50c1t3aGljaChhY2NvdW50cyRncm9lcD09aSAmIGFjY291bnRzJG9jY3VwaWVkPT1GKVsxXSxdDQogICAgI2J1dCBmaXJzdCBhZGQgdGhlIGVtYWlsIGFkcmVzcyBvZiB0aGUgY29ycmVzcG9uZGluZyBwYXJpdGljcGFudHMsIGFzIGFuIGlkZW50aWZpZXINCiAgICBhY2NvdW50cyRpZFt3aGljaChhY2NvdW50cyRncm9lcD09aSAmIGFjY291bnRzJG9jY3VwaWVkPT1GKVsxXV0gPC0gZW1haWwNCiAgICBhY2NvdW50cyRvY2N1cGllZFt3aGljaChhY2NvdW50cyRncm9lcD09aSAmIGFjY291bnRzJG9jY3VwaWVkPT1GKVsxXV0gPC0gVFJVRQ0KICB9DQp9DQpmaXgoYWNjb3VudHMpDQojYW5kIG1ha2Ugc3VyZSB0byBzYXZlIHRoZSBhY2NvdW50cyBkZiBhcyBhIG5ldyB2ZXJzaW9uDQpmc2F2ZShhY2NvdW50cywiYWNjb3VudHMuUiIpDQojYW5kL29yIGp1c3QgYXMgYGFjY291bnRzLlJgLCBiYyB0aGVuIGl0IGZlZWRzIGludG8gdGhlIHNvY2lhbHdhbGwgdXBkYXRlci4uLj8NCnNhdmUoYWNjb3VudHMsZmlsZT0iLi9sb2NhbC9hY2NvdW50cy5SIikNCmBgYA0KDQoNCjwhLS0tLQ0KQFJGOiBhdCB0aGUgZW5kIG9mIGVhY2ggZGF5LCBleHRyYWN0IGFsbCBhY3RpdmF0ZWQgKHJlYWR5LWZvci11c2UpIGFjY291bnRzLCBzYXZlIGFzIGEgZGYsIGltcG9ydCBpbiBMUywgYW5kIHNlbmQgYSBtZXNzYWdlIHRvIHBhcnRpY2lwYW50czogIlRoYW5rcyBmb3IgcGFydGljaXBhdGluZywgaGVyZSBhcmUgeW91ciBsb2dpbiBjcmVkZW50aWFscy4gRm9yIGluc3RydWN0aW9ucyBmb3IgdHJhY2tpbmcgYWN0aXZpdGllcywgY2hlY2sgb3VyIHdlYnNpdGU6IGh0dHBzOi8vd3d3LmtyYWNodGlncnNjLm5sL2luc3RydWN0aWUuaHRtbC4gR29vZCBsdWNrISIgDQotLS0tPg0KDQotLS0tDQoNCjxicj4NCg0KIyBSZW1vdmUgYWNjb3VudHMgeyNyZW1vdmV9DQoNCg0KIyMgVW5saW5rIGZyb20gY29hY2hpbmcgYWNjb3VudA0KDQpJZiB0aGUgZXhwZXJpbWVudCBpcyBkb25lLCBgciBjb2xvcml6ZSgiKHdoZW4gYWxsIGFjdGl2aXR5IGFuZCBzb2NpYWwgd2FsbCBkYXRhIGFyZSBoYXJ2ZXN0ZWQhISEpIiwgInJlZCIpYCwgd2UgcmVtb3ZlIHRoZSBhY2NvdW50cyBmcm9tIG91ciBjb2FjaGluZyBhY2NvdW50Lg0KDQpgYGB7ciwgZXZhbD1GfQ0KRklOSVNIRUQgPSBGQUxTRQ0KDQppZihGSU5JU0hFRD09VFJVRSl7DQogIA0KICByZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vbG9naW4uY3NodG1sP0Fzc29jPSZwYWdlX3JlZGlyZWN0PS8iKQ0KICANCiAgI2xvZ2luDQogIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fbmFtZSIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QobWFzdGVyJGVtYWlsKSkNCiAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJsb2dpbl9wYXNzd29yZCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QobWFzdGVyJHBhc3N3b3JkKSkNCiAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgImJ0biIpW1sxXV0kY2xpY2tFbGVtZW50KCkNCiAgDQogICNnbyB0byBhdGhsZXRlcyBwYWdlDQogIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS9Db2FjaEF0aGxldGVzLmNzaHRtbCIpDQogIA0KICBmb3IgKGkgaW4gMTpsZW5ndGgoYWNjb3VudHMkZW1haWwpKSB7ICNmb3IgYWxsIGFjY291bnRzDQogICAgDQogICAgcHJpbnQocGFzdGUoInVubGlua2luZyBhY2NvdW50OiIsIGFjY291bnRzJGVtYWlsW2ldKSkNCiAgDQogICAgI3NhdmUgYXRobGV0ZSB0YWJsZQ0KICAgIHBhZ2UgPC0gcmVtRHIkZ2V0UGFnZVNvdXJjZSgpDQogICAgdGFibGUgPC0gcGFnZVtbMV1dICU+JQ0KICAgICAgcmVhZF9odG1sKCkgJT4lDQogICAgICBodG1sX25vZGVzKCd0YWJsZScpICU+JSANCiAgICAgIC5bWzFdXSAlPiUNCiAgICAgIGh0bWxfdGFibGUoKSAlPiUNCiAgICAgIGFzLmRhdGEuZnJhbWUoLikgDQogICAgDQogICAgI2dldCBlbWFpbCB0byBhdGhsZXRlIGkNCiAgICBlbWFpbCA8LSBhY2NvdW50cyRlbWFpbFtpXQ0KICAgIA0KICAgICNmaW5kIHdoaWNoIGVudHJ5IGluICh1cGRhdGVkISkgdGFibGUgY29ycmVzcG9uZHMgdG8gdGhpcyBlbWFpbA0KICAgIHggPC0gd2hpY2godGFibGUkRW1haWw9PWVtYWlsKQ0KICAgIA0KICAgICNpZiB0aGVyZSBpcyBhIGNvcnJlc3BvbmRlbmNlLi4uDQogICAgaWYobGVuZ3RoKHgpPjApIHsNCiAgICAgICNlZGl0IGFjY291bnQgd2l0aCB0aGlzIGVtYWlsDQogICAgICAjcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJpY29uLXBlbmNpbCIpW1t4XV0kaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgImljb24tcGVuY2lsIilbW3hdXSRjbGlja0VsZW1lbnQoKQ0KICAgICAgDQogICAgICAjZGVsZXRlIGZyb20gY29hY2hpbmcgYWNjb3VudA0KICAgICAgI3JlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJpY29uLXRyYXNoIikkaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgICByZW1EciRmaW5kRWxlbWVudCgiY2xhc3MiLCAiaWNvbi10cmFzaCIpJGNsaWNrRWxlbWVudCgpDQogICAgICBTeXMuc2xlZXAoMSkNCiAgICAgICNjb25maXJtDQogICAgICAjcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnT0snKSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ09LJykkY2xpY2tFbGVtZW50KCkNCiAgICB9DQogIH0NCg0KfQ0KYGBgDQoNCjxicj4NCg0KIyMgZGVsZXRlIGFjY291bnRzDQpXZSBtYXkgYWxzbyBkZWxldGUgdGhlIGFjY291bnRzLg0KDQpgYGB7ciwgZXZhbD1GfQ0KaWYoRklOSVNIRUQ9PVRSVUUpew0KICANCiAgZm9yIChpIGluIDE6bikgew0KICAgIA0KICAgIHByaW50KHBhc3RlKCJkZWxldGluZyBhY2NvdW50OiIsIGFjY291bnRzJGVtYWlsW2ldKSkNCiAgICANCiAgICAjbmF2aWdhdGUgdG8gbG9naW4gcGFnZQ0KICAgIHJlbURyJG5hdmlnYXRlKCJodHRwczovL2xvZy5maW5hbHN1cmdlLmNvbS8iKQ0KICAgIA0KICAgICNsb2dpbg0KICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fbmFtZSIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoYWNjb3VudHMkZW1haWxbaV0pKQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fcGFzc3dvcmQiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGFjY291bnRzJHBhc3dvb3JkW2ldKSkNCiAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiYnRuIilbWzFdXSRjbGlja0VsZW1lbnQoKQ0KICAgIA0KICAgICNwcm9maWxlIHNldHRpbmdzDQogICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1VzZXJQcm9maWxlLmNzaHRtbD9lZGl0PXAjcHJvZmlsZSIpDQogICAgI2RlbGV0ZQ0KICAgIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsImRlbC11c2VyIikkY2xpY2tFbGVtZW50KCkNCiAgICAjYWNjZXB0DQogICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyIsICJidG4iKVtbNF1dJGNsaWNrRWxlbWVudCgpDQogIH0NCn0NCg0KYGBgDQoNCjxicj4NCg0KbWFrZSBzdXJlIHRvIGVuZCBhIHNlc3Npb24gYnkgdGVybWluYXRpbmcgdGhlIHByb2Nlc3M6DQpgYGB7ciwgZXZhbD1GfQ0KcGlkIDwtIHJEJHNlcnZlciRwcm9jZXNzJGdldF9waWQoKQ0Kc3lzdGVtKHBhc3RlMCgiVGFza2tpbGwgL0YgL1QiICwiIC9QSUQgIiwgcGlkKSkNCmBgYA0KDQo=


Copyright © 2023 Rob Franken