Description
This tool performs a data check and validation over the datasets collected on the Household Survey, for those indicators which might have not been pre-validated by the Kobo form constrains. It generates an excel file highligting all the identified entries with at least one validation error in order to be reviewed back by the enumerators.
Output:
An excel file containing just the new identified shelter on the given period. The column headers are renamed to match the required format and column names
Main functions
This tool does not make use of any specific custom functions.
Configuration
Specify the default data input and output directories, and several data vectors needed by the tool:
HH_members_cols: vector containing the column names to be selected from the Demographic Section (C1)
HH_absent_cols: vector containing the column names to be selected from the Absent Members Section
shelter_lookup: vector containing the values for the Shelter option (B1)
dwelling_map: named vector containg the dwelling types and a code which maps them to the possible shelter types
warning_column_names: vector containing the list of warning messages to be added to the final excel output
suggestion_column_names: vector containing the list of suggested answers to review to be added to the final excel output
Data Load
The input file is expected to be placed under the data subdirectory. The readxl library is used instead of openxlsx as the latter was having some issues in correctly reading the Kobo output. The Household Survey in Kobo stores the collected data in three sheets, the first being the main one and the other two used for the ‘children’ values (in this case the members of a single household, either present or absent). Each of the sheets is stored in a separate dataframe for later processing.
HH_dataset_main <- readxl::read_xlsx(filepath, sheet = 1)
HH_dataset_members <- readxl::read_xlsx(filepath, sheet = 2)
HH_dataset_absent <- readxl::read_xlsx(filepath, sheet = 3)
Process
- We need first to clean a little bit the datasets and remove some ‘false’ columns added by Kobo. Using dplyr and
select(-starts_with("<")) we can remove the culprit columns. The starts_with("<") helper finds those columns matching the given string and the minus sign performs the removal.
#remove warning columns start with < or (
HH_dataset_main <- HH_dataset_main %>% select(-starts_with("<")) %>% select(-starts_with("("))
HH_dataset_members <- HH_dataset_members %>% select(-starts_with("<")) %>% select(-starts_with("("))
HH_dataset_absent <- HH_dataset_absent %>% select(-starts_with("<")) %>% select(-starts_with("("))
- We get the range of indexes corresponding to the columns to be selected from the main dataset. This could also be acommplished by defining a vector of column names under Configuration, however the
get_range_indexes() function (defined in HNAP utils.R library) makes things a little easier as it allows to get the range of several consecutive columns as once, by just specifying the initial and final columns. The get_col_indexes() gets just the index of either one or two columns defined in the parameters. The append() function adds a vector at the end of an existing vector provided as input, in this case the range vector.
# get range of demographic columns to select from HH dataset
range <- get_range_indexes(HH_dataset_main, "Date of Visit", "Pcode = ${admin4}")
range <- append(range, get_col_indexes(HH_dataset_main, "B1. Type of Shelter"))
range <- append(range, get_range_indexes(HH_dataset_main, "Name of HoH" , "HH Type"))
range <- append(range, get_range_indexes(HH_dataset_main, "HH member type of interviewee", "SELF CARE"))
...
- We subset each dataset by selecting only the columns we will be working with. That step is not really necessary as R can deal with the full datasets, though it can actually save some memory and speed things up a little bit. Also it makes easier to work with the datasets when displaying their contents on the View pannel. The *_uuid* gets also renamed using the
rename()function from dplyr
- Before combining the datasets, we need to make sure the column types match. It is common that the expected numerical columns are coerced as non-numerical, either character or unknown type, sometimes due to the presence of
NA values on the original dataset or other reasons. In order to coerce those columns containing values that are expected to remain numerical, we use the type_convert() function from the HNAP utils.R library. This function will perform a ‘soft coertion’ over each dataframe column, affecting only those columns with expected numerical columns (meaning those columns containing only numbers, or numbers and blank cells/NA values). In that case the character numbers will be coerced to numerical and the empty cell will be coerced to NA. If a given column contains any true character value (meaning a non-numerical string of text), it will be left as it is.
- IMPORTANT! We need to apply
type_convert() over all the columns of the dataset, therefore we use the lapply() function to perform this operation over all the column elements of the dataframe (an efficient iteration). The initial output will be a list object containing each converted column. By simply specyfing the output variable as HH_demo[] with the brackets [] we can automatically store back the list elements as dataframe elements (sort of binding them on-the-fly).
# select columns to combine for each dataset and coerce to numeric all those character columns holding numeric values before binding so all columns from each subset match type
HH_demo <- HH_dataset_main %>%
select(range) %>%
rename(uuid = "_uuid")
HH_demo[] <- lapply(HH_demo, type_convert)
HH_members <- HH_dataset_members %>%
select(HH_members_cols) %>%
rename(uuid = "_submission__uuid")
HH_members[] <- lapply(HH_members, type_convert)
HH_absent <- HH_dataset_absent %>%
select(HH_absent_cols) %>%
rename(uuid = "_submission__uuid")
HH_absent[] <- lapply(HH_absent, type_convert)
- We start the processing of the combined
HH_all dataframe using the dplyr library and “pipelining” the different actions, starting with selecting the columns needed for the validation analysis. Some of the columns will be renamed for better manipulation later on the pipeline
HH_data_check_temp <- HH_all %>%
select(uuid,
`Enumerator Code`,
Governorate = Governorate...5,
District = District...6,
Subdistrict = `Sub-district...7`,
Community = Community...8,
Name_HoH = `Name of HoH`,
...
- We use
mutate() to create new additional columns where several logical validations are calculated according to the required constrains. Some of these columns will get TRUE or FALSE values depending on the result of the performed boolean operation.
mutate(check_hhh = (`HH member type` == "Head of Household"),
check_hhh_married = (`HH member type` == "Head of Household" & `Marital status` == "Married"),
check_spouse = (`HH member type` == "Spouse"),
check_child = ((`If no, why?` == "child") | (`If no, why?` == "student" & `Age - Year` < 18)),
check_student_age = ((`If no, why?` == "child" & `Age - Year` >= 6) | (`If no, why?` == "student" & `Age - Year` >= 6)),
check_not_student_age = (`If no, why?` == "child" & `Age - Year` < 6),
check_child_work = (child_work == "1"),
check_child_school = (child_school == "No children in schools"),
...
- Other added columns are used to store calculated values resulting from the sum of the contents of several cells on the same row. As dplyr works columnwise, we need to specify that the sum operation needs to be carried on rowwise. The
rowSums() function will perform the row sums for the selected columns. Please notice that in this case, the select() function needs to explicitely refer the target dataframe as it was at the end of the last “pipelined” operation. We use the . for such reference. This is needed because the use of rowSums() somehow “breaks” the columwise behaviour of dplyr, so we cannot use select() without explicitelly specifying the target dataframe.
water = rowSums(select(.,`i) % for Network`:`ix) % for Other (specify)`), na.rm = TRUE), # sum percentages from different water sources
number_containers = rowSums(select(.,`How many liters does hold the roof tank?`:`How many liters does hold the SmallHouseHolds?`), na.rm = TRUE), # sum total number of containers
perc_spent = rowSums(select(., perc_water, perc_hygiene, perc_garbage, perc_septic), na.rm = TRUE), # sum percentages of income spent
...
- Some new columns require an arithmetical operation which might give a double as output. Even if the column type is the generic numeric, there can be some issues when using
rowSums() later on and referring to this columns, as other numeric columns are integer. Therefore to avoid errors, the calculated column is coerced to integer using as.integer().
hygiene_purchase_monthly = as.integer(hygiene_purchase/3), # divide by 3 to get monthly and coerce to integer to avoid type mismatch
septic_purchase_monthly = as.integer(septic_purchase/3), # divide by 3 to get monthly and coerce to integer to avoid type mismatch
...
- The type of shelter and type of dwelling are related in a 1-to-many fashion as described by the
dwelling_map named vector, which maps a given dwelling to the different type of shelters that can belong. This mapping is coded by referring to the shelter types via their numerical index in the shelter_lookup vector.
- The
match() function allows to look up a given value or pattern inside a lookup vector, in this case the shelter_lookup, and it will return the position index of the value in case a matching is found.
- Once obtained the shelter indexes, we need to check for each shelter index in the column whether it is found in the selected dwelling “map”. This operation is performed by the custom
check_pattern_vector() from the HNAP utils.R library. This function applies the grepl() R function over the whole column using mapply(). For each value of the column shelter_inde(), grepl() will check if the value is found within the mapping string defined for the selected dwelling.
shelter_index = match(shelter, shelter_lookup), # lookup shelter index in shelter_type table
check_dwelling = check_pattern_vector(shelter_index, dwelling_map[dwelling])) %>% # check if shelter index is mapped by the dwelling type
...
- The next step is to group all the rows by a common
uuid in order to calculate summarised results
- When using
group_by, only the selected summarised values are present in the output dataframe. If we want to also include, for informational purposes, columns which are not summarised or aggregated in the output (for example some character type columns), we can accomplish that by using the first() function, which will select the first element found for that columns (as the rest of elements for that column under that group are in any case repeated).
- Some totals are also calculated, such as total_hh_income, as they will be needed to perform specific logical tests on the *_flag* columns.
- The *_flag* are where the final logic checks for the specifics constraints are performed. The output will be
TRUE or FALSE depending on whether the validation test has been passed or not for that constraint.
group_by(uuid) %>%
summarise(enumerator = first(`Enumerator Code`),
Governorate = first(Governorate),
District = first(District),
Subdistrict = first(Subdistrict),
Community = first(Community),
Name_HoH = first(Name_HoH),
total_hh_income = sum(hh_income, na.rm = TRUE),
single_head_flag = ifelse((sum(check_hhh, na.rm = TRUE) == 1), TRUE, FALSE), # C1: there is a single head of household
hh_married = ifelse((single_head_flag == TRUE & sum(check_hhh_married, na.rm = TRUE) == 1) , TRUE, FALSE), # check if married
spouse = ifelse((single_head_flag == TRUE & sum(check_spouse, na.rm = TRUE) >= 1) , TRUE, FALSE), #check if spouse when single head household
spouse_flag = ifelse((hh_married == TRUE & spouse == TRUE) | (hh_married == FALSE & spouse == FALSE), TRUE, FALSE), # C1: check if there are spouse/s if married or no spouse if not married
monthly_income = sum(`Average monthly income in SYP`, na.rm = TRUE), # sum monthly incomes for all household members
income_flag = ifelse(monthly_income <= sum(hh_income, na.rm = TRUE), TRUE, FALSE), # C3: chech if combined monthly income less or equal than reported average monthly income
child = ifelse((sum(check_child, na.rm = TRUE) >= 1), TRUE, FALSE), # check if more than one child in household
...
- We finally add a new columns in order to check if the household has passed all the constraints (all flags are
TRUE). This is performed by calculating the arithmetical mean for that row. The rowMeans() function works in a similar manner as rowSums() in the sense that it breaks the dplyr behaviour and performs the calculation rowwise. For that reason, select() is also used in the same manner as with rowSums(). The ends_with() will make sure all the *_flag* columns are selected. As each TRUE value is automatically coerced to 1, the arithmetical mean will need to equal 1 in case all values are TRUE.
mutate(alltrue_check = rowMeans(select(., ends_with("flag"))))# check mean value of all flags per row. TRUE equals to 1.
...
- The resulting dataframe
HH_data_check_temp will receive now two different treatments depending on its final use, either intended for the Enumerators, or for HNAP Information Management Unit for additional parameters review.
HH_data_check_field stores the final output for the Enumerator in the field. It simply filters out those records that do not need to be reviewed and select the flag columns which highlights the ones which have to be reviewed for each household record.
# HH dataset validated for the Enumerators to check
HH_data_check_field <- HH_data_check_temp %>%
filter(alltrue_check != 1) %>% # filter out rows which are all TRUE, no need to review. All flags TRUE mean coerces to exactly 1.
select(uuid, enumerator, Governorate, District, Subdistrict, Community, Name_HoH, ends_with("flag"))
- For the internal review, there are several columns that might need to be reviewed in order to highlight potential outlier values that could have been recorded accidentally. Two approaches are taken, and for each of them a different dataframe is used for the final output. The first one assumes a nearly normal distribution for the specific column values (for example, the
total_hh_income) and detects whether the value falls within the 95% confidence interval or not (which corresponds approximately to two standard deviations either side of the data mean). The 95% value can be changed in the custom function get_qnorm_range(), which gets the lower and upper quantiles for that specific confidence interval. The final results will then depend on the confidence interval level used, though it might highlight values which though uncommon, still fall within a reasonable margin. In order to check if a given value of total_hh_income is within the calculated range, we use the custom check_in_range() function, as defined within the HNAP utils.R library, which will output TRUE or FALSE depending on the row value for that indicator.
- IMPORTANT! Currently, the script only implements the outliers checking for the
total_hh_income indicator. This can be extended to other indicators as needed, following exactly the same procedure.
# HH dataset validated for internal review of value ranges (using normal distribution probability rule at 95%)
HH_data_check_internal_qnorm <- HH_data_check_temp %>%
mutate(hh_income_range_check = check_in_range(total_hh_income, get_qnorm_range(total_hh_income, 0.95))) %>%
select(uuid, enumerator, Governorate, District, Subdistrict, Community, Name_HoH, total_hh_income, hh_income_range_check, ends_with("flag"))
...
- The second approach takes a more systematic way in order to find the outliers. It makes use of the commonly accepted Interquartile Range Rule or 1.5*IQR rule and could be more effective in order to straightforward detect those anomalous values caused by accidental input. This rule is implemented with the custom function
get_outlier_range().
# HH dataset validated for internal review of value ranges (using 1.5*IQR rule for outlier detection)
HH_data_check_internal_iqr <- HH_data_check_temp %>%
mutate(hh_income_range_check = check_in_range(total_hh_income, get_outlier_range(total_hh_income))) %>%
select(uuid, enumerator, Governorate, District, Subdistrict, Community, Name_HoH, total_hh_income, hh_income_range_check, ends_with("flag"))
...
Output
The script generates an excel output file intended for the Enumerator, which contains the results stored in the HH_data_check_field. It makes use of the openxlsx library and it defines several formatting styles for the different sections of the excel sheet.
# create an excel workbook and sheet
output_file <- paste0(output_dir, "/", file_title, " - Data Check - Field.xlsx")
n_rows <- nrow(HH_data_check_field)
n_cols <- ncol(HH_data_check_field)
wb <- createWorkbook()
addWorksheet(wb, "HH Sample Size")
setColWidths(wb, sheet = 1, cols = 1:n_cols, widths = "20")
# set styles
top_row_style <- createStyle(wrapText = TRUE, valign = "center", fgFill = "#E8E8E8")
falseStyle <- createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE")
trueStyle <- createStyle(fontColour = "#006100", bgFill = "#C6EFCE")
conditionalFormatting(wb, sheet = 1, cols = 8:18, rows = 3:(n_rows + 3), type = "contains", rule = "TRUE", style = trueStyle)
conditionalFormatting(wb, sheet = 1, cols = 8:18, rows = 3:(n_rows + 3), type = "contains", rule = "FALSE", style = falseStyle)
...
- Two additional rows are added to the top of the table, containing specific information on the type of constrain check performed and also suggestions on the survey questions to review in the original data. Special formatting is added to these rows using the
addStyle() function from the openxlsx package. To maintain previous loaded styles, use the stack = TRUE parameter.
# create warning and suggestion rows
warning_row <- as.data.frame(t(warning_column_names))
suggestion_row <- as.data.frame(t(suggestion_column_names))
writeData(wb, sheet = 1, warning_row, startRow = 1, startCol = 8, colNames = FALSE, borders = "all")
addStyle(wb, sheet = 1, top_row_style, rows = 1, cols = 8:18, gridExpand = TRUE, stack = TRUE)
writeData(wb, sheet = 1, suggestion_row, startRow = 2, startCol = 8, colNames = FALSE, borders = "all")
addStyle(wb, sheet = 1, top_row_style, rows = 2, cols = 8:18, gridExpand = TRUE, stack = TRUE)
...
- The data is finally exported
# export result into excel
writeDataTable(wb, sheet = 1, HH_data_check_field, startRow = 3, startCol = 1)
saveWorkbook(wb, file = output_file, overwrite = TRUE)
...
Running everything!
The HH-survey-check.R main script contains the source files in the order that need to be loaded and run. The here library will deal with relative and direct path issues to make it issue running the tool under different file systems. It takes the root directory (the one containing the Rstudio .rproj file) as reference. The *_common* directory stores the common scripts used across all HNAP tools, defining the minimum required libraries and several commonly used functions. The rest of the tool scripts are loaded sequentially. Use the Source button on RStudio to run the whole sequence.
# load here library hto handle relative paths from project ROOT
library(here)
source(here("R", "_common", "required.R"))
source(here("R", "_common", "utils.R"))
source(here("R", "HH_survey_check", "01-hh-required.R"))
source(here("R", "HH_survey_check", "02-hh-functions.R"))
source(here("R", "HH_survey_check", "03-hh-config.R"))
source(here("R", "HH_survey_check", "04-hh-dataload.R"))
source(here("R", "HH_survey_check", "05-hh-process.R"))
source(here("R", "HH_survey_check", "06-hh-output.R"))
LS0tCnRpdGxlOiAiSG91c2Vob2xkIFN1cnZleSBEYXRhIENoZWNrIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KIyMjIERlc2NyaXB0aW9uClRoaXMgdG9vbCBwZXJmb3JtcyBhIGRhdGEgY2hlY2sgYW5kIHZhbGlkYXRpb24gb3ZlciB0aGUgZGF0YXNldHMgY29sbGVjdGVkIG9uIHRoZSBIb3VzZWhvbGQgU3VydmV5LCBmb3IgdGhvc2UgaW5kaWNhdG9ycyB3aGljaCBtaWdodCBoYXZlIG5vdCBiZWVuIHByZS12YWxpZGF0ZWQgYnkgdGhlIEtvYm8gZm9ybSBjb25zdHJhaW5zLiBJdCBnZW5lcmF0ZXMgYW4gZXhjZWwgZmlsZSBoaWdobGlndGluZyBhbGwgdGhlIGlkZW50aWZpZWQgZW50cmllcyB3aXRoIGF0IGxlYXN0IG9uZSB2YWxpZGF0aW9uIGVycm9yIGluIG9yZGVyIHRvIGJlIHJldmlld2VkIGJhY2sgYnkgdGhlIGVudW1lcmF0b3JzLgoKIyMjIyBJbnB1dDoKQSBIb3VzZWhvbGQgU3VydmV5IGZpbGUgYXMgZ2VuZXJhdGVkIGJ5IEtvYm8gZm9yIGEgZ2l2ZW4gR292ZXJub3JhdGUuIFRoZSB0b29sIGNhbiBiZSBlYXNpbHkgYWRhcHRlZCB0byBtZXJnZSBhbGwgdGhlIHNpbmdsZSBnb3Zlcm5vcmF0ZSBmaWxlcyBpbnRvIGEgc2luZ2xlIGRhdGFmcmFtZS4KCiMjIyMgT3V0cHV0OgpBbiBleGNlbCBmaWxlIGNvbnRhaW5pbmcganVzdCB0aGUgbmV3IGlkZW50aWZpZWQgc2hlbHRlciBvbiB0aGUgZ2l2ZW4gcGVyaW9kLiBUaGUgY29sdW1uIGhlYWRlcnMgYXJlIHJlbmFtZWQgdG8gbWF0Y2ggdGhlIHJlcXVpcmVkIGZvcm1hdCBhbmQgY29sdW1uIG5hbWVzCgojIyMgTWFpbiBmdW5jdGlvbnMKClRoaXMgdG9vbCBkb2VzIG5vdCBtYWtlIHVzZSBvZiBhbnkgc3BlY2lmaWMgY3VzdG9tIGZ1bmN0aW9ucy4KYGBge3IgZWNobz1GQUxTRX0KIyBPTkxZIGZvciBOb3RlYm9vazogZG8gbm90IHJ1biAoZXZhbCkgY2h1bmtzIHdoZW4ga25pdHRpbmcgbm90ZWJvb2suIEFwcGxpZXMgdG8gYWxsIGNodW5rcwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbCA9IEZBTFNFKQoKYGBgCgojIyMgQ29uZmlndXJhdGlvbgpTcGVjaWZ5IHRoZSBkZWZhdWx0IGRhdGEgaW5wdXQgYW5kIG91dHB1dCBkaXJlY3RvcmllcywgYW5kIHNldmVyYWwgZGF0YSB2ZWN0b3JzIG5lZWRlZCBieSB0aGUgdG9vbDoKCiogYEhIX21lbWJlcnNfY29sc2A6IHZlY3RvciBjb250YWluaW5nIHRoZSBjb2x1bW4gbmFtZXMgdG8gYmUgc2VsZWN0ZWQgZnJvbSB0aGUgRGVtb2dyYXBoaWMgU2VjdGlvbiAoQzEpCgoqIGBISF9hYnNlbnRfY29sc2A6IHZlY3RvciBjb250YWluaW5nIHRoZSBjb2x1bW4gbmFtZXMgdG8gYmUgc2VsZWN0ZWQgZnJvbSB0aGUgQWJzZW50IE1lbWJlcnMgU2VjdGlvbgoKKiBgc2hlbHRlcl9sb29rdXBgOiB2ZWN0b3IgY29udGFpbmluZyB0aGUgdmFsdWVzIGZvciB0aGUgU2hlbHRlciBvcHRpb24gKEIxKQoKKiBgZHdlbGxpbmdfbWFwYDogbmFtZWQgdmVjdG9yIGNvbnRhaW5nIHRoZSBkd2VsbGluZyB0eXBlcyBhbmQgYSBjb2RlIHdoaWNoIG1hcHMgdGhlbSB0byB0aGUgcG9zc2libGUgc2hlbHRlciB0eXBlcwoKKiBgd2FybmluZ19jb2x1bW5fbmFtZXNgOiB2ZWN0b3IgY29udGFpbmluZyB0aGUgbGlzdCBvZiB3YXJuaW5nIG1lc3NhZ2VzIHRvIGJlIGFkZGVkIHRvIHRoZSBmaW5hbCBleGNlbCBvdXRwdXQKCiogYHN1Z2dlc3Rpb25fY29sdW1uX25hbWVzYDogdmVjdG9yIGNvbnRhaW5pbmcgdGhlIGxpc3Qgb2Ygc3VnZ2VzdGVkIGFuc3dlcnMgdG8gcmV2aWV3IHRvIGJlIGFkZGVkIHRvIHRoZSBmaW5hbCBleGNlbCBvdXRwdXQKCgojIyMgRGF0YSBMb2FkClRoZSBpbnB1dCBmaWxlIGlzIGV4cGVjdGVkIHRvIGJlIHBsYWNlZCB1bmRlciB0aGUgYGRhdGFgIHN1YmRpcmVjdG9yeS4gVGhlICoqcmVhZHhsKiogbGlicmFyeSBpcyB1c2VkIGluc3RlYWQgb2YgKipvcGVueGxzeCoqIGFzIHRoZSBsYXR0ZXIgd2FzIGhhdmluZyBzb21lIGlzc3VlcyBpbiBjb3JyZWN0bHkgcmVhZGluZyB0aGUgS29ibyBvdXRwdXQuIFRoZSBIb3VzZWhvbGQgU3VydmV5IGluIEtvYm8gc3RvcmVzIHRoZSBjb2xsZWN0ZWQgZGF0YSBpbiB0aHJlZSBzaGVldHMsIHRoZSBmaXJzdCBiZWluZyB0aGUgbWFpbiBvbmUgYW5kIHRoZSBvdGhlciB0d28gdXNlZCBmb3IgdGhlICdjaGlsZHJlbicgdmFsdWVzIChpbiB0aGlzIGNhc2UgdGhlIG1lbWJlcnMgb2YgYSBzaW5nbGUgaG91c2Vob2xkLCBlaXRoZXIgcHJlc2VudCBvciBhYnNlbnQpLiBFYWNoIG9mIHRoZSBzaGVldHMgaXMgc3RvcmVkIGluIGEgc2VwYXJhdGUgZGF0YWZyYW1lIGZvciBsYXRlciBwcm9jZXNzaW5nLgoKYGBge3J9CkhIX2RhdGFzZXRfbWFpbiA8LSByZWFkeGw6OnJlYWRfeGxzeChmaWxlcGF0aCwgc2hlZXQgPSAxKQpISF9kYXRhc2V0X21lbWJlcnMgPC0gcmVhZHhsOjpyZWFkX3hsc3goZmlsZXBhdGgsIHNoZWV0ID0gMikKSEhfZGF0YXNldF9hYnNlbnQgPC0gcmVhZHhsOjpyZWFkX3hsc3goZmlsZXBhdGgsIHNoZWV0ID0gMykKYGBgCgojIyMgUHJvY2VzcwoKCihAKSBXZSBuZWVkIGZpcnN0IHRvIGNsZWFuIGEgbGl0dGxlIGJpdCB0aGUgZGF0YXNldHMgYW5kIHJlbW92ZSBzb21lICdmYWxzZScgY29sdW1ucyBhZGRlZCBieSBLb2JvLiBVc2luZyAqKmRwbHlyKiogYW5kIGBzZWxlY3QoLXN0YXJ0c193aXRoKCI8IikpYCB3ZSBjYW4gcmVtb3ZlIHRoZSBjdWxwcml0IGNvbHVtbnMuIFRoZSBgc3RhcnRzX3dpdGgoIjwiKWAgaGVscGVyIGZpbmRzIHRob3NlIGNvbHVtbnMgbWF0Y2hpbmcgdGhlIGdpdmVuIHN0cmluZyBhbmQgdGhlIG1pbnVzIHNpZ24gcGVyZm9ybXMgdGhlIHJlbW92YWwuCgpgYGB7cn0KI3JlbW92ZSB3YXJuaW5nIGNvbHVtbnMgc3RhcnQgd2l0aCA8IG9yICgKSEhfZGF0YXNldF9tYWluIDwtIEhIX2RhdGFzZXRfbWFpbiAlPiUgc2VsZWN0KC1zdGFydHNfd2l0aCgiPCIpKSAlPiUgc2VsZWN0KC1zdGFydHNfd2l0aCgiKCIpKQpISF9kYXRhc2V0X21lbWJlcnMgPC0gSEhfZGF0YXNldF9tZW1iZXJzICU+JSBzZWxlY3QoLXN0YXJ0c193aXRoKCI8IikpICU+JSBzZWxlY3QoLXN0YXJ0c193aXRoKCIoIikpCkhIX2RhdGFzZXRfYWJzZW50IDwtIEhIX2RhdGFzZXRfYWJzZW50ICU+JSBzZWxlY3QoLXN0YXJ0c193aXRoKCI8IikpICU+JSBzZWxlY3QoLXN0YXJ0c193aXRoKCIoIikpCmBgYAoKKEApIFdlIGdldCB0aGUgKnJhbmdlKiBvZiBpbmRleGVzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNvbHVtbnMgdG8gYmUgc2VsZWN0ZWQgZnJvbSB0aGUgbWFpbiBkYXRhc2V0LiBUaGlzIGNvdWxkIGFsc28gYmUgYWNvbW1wbGlzaGVkIGJ5IGRlZmluaW5nIGEgdmVjdG9yIG9mIGNvbHVtbiBuYW1lcyB1bmRlciAqKkNvbmZpZ3VyYXRpb24qKiwgaG93ZXZlciB0aGUgYGdldF9yYW5nZV9pbmRleGVzKClgIGZ1bmN0aW9uIChkZWZpbmVkIGluIEhOQVAgKnV0aWxzLlIqIGxpYnJhcnkpIG1ha2VzIHRoaW5ncyBhIGxpdHRsZSBlYXNpZXIgYXMgaXQgYWxsb3dzIHRvIGdldCB0aGUgcmFuZ2Ugb2Ygc2V2ZXJhbCBjb25zZWN1dGl2ZSBjb2x1bW5zIGFzIG9uY2UsIGJ5IGp1c3Qgc3BlY2lmeWluZyB0aGUgaW5pdGlhbCBhbmQgZmluYWwgY29sdW1ucy4gVGhlIGBnZXRfY29sX2luZGV4ZXMoKWAgZ2V0cyBqdXN0IHRoZSBpbmRleCBvZiBlaXRoZXIgb25lIG9yIHR3byBjb2x1bW5zIGRlZmluZWQgaW4gdGhlIHBhcmFtZXRlcnMuIFRoZSBgYXBwZW5kKClgIGZ1bmN0aW9uIGFkZHMgYSB2ZWN0b3IgYXQgdGhlIGVuZCBvZiBhbiBleGlzdGluZyB2ZWN0b3IgcHJvdmlkZWQgYXMgaW5wdXQsIGluIHRoaXMgY2FzZSB0aGUgKnJhbmdlKiB2ZWN0b3IuCgpgYGB7cn0KIyBnZXQgcmFuZ2Ugb2YgZGVtb2dyYXBoaWMgY29sdW1ucyB0byBzZWxlY3QgZnJvbSBISCBkYXRhc2V0CnJhbmdlIDwtIGdldF9yYW5nZV9pbmRleGVzKEhIX2RhdGFzZXRfbWFpbiwgIkRhdGUgb2YgVmlzaXQiLCAiUGNvZGUgPSAke2FkbWluNH0iKQpyYW5nZSA8LSBhcHBlbmQocmFuZ2UsIGdldF9jb2xfaW5kZXhlcyhISF9kYXRhc2V0X21haW4sICJCMS4gVHlwZSBvZiBTaGVsdGVyIikpCnJhbmdlIDwtIGFwcGVuZChyYW5nZSwgZ2V0X3JhbmdlX2luZGV4ZXMoSEhfZGF0YXNldF9tYWluLCAiTmFtZSBvZiBIb0giICwgIkhIIFR5cGUiKSkKcmFuZ2UgPC0gYXBwZW5kKHJhbmdlLCBnZXRfcmFuZ2VfaW5kZXhlcyhISF9kYXRhc2V0X21haW4sICJISCBtZW1iZXIgdHlwZSBvZiBpbnRlcnZpZXdlZSIsICJTRUxGIENBUkUiKSkKLi4uCmBgYAoKKEApIFdlICpzdWJzZXQqIGVhY2ggZGF0YXNldCBieSBzZWxlY3Rpbmcgb25seSB0aGUgY29sdW1ucyB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aC4gVGhhdCBzdGVwIGlzIG5vdCByZWFsbHkgbmVjZXNzYXJ5IGFzIFIgY2FuIGRlYWwgd2l0aCB0aGUgZnVsbCBkYXRhc2V0cywgdGhvdWdoIGl0IGNhbiBhY3R1YWxseSBzYXZlIHNvbWUgbWVtb3J5IGFuZCBzcGVlZCB0aGluZ3MgdXAgYSBsaXR0bGUgYml0LiBBbHNvIGl0IG1ha2VzIGVhc2llciB0byB3b3JrIHdpdGggdGhlIGRhdGFzZXRzIHdoZW4gZGlzcGxheWluZyB0aGVpciBjb250ZW50cyBvbiB0aGUgKlZpZXcqIHBhbm5lbC4gVGhlICpfdXVpZCogZ2V0cyBhbHNvIHJlbmFtZWQgdXNpbmcgdGhlIGByZW5hbWUoKWBmdW5jdGlvbiBmcm9tICoqZHBseXIqKgooQCkgQmVmb3JlIGNvbWJpbmluZyB0aGUgZGF0YXNldHMsIHdlIG5lZWQgdG8gbWFrZSBzdXJlIHRoZSBjb2x1bW4gdHlwZXMgbWF0Y2guIEl0IGlzIGNvbW1vbiB0aGF0IHRoZSBleHBlY3RlZCAqbnVtZXJpY2FsKiBjb2x1bW5zIGFyZSBjb2VyY2VkIGFzICpub24tbnVtZXJpY2FsKiwgZWl0aGVyICpjaGFyYWN0ZXIqIG9yICp1bmtub3duKiB0eXBlLCBzb21ldGltZXMgZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiBgTkFgIHZhbHVlcyBvbiB0aGUgb3JpZ2luYWwgZGF0YXNldCBvciBvdGhlciByZWFzb25zLiBJbiBvcmRlciB0byBjb2VyY2UgdGhvc2UgY29sdW1ucyBjb250YWluaW5nIHZhbHVlcyB0aGF0IGFyZSBleHBlY3RlZCB0byByZW1haW4gKm51bWVyaWNhbCosIHdlIHVzZSB0aGUgYHR5cGVfY29udmVydCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBITkFQICoqdXRpbHMuUioqIGxpYnJhcnkuIFRoaXMgZnVuY3Rpb24gd2lsbCBwZXJmb3JtIGEgJ3NvZnQgY29lcnRpb24nIG92ZXIgZWFjaCBkYXRhZnJhbWUgY29sdW1uLCBhZmZlY3Rpbmcgb25seSB0aG9zZSBjb2x1bW5zIHdpdGggZXhwZWN0ZWQgbnVtZXJpY2FsIGNvbHVtbnMgKG1lYW5pbmcgdGhvc2UgY29sdW1ucyBjb250YWluaW5nIG9ubHkgbnVtYmVycywgb3IgbnVtYmVycyBhbmQgYmxhbmsgY2VsbHMvTkEgdmFsdWVzKS4gSW4gdGhhdCBjYXNlIHRoZSBjaGFyYWN0ZXIgbnVtYmVycyB3aWxsIGJlIGNvZXJjZWQgdG8gbnVtZXJpY2FsIGFuZCB0aGUgZW1wdHkgY2VsbCB3aWxsIGJlIGNvZXJjZWQgdG8gYE5BYC4gSWYgYSBnaXZlbiBjb2x1bW4gY29udGFpbnMgYW55IHRydWUgKmNoYXJhY3RlciogdmFsdWUgKG1lYW5pbmcgYSBub24tbnVtZXJpY2FsIHN0cmluZyBvZiB0ZXh0KSwgaXQgd2lsbCBiZSBsZWZ0IGFzIGl0IGlzLiAKKEApIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPioqSU1QT1JUQU5UISoqPC9zcGFuPiBXZSBuZWVkIHRvIGFwcGx5IGB0eXBlX2NvbnZlcnQoKWAgb3ZlciBhbGwgdGhlIGNvbHVtbnMgb2YgdGhlIGRhdGFzZXQsIHRoZXJlZm9yZSB3ZSB1c2UgdGhlIGBsYXBwbHkoKWAgZnVuY3Rpb24gdG8gcGVyZm9ybSB0aGlzIG9wZXJhdGlvbiBvdmVyIGFsbCB0aGUgY29sdW1uIGVsZW1lbnRzIG9mIHRoZSBkYXRhZnJhbWUgKGFuIGVmZmljaWVudCBpdGVyYXRpb24pLiBUaGUgaW5pdGlhbCBvdXRwdXQgd2lsbCBiZSBhICpsaXN0KiBvYmplY3QgY29udGFpbmluZyBlYWNoIGNvbnZlcnRlZCBjb2x1bW4uIEJ5IHNpbXBseSBzcGVjeWZpbmcgdGhlIG91dHB1dCB2YXJpYWJsZSBhcyBgSEhfZGVtb1tdYCB3aXRoIHRoZSBicmFja2V0cyBgW11gIHdlIGNhbiBhdXRvbWF0aWNhbGx5IHN0b3JlIGJhY2sgdGhlIGxpc3QgZWxlbWVudHMgYXMgZGF0YWZyYW1lIGVsZW1lbnRzIChzb3J0IG9mICpiaW5kaW5nKiB0aGVtIG9uLXRoZS1mbHkpLgoKYGBge3J9CiMgc2VsZWN0IGNvbHVtbnMgdG8gY29tYmluZSBmb3IgZWFjaCBkYXRhc2V0IGFuZCBjb2VyY2UgdG8gbnVtZXJpYyBhbGwgdGhvc2UgY2hhcmFjdGVyIGNvbHVtbnMgaG9sZGluZyBudW1lcmljIHZhbHVlcyBiZWZvcmUgYmluZGluZyBzbyBhbGwgY29sdW1ucyBmcm9tIGVhY2ggc3Vic2V0IG1hdGNoIHR5cGUKCkhIX2RlbW8gPC0gSEhfZGF0YXNldF9tYWluICU+JSAKICBzZWxlY3QocmFuZ2UpICU+JSAKICByZW5hbWUodXVpZCA9ICJfdXVpZCIpCkhIX2RlbW9bXSA8LSBsYXBwbHkoSEhfZGVtbywgdHlwZV9jb252ZXJ0KQoKSEhfbWVtYmVycyA8LSBISF9kYXRhc2V0X21lbWJlcnMgJT4lIAogIHNlbGVjdChISF9tZW1iZXJzX2NvbHMpICU+JSAKICByZW5hbWUodXVpZCA9ICJfc3VibWlzc2lvbl9fdXVpZCIpCkhIX21lbWJlcnNbXSA8LSBsYXBwbHkoSEhfbWVtYmVycywgdHlwZV9jb252ZXJ0KQoKSEhfYWJzZW50IDwtIEhIX2RhdGFzZXRfYWJzZW50ICU+JQogIHNlbGVjdChISF9hYnNlbnRfY29scykgJT4lIAogIHJlbmFtZSh1dWlkID0gIl9zdWJtaXNzaW9uX191dWlkIikKSEhfYWJzZW50W10gPC0gbGFwcGx5KEhIX2Fic2VudCwgdHlwZV9jb252ZXJ0KQpgYGAKCihAKSBXZSBzdGFydCB0aGUgcHJvY2Vzc2luZyBvZiB0aGUgY29tYmluZWQgYEhIX2FsbGAgZGF0YWZyYW1lIHVzaW5nIHRoZSAqKmRwbHlyKiogbGlicmFyeSBhbmQgInBpcGVsaW5pbmciIHRoZSBkaWZmZXJlbnQgYWN0aW9ucywgc3RhcnRpbmcgd2l0aCBzZWxlY3RpbmcgdGhlIGNvbHVtbnMgbmVlZGVkIGZvciB0aGUgdmFsaWRhdGlvbiBhbmFseXNpcy4gU29tZSBvZiB0aGUgY29sdW1ucyB3aWxsIGJlIHJlbmFtZWQgZm9yIGJldHRlciBtYW5pcHVsYXRpb24gbGF0ZXIgb24gdGhlIHBpcGVsaW5lCgpgYGB7cn0KSEhfZGF0YV9jaGVja190ZW1wIDwtIEhIX2FsbCAlPiUKICBzZWxlY3QodXVpZCwgCiAgICAgICAgIGBFbnVtZXJhdG9yIENvZGVgLAogICAgICAgICBHb3Zlcm5vcmF0ZSA9IEdvdmVybm9yYXRlLi4uNSwKICAgICAgICAgRGlzdHJpY3QgPSBEaXN0cmljdC4uLjYsCiAgICAgICAgIFN1YmRpc3RyaWN0ID0gYFN1Yi1kaXN0cmljdC4uLjdgLAogICAgICAgICBDb21tdW5pdHkgPSBDb21tdW5pdHkuLi44LAogICAgICAgICBOYW1lX0hvSCA9IGBOYW1lIG9mIEhvSGAsCiAgICAgICAgIC4uLgpgYGAKCihAKSBXZSB1c2UgYG11dGF0ZSgpYCB0byBjcmVhdGUgbmV3IGFkZGl0aW9uYWwgY29sdW1ucyB3aGVyZSBzZXZlcmFsIGxvZ2ljYWwgdmFsaWRhdGlvbnMgYXJlIGNhbGN1bGF0ZWQgYWNjb3JkaW5nIHRvIHRoZSByZXF1aXJlZCBjb25zdHJhaW5zLiBTb21lIG9mIHRoZXNlIGNvbHVtbnMgd2lsbCBnZXQgYFRSVUVgIG9yIGBGQUxTRWAgdmFsdWVzIGRlcGVuZGluZyBvbiB0aGUgcmVzdWx0IG9mIHRoZSBwZXJmb3JtZWQgYm9vbGVhbiBvcGVyYXRpb24uIAoKYGBge3J9CiBtdXRhdGUoY2hlY2tfaGhoID0gKGBISCBtZW1iZXIgdHlwZWAgPT0gIkhlYWQgb2YgSG91c2Vob2xkIiksIAogICAgICAgICBjaGVja19oaGhfbWFycmllZCA9IChgSEggbWVtYmVyIHR5cGVgID09ICJIZWFkIG9mIEhvdXNlaG9sZCIgJiBgTWFyaXRhbCBzdGF0dXNgID09ICJNYXJyaWVkIiksIAogICAgICAgICBjaGVja19zcG91c2UgPSAoYEhIIG1lbWJlciB0eXBlYCA9PSAiU3BvdXNlIiksCiAgICAgICAgIGNoZWNrX2NoaWxkID0gKChgSWYgbm8sIHdoeT9gID09ICJjaGlsZCIpIHwgKGBJZiBubywgd2h5P2AgPT0gInN0dWRlbnQiICYgYEFnZSAtIFllYXJgIDwgMTgpKSwKICAgICAgICAgY2hlY2tfc3R1ZGVudF9hZ2UgPSAoKGBJZiBubywgd2h5P2AgPT0gImNoaWxkIiAmIGBBZ2UgLSBZZWFyYCA+PSA2KSB8IChgSWYgbm8sIHdoeT9gID09ICJzdHVkZW50IiAmIGBBZ2UgLSBZZWFyYCA+PSA2KSksCiAgICAgICAgIGNoZWNrX25vdF9zdHVkZW50X2FnZSA9IChgSWYgbm8sIHdoeT9gID09ICJjaGlsZCIgJiBgQWdlIC0gWWVhcmAgPCA2KSwKICAgICAgICAgY2hlY2tfY2hpbGRfd29yayA9IChjaGlsZF93b3JrID09ICIxIiksCiAgICAgICAgIGNoZWNrX2NoaWxkX3NjaG9vbCA9IChjaGlsZF9zY2hvb2wgPT0gIk5vIGNoaWxkcmVuIGluIHNjaG9vbHMiKSwKICAgICAgICAgLi4uCmBgYAoKKEApIE90aGVyIGFkZGVkIGNvbHVtbnMgYXJlIHVzZWQgdG8gc3RvcmUgY2FsY3VsYXRlZCB2YWx1ZXMgcmVzdWx0aW5nIGZyb20gdGhlIHN1bSBvZiB0aGUgY29udGVudHMgb2Ygc2V2ZXJhbCBjZWxscyBvbiB0aGUgc2FtZSByb3cuIEFzICoqZHBseXIqKiB3b3JrcyBjb2x1bW53aXNlLCB3ZSBuZWVkIHRvIHNwZWNpZnkgdGhhdCB0aGUgc3VtIG9wZXJhdGlvbiBuZWVkcyB0byBiZSBjYXJyaWVkIG9uICpyb3d3aXNlKi4gVGhlIGByb3dTdW1zKClgIGZ1bmN0aW9uIHdpbGwgcGVyZm9ybSB0aGUgcm93IHN1bXMgZm9yIHRoZSBzZWxlY3RlZCBjb2x1bW5zLiBQbGVhc2Ugbm90aWNlIHRoYXQgaW4gdGhpcyBjYXNlLCB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbiBuZWVkcyB0byBleHBsaWNpdGVseSByZWZlciB0aGUgdGFyZ2V0IGRhdGFmcmFtZSBhcyBpdCB3YXMgYXQgdGhlIGVuZCBvZiB0aGUgbGFzdCAicGlwZWxpbmVkIiBvcGVyYXRpb24uIFdlIHVzZSB0aGUgYC5gIGZvciBzdWNoIHJlZmVyZW5jZS4gVGhpcyBpcyBuZWVkZWQgYmVjYXVzZSB0aGUgdXNlIG9mIGByb3dTdW1zKClgIHNvbWVob3cgImJyZWFrcyIgdGhlICpjb2x1bXdpc2UqIGJlaGF2aW91ciBvZiAqKmRwbHlyKiosIHNvIHdlIGNhbm5vdCB1c2UgYHNlbGVjdCgpYCB3aXRob3V0IGV4cGxpY2l0ZWxseSBzcGVjaWZ5aW5nIHRoZSB0YXJnZXQgZGF0YWZyYW1lLgoKYGBge3IgfQogICAgICAgICB3YXRlciA9IHJvd1N1bXMoc2VsZWN0KC4sYGkpICUgZm9yIE5ldHdvcmtgOmBpeCkgJSBmb3IgT3RoZXIgKHNwZWNpZnkpYCksIG5hLnJtID0gVFJVRSksICMgc3VtIHBlcmNlbnRhZ2VzIGZyb20gZGlmZmVyZW50IHdhdGVyIHNvdXJjZXMKICAgICAgICAgbnVtYmVyX2NvbnRhaW5lcnMgPSByb3dTdW1zKHNlbGVjdCguLGBIb3cgbWFueSBsaXRlcnMgZG9lcyBob2xkIHRoZSByb29mIHRhbms/YDpgSG93IG1hbnkgbGl0ZXJzIGRvZXMgaG9sZCB0aGUgU21hbGxIb3VzZUhvbGRzP2ApLCBuYS5ybSA9IFRSVUUpLCAjIHN1bSB0b3RhbCBudW1iZXIgb2YgY29udGFpbmVycwogICAgICAgICBwZXJjX3NwZW50ID0gcm93U3VtcyhzZWxlY3QoLiwgcGVyY193YXRlciwgcGVyY19oeWdpZW5lLCBwZXJjX2dhcmJhZ2UsIHBlcmNfc2VwdGljKSwgbmEucm0gPSBUUlVFKSwgIyBzdW0gcGVyY2VudGFnZXMgb2YgaW5jb21lIHNwZW50Ci4uLgpgYGAKCihAKSBTb21lIG5ldyBjb2x1bW5zIHJlcXVpcmUgYW4gYXJpdGhtZXRpY2FsIG9wZXJhdGlvbiB3aGljaCBtaWdodCBnaXZlIGEgKmRvdWJsZSogYXMgb3V0cHV0LiBFdmVuIGlmIHRoZSBjb2x1bW4gdHlwZSBpcyB0aGUgZ2VuZXJpYyAqbnVtZXJpYyosIHRoZXJlIGNhbiBiZSBzb21lIGlzc3VlcyB3aGVuIHVzaW5nIGByb3dTdW1zKClgIGxhdGVyIG9uIGFuZCByZWZlcnJpbmcgdG8gdGhpcyBjb2x1bW5zLCBhcyBvdGhlciAqbnVtZXJpYyogY29sdW1ucyBhcmUgKmludGVnZXIqLiBUaGVyZWZvcmUgdG8gYXZvaWQgZXJyb3JzLCB0aGUgY2FsY3VsYXRlZCBjb2x1bW4gaXMgY29lcmNlZCB0byBpbnRlZ2VyIHVzaW5nIGBhcy5pbnRlZ2VyKClgLgoKYGBge3J9CiAgICAgICAgIGh5Z2llbmVfcHVyY2hhc2VfbW9udGhseSA9IGFzLmludGVnZXIoaHlnaWVuZV9wdXJjaGFzZS8zKSwgIyBkaXZpZGUgYnkgMyB0byBnZXQgbW9udGhseSBhbmQgY29lcmNlIHRvIGludGVnZXIgdG8gYXZvaWQgdHlwZSBtaXNtYXRjaAogICAgICAgICBzZXB0aWNfcHVyY2hhc2VfbW9udGhseSA9IGFzLmludGVnZXIoc2VwdGljX3B1cmNoYXNlLzMpLCAjIGRpdmlkZSBieSAzIHRvIGdldCBtb250aGx5IGFuZCBjb2VyY2UgdG8gaW50ZWdlciB0byBhdm9pZCB0eXBlIG1pc21hdGNoCi4uLgpgYGAKCihAKSBUaGUgKnR5cGUgb2Ygc2hlbHRlciogYW5kICp0eXBlIG9mIGR3ZWxsaW5nKiBhcmUgcmVsYXRlZCBpbiBhICoxLXRvLW1hbnkqIGZhc2hpb24gYXMgZGVzY3JpYmVkIGJ5IHRoZSBgZHdlbGxpbmdfbWFwYCBuYW1lZCB2ZWN0b3IsIHdoaWNoIG1hcHMgYSBnaXZlbiAqZHdlbGxpbmcqIHRvIHRoZSBkaWZmZXJlbnQgdHlwZSBvZiAqc2hlbHRlcnMqIHRoYXQgY2FuIGJlbG9uZy4gVGhpcyAqbWFwcGluZyogaXMgY29kZWQgYnkgcmVmZXJyaW5nIHRvIHRoZSAqc2hlbHRlciB0eXBlcyogdmlhIHRoZWlyIG51bWVyaWNhbCBpbmRleCBpbiB0aGUgYHNoZWx0ZXJfbG9va3VwYCB2ZWN0b3IuCihAKSBUaGUgYG1hdGNoKClgIGZ1bmN0aW9uIGFsbG93cyB0byBsb29rIHVwIGEgZ2l2ZW4gdmFsdWUgb3IgcGF0dGVybiBpbnNpZGUgYSBsb29rdXAgdmVjdG9yLCBpbiB0aGlzIGNhc2UgdGhlIGBzaGVsdGVyX2xvb2t1cGAsIGFuZCBpdCB3aWxsIHJldHVybiB0aGUgcG9zaXRpb24gaW5kZXggb2YgdGhlIHZhbHVlIGluIGNhc2UgYSBtYXRjaGluZyBpcyBmb3VuZC4KKEApIE9uY2Ugb2J0YWluZWQgdGhlIHNoZWx0ZXIgaW5kZXhlcywgd2UgbmVlZCB0byBjaGVjayBmb3IgZWFjaCBzaGVsdGVyIGluZGV4IGluIHRoZSBjb2x1bW4gd2hldGhlciBpdCBpcyBmb3VuZCBpbiB0aGUgc2VsZWN0ZWQgZHdlbGxpbmcgIm1hcCIuIFRoaXMgb3BlcmF0aW9uIGlzIHBlcmZvcm1lZCBieSB0aGUgY3VzdG9tIGBjaGVja19wYXR0ZXJuX3ZlY3RvcigpYCBmcm9tIHRoZSBITkFQICoqdXRpbHMuUioqIGxpYnJhcnkuIFRoaXMgZnVuY3Rpb24gYXBwbGllcyB0aGUgYGdyZXBsKClgIFIgZnVuY3Rpb24gb3ZlciB0aGUgd2hvbGUgY29sdW1uIHVzaW5nIGBtYXBwbHkoKWAuIEZvciBlYWNoIHZhbHVlIG9mIHRoZSBjb2x1bW4gYHNoZWx0ZXJfaW5kZSgpYCwgYGdyZXBsKClgIHdpbGwgY2hlY2sgaWYgdGhlIHZhbHVlIGlzIGZvdW5kIHdpdGhpbiB0aGUgbWFwcGluZyBzdHJpbmcgZGVmaW5lZCBmb3IgdGhlIHNlbGVjdGVkICpkd2VsbGluZyouIApgYGB7cn0KICAgICAgICAgc2hlbHRlcl9pbmRleCA9IG1hdGNoKHNoZWx0ZXIsIHNoZWx0ZXJfbG9va3VwKSwgIyBsb29rdXAgc2hlbHRlciBpbmRleCBpbiBzaGVsdGVyX3R5cGUgdGFibGUKICAgICAgICAgY2hlY2tfZHdlbGxpbmcgPSBjaGVja19wYXR0ZXJuX3ZlY3RvcihzaGVsdGVyX2luZGV4LCBkd2VsbGluZ19tYXBbZHdlbGxpbmddKSkgJT4lICMgY2hlY2sgaWYgc2hlbHRlciBpbmRleCBpcyBtYXBwZWQgYnkgdGhlIGR3ZWxsaW5nIHR5cGUKICAuLi4KYGBgCgooQCkgVGhlIG5leHQgc3RlcCBpcyB0byBncm91cCBhbGwgdGhlIHJvd3MgYnkgYSBjb21tb24gYHV1aWRgIGluIG9yZGVyIHRvIGNhbGN1bGF0ZSBzdW1tYXJpc2VkIHJlc3VsdHMKKEApIFdoZW4gdXNpbmcgYGdyb3VwX2J5YCwgb25seSB0aGUgc2VsZWN0ZWQgc3VtbWFyaXNlZCB2YWx1ZXMgYXJlIHByZXNlbnQgaW4gdGhlIG91dHB1dCBkYXRhZnJhbWUuIElmIHdlIHdhbnQgdG8gYWxzbyBpbmNsdWRlLCBmb3IgaW5mb3JtYXRpb25hbCBwdXJwb3NlcywgIGNvbHVtbnMgd2hpY2ggYXJlIG5vdCBzdW1tYXJpc2VkIG9yIGFnZ3JlZ2F0ZWQgaW4gdGhlIG91dHB1dCAoZm9yIGV4YW1wbGUgc29tZSAqY2hhcmFjdGVyKiB0eXBlIGNvbHVtbnMpLCB3ZSBjYW4gYWNjb21wbGlzaCB0aGF0IGJ5IHVzaW5nIHRoZSBgZmlyc3QoKWAgZnVuY3Rpb24sIHdoaWNoIHdpbGwgc2VsZWN0IHRoZSBmaXJzdCBlbGVtZW50IGZvdW5kIGZvciB0aGF0IGNvbHVtbnMgKGFzIHRoZSByZXN0IG9mIGVsZW1lbnRzIGZvciB0aGF0IGNvbHVtbiB1bmRlciB0aGF0IGdyb3VwIGFyZSBpbiBhbnkgY2FzZSByZXBlYXRlZCkuIAooQCkgU29tZSB0b3RhbHMgYXJlIGFsc28gY2FsY3VsYXRlZCwgc3VjaCBhcyAqdG90YWxfaGhfaW5jb21lKiwgYXMgdGhleSB3aWxsIGJlIG5lZWRlZCB0byBwZXJmb3JtIHNwZWNpZmljIGxvZ2ljYWwgdGVzdHMgb24gdGhlICpfZmxhZyogY29sdW1ucy4KKEApIFRoZSAqX2ZsYWcqIGFyZSB3aGVyZSB0aGUgZmluYWwgbG9naWMgY2hlY2tzIGZvciB0aGUgc3BlY2lmaWNzIGNvbnN0cmFpbnRzIGFyZSBwZXJmb3JtZWQuIFRoZSBvdXRwdXQgd2lsbCBiZSBgVFJVRWAgb3IgYEZBTFNFYCBkZXBlbmRpbmcgb24gd2hldGhlciB0aGUgdmFsaWRhdGlvbiB0ZXN0IGhhcyBiZWVuIHBhc3NlZCBvciBub3QgZm9yIHRoYXQgY29uc3RyYWludC4KCmBgYHtyfQogIGdyb3VwX2J5KHV1aWQpICU+JQogIHN1bW1hcmlzZShlbnVtZXJhdG9yID0gZmlyc3QoYEVudW1lcmF0b3IgQ29kZWApLAogICAgICAgICAgICBHb3Zlcm5vcmF0ZSA9IGZpcnN0KEdvdmVybm9yYXRlKSwKICAgICAgICAgICAgRGlzdHJpY3QgPSBmaXJzdChEaXN0cmljdCksCiAgICAgICAgICAgIFN1YmRpc3RyaWN0ID0gZmlyc3QoU3ViZGlzdHJpY3QpLAogICAgICAgICAgICBDb21tdW5pdHkgPSBmaXJzdChDb21tdW5pdHkpLAogICAgICAgICAgICBOYW1lX0hvSCA9IGZpcnN0KE5hbWVfSG9IKSwKICAgICAgICAgICAgdG90YWxfaGhfaW5jb21lID0gc3VtKGhoX2luY29tZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgc2luZ2xlX2hlYWRfZmxhZyA9IGlmZWxzZSgoc3VtKGNoZWNrX2hoaCwgbmEucm0gPSBUUlVFKSA9PSAxKSwgVFJVRSwgRkFMU0UpLCAjIEMxOiB0aGVyZSBpcyBhIHNpbmdsZSBoZWFkIG9mIGhvdXNlaG9sZAogICAgICAgICAgICBoaF9tYXJyaWVkID0gaWZlbHNlKChzaW5nbGVfaGVhZF9mbGFnID09IFRSVUUgJiBzdW0oY2hlY2tfaGhoX21hcnJpZWQsIG5hLnJtID0gVFJVRSkgPT0gMSkgLCBUUlVFLCBGQUxTRSksICMgY2hlY2sgaWYgbWFycmllZAogICAgICAgICAgICBzcG91c2UgPSBpZmVsc2UoKHNpbmdsZV9oZWFkX2ZsYWcgPT0gVFJVRSAmIHN1bShjaGVja19zcG91c2UsIG5hLnJtID0gVFJVRSkgPj0gMSkgLCBUUlVFLCBGQUxTRSksICNjaGVjayBpZiBzcG91c2Ugd2hlbiBzaW5nbGUgaGVhZCBob3VzZWhvbGQKICAgICAgICAgICAgc3BvdXNlX2ZsYWcgPSBpZmVsc2UoKGhoX21hcnJpZWQgPT0gVFJVRSAmIHNwb3VzZSA9PSBUUlVFKSB8IChoaF9tYXJyaWVkID09IEZBTFNFICYgc3BvdXNlID09IEZBTFNFKSwgVFJVRSwgRkFMU0UpLCAjIEMxOiBjaGVjayBpZiB0aGVyZSBhcmUgc3BvdXNlL3MgaWYgbWFycmllZCBvciBubyBzcG91c2UgaWYgbm90IG1hcnJpZWQKICAgICAgICAgICAgbW9udGhseV9pbmNvbWUgPSBzdW0oYEF2ZXJhZ2UgbW9udGhseSBpbmNvbWUgaW4gU1lQYCwgbmEucm0gPSBUUlVFKSwgIyBzdW0gbW9udGhseSBpbmNvbWVzIGZvciBhbGwgaG91c2Vob2xkIG1lbWJlcnMKICAgICAgICAgICAgaW5jb21lX2ZsYWcgPSBpZmVsc2UobW9udGhseV9pbmNvbWUgPD0gc3VtKGhoX2luY29tZSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLCAjIEMzOiBjaGVjaCBpZiBjb21iaW5lZCBtb250aGx5IGluY29tZSBsZXNzIG9yIGVxdWFsIHRoYW4gcmVwb3J0ZWQgYXZlcmFnZSBtb250aGx5IGluY29tZQogICAgICAgICAgICBjaGlsZCA9IGlmZWxzZSgoc3VtKGNoZWNrX2NoaWxkLCBuYS5ybSA9IFRSVUUpID49IDEpLCBUUlVFLCBGQUxTRSksICMgY2hlY2sgaWYgbW9yZSB0aGFuIG9uZSBjaGlsZCBpbiBob3VzZWhvbGQKICAgICAgICAgICAgLi4uCmBgYAoKKEApIFdlIGZpbmFsbHkgYWRkIGEgbmV3IGNvbHVtbnMgaW4gb3JkZXIgdG8gY2hlY2sgaWYgdGhlIGhvdXNlaG9sZCBoYXMgcGFzc2VkIGFsbCB0aGUgY29uc3RyYWludHMgKGFsbCAqZmxhZ3MqIGFyZSBgVFJVRWApLiBUaGlzIGlzIHBlcmZvcm1lZCBieSBjYWxjdWxhdGluZyB0aGUgYXJpdGhtZXRpY2FsIG1lYW4gZm9yIHRoYXQgcm93LiBUaGUgYHJvd01lYW5zKClgIGZ1bmN0aW9uIHdvcmtzIGluIGEgc2ltaWxhciBtYW5uZXIgYXMgYHJvd1N1bXMoKWAgaW4gdGhlIHNlbnNlIHRoYXQgaXQgYnJlYWtzIHRoZSAqKmRwbHlyKiogYmVoYXZpb3VyIGFuZCBwZXJmb3JtcyB0aGUgY2FsY3VsYXRpb24gKnJvd3dpc2UqLiBGb3IgdGhhdCByZWFzb24sIGBzZWxlY3QoKWAgaXMgYWxzbyB1c2VkIGluIHRoZSBzYW1lIG1hbm5lciBhcyB3aXRoIGByb3dTdW1zKClgLiAgVGhlIGBlbmRzX3dpdGgoKWAgd2lsbCBtYWtlIHN1cmUgYWxsIHRoZSAqX2ZsYWcqIGNvbHVtbnMgYXJlIHNlbGVjdGVkLiBBcyBlYWNoIGBUUlVFYCB2YWx1ZSBpcyBhdXRvbWF0aWNhbGx5IGNvZXJjZWQgdG8gYDFgLCB0aGUgYXJpdGhtZXRpY2FsIG1lYW4gd2lsbCBuZWVkIHRvIGVxdWFsIGAxYCBpbiBjYXNlIGFsbCB2YWx1ZXMgYXJlIGBUUlVFYC4KYGBge3J9CiAgbXV0YXRlKGFsbHRydWVfY2hlY2sgPSByb3dNZWFucyhzZWxlY3QoLiwgZW5kc193aXRoKCJmbGFnIikpKSkjIGNoZWNrIG1lYW4gdmFsdWUgb2YgYWxsIGZsYWdzIHBlciByb3cuIFRSVUUgZXF1YWxzIHRvIDEuCi4uLgpgYGAKCihAKSBUaGUgcmVzdWx0aW5nIGRhdGFmcmFtZSBgSEhfZGF0YV9jaGVja190ZW1wYCB3aWxsIHJlY2VpdmUgbm93IHR3byBkaWZmZXJlbnQgdHJlYXRtZW50cyBkZXBlbmRpbmcgb24gaXRzIGZpbmFsIHVzZSwgZWl0aGVyIGludGVuZGVkIGZvciB0aGUgRW51bWVyYXRvcnMsIG9yIGZvciBITkFQIEluZm9ybWF0aW9uIE1hbmFnZW1lbnQgVW5pdCBmb3IgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIHJldmlldy4KKEApIGBISF9kYXRhX2NoZWNrX2ZpZWxkYCBzdG9yZXMgdGhlIGZpbmFsIG91dHB1dCBmb3IgdGhlIEVudW1lcmF0b3IgaW4gdGhlIGZpZWxkLiBJdCBzaW1wbHkgZmlsdGVycyBvdXQgdGhvc2UgcmVjb3JkcyB0aGF0IGRvIG5vdCBuZWVkIHRvIGJlIHJldmlld2VkIGFuZCBzZWxlY3QgdGhlICpmbGFnKiBjb2x1bW5zIHdoaWNoIGhpZ2hsaWdodHMgdGhlIG9uZXMgd2hpY2ggaGF2ZSB0byBiZSByZXZpZXdlZCBmb3IgZWFjaCBob3VzZWhvbGQgcmVjb3JkLgoKYGBge3J9CiMgSEggZGF0YXNldCB2YWxpZGF0ZWQgZm9yIHRoZSBFbnVtZXJhdG9ycyB0byBjaGVjawpISF9kYXRhX2NoZWNrX2ZpZWxkIDwtIEhIX2RhdGFfY2hlY2tfdGVtcCAlPiUKICBmaWx0ZXIoYWxsdHJ1ZV9jaGVjayAhPSAxKSAlPiUgIyBmaWx0ZXIgb3V0IHJvd3Mgd2hpY2ggYXJlIGFsbCBUUlVFLCBubyBuZWVkIHRvIHJldmlldy4gQWxsIGZsYWdzIFRSVUUgbWVhbiBjb2VyY2VzIHRvIGV4YWN0bHkgMS4KICBzZWxlY3QodXVpZCwgZW51bWVyYXRvciwgR292ZXJub3JhdGUsIERpc3RyaWN0LCBTdWJkaXN0cmljdCwgQ29tbXVuaXR5LCBOYW1lX0hvSCwgZW5kc193aXRoKCJmbGFnIikpCmBgYAoKKEApIEZvciB0aGUgaW50ZXJuYWwgcmV2aWV3LCB0aGVyZSBhcmUgc2V2ZXJhbCBjb2x1bW5zIHRoYXQgbWlnaHQgbmVlZCB0byBiZSByZXZpZXdlZCBpbiBvcmRlciB0byBoaWdobGlnaHQgcG90ZW50aWFsIG91dGxpZXIgdmFsdWVzIHRoYXQgY291bGQgaGF2ZSBiZWVuIHJlY29yZGVkIGFjY2lkZW50YWxseS4gVHdvIGFwcHJvYWNoZXMgYXJlIHRha2VuLCBhbmQgZm9yIGVhY2ggb2YgdGhlbSBhIGRpZmZlcmVudCBkYXRhZnJhbWUgaXMgdXNlZCBmb3IgdGhlIGZpbmFsIG91dHB1dC4gVGhlIGZpcnN0IG9uZSBhc3N1bWVzIGEgbmVhcmx5ICpub3JtYWwgZGlzdHJpYnV0aW9uKiBmb3IgdGhlIHNwZWNpZmljIGNvbHVtbiB2YWx1ZXMgKGZvciBleGFtcGxlLCB0aGUgYHRvdGFsX2hoX2luY29tZWApIGFuZCBkZXRlY3RzIHdoZXRoZXIgdGhlIHZhbHVlIGZhbGxzIHdpdGhpbiB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb3Igbm90ICh3aGljaCBjb3JyZXNwb25kcyBhcHByb3hpbWF0ZWx5IHRvICp0d28gc3RhbmRhcmQgZGV2aWF0aW9ucyogZWl0aGVyIHNpZGUgb2YgdGhlIGRhdGEgKm1lYW4qKS4gVGhlIDk1JSB2YWx1ZSBjYW4gYmUgY2hhbmdlZCBpbiB0aGUgY3VzdG9tIGZ1bmN0aW9uIGBnZXRfcW5vcm1fcmFuZ2UoKWAsIHdoaWNoIGdldHMgdGhlIGxvd2VyIGFuZCB1cHBlciBxdWFudGlsZXMgZm9yIHRoYXQgc3BlY2lmaWMgY29uZmlkZW5jZSBpbnRlcnZhbC4gVGhlIGZpbmFsIHJlc3VsdHMgd2lsbCB0aGVuIGRlcGVuZCBvbiB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBsZXZlbCB1c2VkLCB0aG91Z2ggaXQgbWlnaHQgaGlnaGxpZ2h0IHZhbHVlcyB3aGljaCB0aG91Z2ggdW5jb21tb24sIHN0aWxsIGZhbGwgd2l0aGluIGEgcmVhc29uYWJsZSBtYXJnaW4uIEluIG9yZGVyIHRvIGNoZWNrIGlmIGEgZ2l2ZW4gdmFsdWUgb2YgYHRvdGFsX2hoX2luY29tZWAgaXMgd2l0aGluIHRoZSBjYWxjdWxhdGVkIHJhbmdlLCB3ZSB1c2UgdGhlIGN1c3RvbSBgY2hlY2tfaW5fcmFuZ2UoKWAgZnVuY3Rpb24sIGFzIGRlZmluZWQgd2l0aGluIHRoZSBITkFQICoqdXRpbHMuUioqIGxpYnJhcnksIHdoaWNoIHdpbGwgb3V0cHV0IGBUUlVFYCBvciBgRkFMU0VgIGRlcGVuZGluZyBvbiB0aGUgcm93IHZhbHVlIGZvciB0aGF0IGluZGljYXRvci4KKEApIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPioqSU1QT1JUQU5UISoqPC9zcGFuPiBDdXJyZW50bHksIHRoZSBzY3JpcHQgb25seSBpbXBsZW1lbnRzIHRoZSBvdXRsaWVycyBjaGVja2luZyBmb3IgdGhlIGB0b3RhbF9oaF9pbmNvbWVgIGluZGljYXRvci4gVGhpcyBjYW4gYmUgZXh0ZW5kZWQgdG8gb3RoZXIgaW5kaWNhdG9ycyBhcyBuZWVkZWQsIGZvbGxvd2luZyBleGFjdGx5IHRoZSBzYW1lIHByb2NlZHVyZS4KCmBgYHtyfQojIEhIIGRhdGFzZXQgdmFsaWRhdGVkIGZvciBpbnRlcm5hbCByZXZpZXcgb2YgdmFsdWUgcmFuZ2VzICh1c2luZyBub3JtYWwgZGlzdHJpYnV0aW9uIHByb2JhYmlsaXR5IHJ1bGUgYXQgOTUlKQpISF9kYXRhX2NoZWNrX2ludGVybmFsX3Fub3JtIDwtIEhIX2RhdGFfY2hlY2tfdGVtcCAlPiUKbXV0YXRlKGhoX2luY29tZV9yYW5nZV9jaGVjayA9IGNoZWNrX2luX3JhbmdlKHRvdGFsX2hoX2luY29tZSwgZ2V0X3Fub3JtX3JhbmdlKHRvdGFsX2hoX2luY29tZSwgMC45NSkpKSAlPiUKc2VsZWN0KHV1aWQsIGVudW1lcmF0b3IsIEdvdmVybm9yYXRlLCBEaXN0cmljdCwgU3ViZGlzdHJpY3QsIENvbW11bml0eSwgTmFtZV9Ib0gsIHRvdGFsX2hoX2luY29tZSwgaGhfaW5jb21lX3JhbmdlX2NoZWNrLCBlbmRzX3dpdGgoImZsYWciKSkKLi4uCmBgYAogKEApIFRoZSBzZWNvbmQgYXBwcm9hY2ggdGFrZXMgYSBtb3JlIHN5c3RlbWF0aWMgd2F5IGluIG9yZGVyIHRvIGZpbmQgdGhlICpvdXRsaWVycyouIEl0IG1ha2VzIHVzZSBvZiB0aGUgY29tbW9ubHkgYWNjZXB0ZWQgW0ludGVycXVhcnRpbGUgUmFuZ2UgUnVsZSBvciAxLjUqSVFSIHJ1bGVdKGh0dHBzOi8vd3d3LnRob3VnaHRjby5jb20vd2hhdC1pcy10aGUtaW50ZXJxdWFydGlsZS1yYW5nZS1ydWxlLTMxMjYyNDQpIGFuZCBjb3VsZCBiZSBtb3JlIGVmZmVjdGl2ZSBpbiBvcmRlciB0byBzdHJhaWdodGZvcndhcmQgZGV0ZWN0IHRob3NlIGFub21hbG91cyB2YWx1ZXMgY2F1c2VkIGJ5IGFjY2lkZW50YWwgaW5wdXQuIFRoaXMgcnVsZSBpcyBpbXBsZW1lbnRlZCB3aXRoIHRoZSBjdXN0b20gZnVuY3Rpb24gYGdldF9vdXRsaWVyX3JhbmdlKClgLgogCmBgYHtyfQojIEhIIGRhdGFzZXQgdmFsaWRhdGVkIGZvciBpbnRlcm5hbCByZXZpZXcgb2YgdmFsdWUgcmFuZ2VzICh1c2luZyAxLjUqSVFSIHJ1bGUgZm9yIG91dGxpZXIgZGV0ZWN0aW9uKQpISF9kYXRhX2NoZWNrX2ludGVybmFsX2lxciA8LSBISF9kYXRhX2NoZWNrX3RlbXAgJT4lCiAgbXV0YXRlKGhoX2luY29tZV9yYW5nZV9jaGVjayA9IGNoZWNrX2luX3JhbmdlKHRvdGFsX2hoX2luY29tZSwgZ2V0X291dGxpZXJfcmFuZ2UodG90YWxfaGhfaW5jb21lKSkpICU+JQogIHNlbGVjdCh1dWlkLCBlbnVtZXJhdG9yLCBHb3Zlcm5vcmF0ZSwgRGlzdHJpY3QsIFN1YmRpc3RyaWN0LCBDb21tdW5pdHksIE5hbWVfSG9ILCB0b3RhbF9oaF9pbmNvbWUsIGhoX2luY29tZV9yYW5nZV9jaGVjaywgZW5kc193aXRoKCJmbGFnIikpCi4uLgpgYGAKIAojIyMgT3V0cHV0ClRoZSBzY3JpcHQgZ2VuZXJhdGVzIGFuIGV4Y2VsIG91dHB1dCBmaWxlIGludGVuZGVkIGZvciB0aGUgRW51bWVyYXRvciwgd2hpY2ggY29udGFpbnMgdGhlIHJlc3VsdHMgc3RvcmVkIGluIHRoZSBgSEhfZGF0YV9jaGVja19maWVsZGAuIEl0IG1ha2VzIHVzZSBvZiB0aGUgKipvcGVueGxzeCoqIGxpYnJhcnkgYW5kIGl0IGRlZmluZXMgc2V2ZXJhbCBmb3JtYXR0aW5nIHN0eWxlcyBmb3IgdGhlIGRpZmZlcmVudCBzZWN0aW9ucyBvZiB0aGUgZXhjZWwgc2hlZXQuCgpgYGB7cn0KIyBjcmVhdGUgYW4gZXhjZWwgd29ya2Jvb2sgYW5kIHNoZWV0Cm91dHB1dF9maWxlIDwtIHBhc3RlMChvdXRwdXRfZGlyLCAiLyIsIGZpbGVfdGl0bGUsICIgLSBEYXRhIENoZWNrIC0gRmllbGQueGxzeCIpCm5fcm93cyA8LSBucm93KEhIX2RhdGFfY2hlY2tfZmllbGQpCm5fY29scyA8LSBuY29sKEhIX2RhdGFfY2hlY2tfZmllbGQpCndiIDwtIGNyZWF0ZVdvcmtib29rKCkKYWRkV29ya3NoZWV0KHdiLCAiSEggU2FtcGxlIFNpemUiKQpzZXRDb2xXaWR0aHMod2IsIHNoZWV0ID0gMSwgY29scyA9IDE6bl9jb2xzLCB3aWR0aHMgPSAiMjAiKQoKCiMgc2V0IHN0eWxlcwp0b3Bfcm93X3N0eWxlIDwtIGNyZWF0ZVN0eWxlKHdyYXBUZXh0ID0gVFJVRSwgdmFsaWduID0gImNlbnRlciIsIGZnRmlsbCA9ICIjRThFOEU4IikKZmFsc2VTdHlsZSA8LSBjcmVhdGVTdHlsZShmb250Q29sb3VyID0gIiM5QzAwMDYiLCBiZ0ZpbGwgPSAiI0ZGQzdDRSIpCnRydWVTdHlsZSA8LSBjcmVhdGVTdHlsZShmb250Q29sb3VyID0gIiMwMDYxMDAiLCBiZ0ZpbGwgPSAiI0M2RUZDRSIpCmNvbmRpdGlvbmFsRm9ybWF0dGluZyh3Yiwgc2hlZXQgPSAxLCBjb2xzID0gODoxOCwgcm93cyA9IDM6KG5fcm93cyArIDMpLCB0eXBlID0gImNvbnRhaW5zIiwgcnVsZSA9ICJUUlVFIiwgc3R5bGUgPSB0cnVlU3R5bGUpCmNvbmRpdGlvbmFsRm9ybWF0dGluZyh3Yiwgc2hlZXQgPSAxLCBjb2xzID0gODoxOCwgcm93cyA9IDM6KG5fcm93cyArIDMpLCB0eXBlID0gImNvbnRhaW5zIiwgcnVsZSA9ICJGQUxTRSIsIHN0eWxlID0gZmFsc2VTdHlsZSkKLi4uCmBgYAooQCkgVHdvIGFkZGl0aW9uYWwgcm93cyBhcmUgYWRkZWQgdG8gdGhlIHRvcCBvZiB0aGUgdGFibGUsIGNvbnRhaW5pbmcgc3BlY2lmaWMgaW5mb3JtYXRpb24gb24gdGhlIHR5cGUgb2YgY29uc3RyYWluIGNoZWNrIHBlcmZvcm1lZCBhbmQgYWxzbyBzdWdnZXN0aW9ucyBvbiB0aGUgc3VydmV5IHF1ZXN0aW9ucyB0byByZXZpZXcgaW4gdGhlIG9yaWdpbmFsIGRhdGEuIFNwZWNpYWwgZm9ybWF0dGluZyBpcyBhZGRlZCB0byB0aGVzZSByb3dzIHVzaW5nIHRoZSBgYWRkU3R5bGUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgKipvcGVueGxzeCoqIHBhY2thZ2UuIFRvIG1haW50YWluIHByZXZpb3VzIGxvYWRlZCBzdHlsZXMsIHVzZSB0aGUgYHN0YWNrID0gVFJVRWAgcGFyYW1ldGVyLgoKYGBge3J9CiMgY3JlYXRlIHdhcm5pbmcgYW5kIHN1Z2dlc3Rpb24gcm93cwp3YXJuaW5nX3JvdyA8LSBhcy5kYXRhLmZyYW1lKHQod2FybmluZ19jb2x1bW5fbmFtZXMpKQpzdWdnZXN0aW9uX3JvdyA8LSBhcy5kYXRhLmZyYW1lKHQoc3VnZ2VzdGlvbl9jb2x1bW5fbmFtZXMpKQp3cml0ZURhdGEod2IsIHNoZWV0ID0gMSwgd2FybmluZ19yb3csIHN0YXJ0Um93ID0gMSwgc3RhcnRDb2wgPSA4LCBjb2xOYW1lcyA9IEZBTFNFLCBib3JkZXJzID0gImFsbCIpCmFkZFN0eWxlKHdiLCBzaGVldCA9IDEsIHRvcF9yb3dfc3R5bGUsIHJvd3MgPSAxLCBjb2xzID0gODoxOCwgZ3JpZEV4cGFuZCA9IFRSVUUsIHN0YWNrID0gVFJVRSkKd3JpdGVEYXRhKHdiLCBzaGVldCA9IDEsIHN1Z2dlc3Rpb25fcm93LCBzdGFydFJvdyA9IDIsIHN0YXJ0Q29sID0gOCwgY29sTmFtZXMgPSBGQUxTRSwgYm9yZGVycyA9ICJhbGwiKQphZGRTdHlsZSh3Yiwgc2hlZXQgPSAxLCB0b3Bfcm93X3N0eWxlLCByb3dzID0gMiwgY29scyA9IDg6MTgsIGdyaWRFeHBhbmQgPSBUUlVFLCBzdGFjayA9IFRSVUUpCi4uLgpgYGAKKEApIFRoZSBkYXRhIGlzIGZpbmFsbHkgZXhwb3J0ZWQKYGBge3J9CiMgZXhwb3J0IHJlc3VsdCBpbnRvIGV4Y2VsCndyaXRlRGF0YVRhYmxlKHdiLCBzaGVldCA9IDEsIEhIX2RhdGFfY2hlY2tfZmllbGQsIHN0YXJ0Um93ID0gMywgc3RhcnRDb2wgPSAxKQpzYXZlV29ya2Jvb2sod2IsIGZpbGUgPSBvdXRwdXRfZmlsZSwgb3ZlcndyaXRlID0gVFJVRSkKLi4uCmBgYAoKIyMjIFJ1bm5pbmcgZXZlcnl0aGluZyEKClRoZSAqSEgtc3VydmV5LWNoZWNrLlIqIG1haW4gc2NyaXB0IGNvbnRhaW5zIHRoZSBzb3VyY2UgZmlsZXMgaW4gdGhlIG9yZGVyIHRoYXQgbmVlZCB0byBiZSBsb2FkZWQgYW5kIHJ1bi4gVGhlICoqaGVyZSoqIGxpYnJhcnkgd2lsbCBkZWFsIHdpdGggcmVsYXRpdmUgYW5kIGRpcmVjdCBwYXRoIGlzc3VlcyB0byBtYWtlIGl0IGlzc3VlIHJ1bm5pbmcgdGhlIHRvb2wgdW5kZXIgZGlmZmVyZW50IGZpbGUgc3lzdGVtcy4gSXQgdGFrZXMgdGhlIHJvb3QgZGlyZWN0b3J5ICh0aGUgb25lIGNvbnRhaW5pbmcgdGhlIFJzdHVkaW8gKi5ycHJvaiogZmlsZSkgYXMgcmVmZXJlbmNlLiBUaGUgKl9jb21tb24qIGRpcmVjdG9yeSBzdG9yZXMgdGhlIGNvbW1vbiBzY3JpcHRzIHVzZWQgYWNyb3NzIGFsbCBITkFQIHRvb2xzLCBkZWZpbmluZyB0aGUgbWluaW11bSByZXF1aXJlZCBsaWJyYXJpZXMgYW5kIHNldmVyYWwgY29tbW9ubHkgdXNlZCBmdW5jdGlvbnMuIFRoZSByZXN0IG9mIHRoZSB0b29sIHNjcmlwdHMgYXJlIGxvYWRlZCBzZXF1ZW50aWFsbHkuIFVzZSB0aGUgKlNvdXJjZSogYnV0dG9uIG9uICpSU3R1ZGlvKiB0byBydW4gdGhlIHdob2xlIHNlcXVlbmNlLgoKYGBge3J9CiMgbG9hZCBoZXJlIGxpYnJhcnkgaHRvIGhhbmRsZSByZWxhdGl2ZSBwYXRocyBmcm9tIHByb2plY3QgUk9PVApsaWJyYXJ5KGhlcmUpCgpzb3VyY2UoaGVyZSgiUiIsICJfY29tbW9uIiwgInJlcXVpcmVkLlIiKSkKc291cmNlKGhlcmUoIlIiLCAiX2NvbW1vbiIsICJ1dGlscy5SIikpCnNvdXJjZShoZXJlKCJSIiwgIkhIX3N1cnZleV9jaGVjayIsICIwMS1oaC1yZXF1aXJlZC5SIikpCnNvdXJjZShoZXJlKCJSIiwgIkhIX3N1cnZleV9jaGVjayIsICIwMi1oaC1mdW5jdGlvbnMuUiIpKQpzb3VyY2UoaGVyZSgiUiIsICJISF9zdXJ2ZXlfY2hlY2siLCAiMDMtaGgtY29uZmlnLlIiKSkKc291cmNlKGhlcmUoIlIiLCAiSEhfc3VydmV5X2NoZWNrIiwgIjA0LWhoLWRhdGFsb2FkLlIiKSkKc291cmNlKGhlcmUoIlIiLCAiSEhfc3VydmV5X2NoZWNrIiwgIjA1LWhoLXByb2Nlc3MuUiIpKQpzb3VyY2UoaGVyZSgiUiIsICJISF9zdXJ2ZXlfY2hlY2siLCAiMDYtaGgtb3V0cHV0LlIiKSkKYGBg