Introduction
At the heart of many programming tasks is the concept of repeating a
task multiple times. A for
loop in R allows us to do just
that. Loops enable efficient repetition, saving time and effort.
Whether you’re a beginner or an experienced coder, mastering these
concepts is essential for writing intelligent R code.
Let’s dive in and enhance your coding skills!
Learning
Objectives
By the end of this lesson, you will be able to:
- Explain the syntax and structure of a basic
for
loop in
R
- Use index variables to iterate through multiple vectors
simultaneously in a loop
- Integrate
if/else
conditional statements within a
loop
- Store loop results in vectors and lists
- Apply loops to tasks like analyzing multiple datasets and generating
multiple plots
- Debug loops by isolating and testing single iterations
Packages
‣ We will use multiple packages in this lesson
‣ Ensure the following packages are installed and
loaded:
# Load necessary packages
if(!require(pacman)) install.packages("pacman")
pacman::p_load(tidyverse, here, openxlsx, tools, outbreaks, medicaldata)
Intro to
for
Loops
‣ Let’s start with a simple example of using for
loops
in R
‣ Suppose we have a vector of children’s ages and
want to convert these to months
‣ First, create a vector of ages in years
ages <- c(7, 8, 9) # ages 7 8 and 9
‣ We could easily convert ages to months using *
operation in R
## [1] 84 96 108
‣ What R is doing (conceptually) though is running a for loop. Let’s
write it out explicitly
for (age in ages) print(age * 12)
## [1] 84
## [1] 96
## [1] 108
‣ age
is a temporary variable that takes each element in
ages
‣ You can choose any name for this variable
for (random_name in ages) print(random_name * 12) # random_name
## [1] 84
## [1] 96
## [1] 108
‣ If the loop’s content is more than one line, use
curly brackets {}
for (age in ages) { # multiple line loop
age_months <- age * 12
print(age_months)
}
## [1] 84
## [1] 96
## [1] 108
‣ The general structure of any for
loop:

(NOTE: Answers are at the bottom of the page. Try to answer the
questions yourself before checking.)
Hours to Minutes Basic Loop
Try converting hours to minutes using a for
loop. Start
with this vector of hours:
hours <- c(3, 4, 5) # Vector of hours
# Your code here
for (hour in hours) {
minutes <- hour * 60
print(minutes) # convert hours to minutes and print(minutes)
}
‣ Side-note: Loops can be nested within each other. For instance:
# for i in 1:2, for j in 1:2, print i * j
for (i in 1:2) {
for (j in 1:2) {
print(i * j)
}
}
## [1] 1
## [1] 2
## [1] 2
## [1] 4
‣ This creates a combination of i
and j
values as shown in this table:
‣ Nested loops are less common though, and often have more efficient
alternatives.
:::
Are for
Loops Useful in R?
‣ R already has vectorized operations!
‣ Example: Age conversion without a loop. (Does not work by default
in most programming languages)
## [1] 84 96 108
‣ Moreover, usually working with data frames and
tidyverse
operations:
ages_df <- tibble(age = ages)
# mutate ages_df to add age_months column
ages_df %>%
mutate(age_months = age * 12)
## # A tibble: 3 × 2
## age age_months
## <dbl> <dbl>
## 1 7 84
## 2 8 96
## 3 9 108
‣ Loops will be quite useful in specific scenarios:
- operations on multiple data frames
- working with non-dataframe objects (e.g. plots)
‣ We’ll see this later in the lesson. For now, will focus on simple,
“toy” examples.
‣ Loops vs function mapping?
‣ Often, loop tasks can be replaced with custom functions which are
then mapped across a vector or data frame.
‣ But loops are easy to learn, think about and debug, even for
beginners.
Looping with an
Index
‣ Often useful to loop through a vector using an index, which is a
counter for the current iteration.
‣ Example: converting ages in years to months using an index:
# Recall:
ages <- c(7, 8, 9) # Vector of ages
‣ Create a sequence of indices the same length as the ages
vector:
1:length(ages) # 1 to the length of ages
## [1] 1 2 3
indices <- 1:length(ages) # then assign to object called indices
‣ Use the index in a for
loop to convert each age to
months:
# for i in indices, print ages[i] * 12
for (i in indices) print(ages[i] * 12)
## [1] 84
## [1] 96
## [1] 108
‣ The variable name in the loop (e.g., i
,
j
, index
) is arbitrary.
‣ Of course, index-based loops can be used directly without a
separate variable:
# for i in 1:length(ages), print ages[i] * 12
for (i in 1:length(ages)) print(ages[i] * 12)
## [1] 84
## [1] 96
## [1] 108
‣ Such indexed loops will be helpful for working with multiple
vectors simultaneously.
Hours to Minutes Indexed Loop
Rewrite your loop from last question using indices:
hours <- c(3, 4, 5) # Vector of hours
# Your code here
for (i in 1:length(hours)) {
print(hours[i] * 60)
}
‣ Side-note: The function seq_along()
is a shortcut for
creating a sequence of indices.
‣ Equivalent to 1:length()
:
# These two are equivalent ways to generate sequence of indices for `ages`
1:length(ages)
## [1] 1 2 3
## [1] 1 2 3
Looping on Multiple
Vectors
‣ Looping with indices allows us to work with multiple vectors
simultaneously.
# Consider:
ages <- c(7, 8, 9) # ages in years
heights <- c(120, 130, 140) # heights in cm
‣ We can loop through both using the index method:
# for i in 1:length(ages), print a pasted string with age and height
for(i in 1:length(ages)) {
age <- ages[i]
height <- heights[i]
print(paste("Age:", age, "Height:", height))
}
## [1] "Age: 7 Height: 120"
## [1] "Age: 8 Height: 130"
## [1] "Age: 9 Height: 140"
‣ In each iteration:
i
is the index.
- We extract the ith element from each vector
- We paste the two together
- We print the result
Alternatively, we can skip the variable assignment and use the
indices in the print()
statement directly:
for(i in 1:length(ages)) {
print(paste("Age:", ages[i], "Height:", heights[i]))
}
## [1] "Age: 7 Height: 120"
## [1] "Age: 8 Height: 130"
## [1] "Age: 9 Height: 140"
BMI Calculation Loop
Using a for loop, calculate the Body Mass Index (BMI) of the three
individuals shown below. The formul for BMI is
BMI = weight / (height ^ 2)
.
weights <- c(30, 32, 35) # Weights in kg
heights <- c(1.2, 1.3, 1.4) # Heights in meters
for(i in 1:length(weights)) {
weight <- weights[i]
height <- heights[i]
bmi <- weight/(height^2)
print(paste("Weight:", weight,
"Height:", height,
"BMI:", bmi
))
}
Storing Loop
Results
‣ Usually we want to store loop results rather than just
printing.
‣ Example: converting ages to months.
# Recall this loop:
ages <- c(7, 8, 9) # Vector of ages
for(i in 1:length(ages)) {
print(ages[i] * 12)
}
## [1] 84
## [1] 96
## [1] 108
‣ Let’s try to store this info in a vector.
‣ Create an empty vector to store results.
ages_months <- vector(mode = "numeric", length = length(ages)) # vector of mode "numeric" and length 3
ages_months
## [1] 0 0 0
‣ How do we store values in this vector?
ages_months[1] <- 99 # Store 99 in the first element of ages_months
ages_months[2] <- 100 # Store 100 in the second element of ages_months
ages_months
## [1] 99 100 0
‣ Now, let’s execute the loop, storing the results in
ages_months
:
ages_months <- vector("numeric", length(ages)) # Create `age_months` vector of mode "numeric" and length of ages
for (i in 1:length(ages)){
ages_months[i] <- ages[i] * 12
} # for i in 1:length(ages)
# store ages[i] * 12 in ages_months[i]
ages_months
## [1] 84 96 108
Height cm to m
Use a for loop to convert height measurements from cm to m. Store the
results in a vector called height_meters
.
height_cm <- c(180, 170, 190, 160, 150) # Heights in cm
height_m <- vector("numeric", length(height_cm)) # numeric vector of same length as height_cm
for (i in 1:length(height_cm)) {
height_m[i] <- height_cm[i]/100
}
height_m
‣ Watch out! Create your empty object outside the
loop to save all iteration results.
# Consider this:
ages <- c(7, 8, 9)
for (i in 1:length(ages)) {
ages_months <- vector("numeric", length(ages))
ages_months[i] <- ages[i] * 12
}
ages_months
## [1] 0 0 108
‣ Do you see the problem?
‣ Side Note. In a rush? Initialize your vector with c()
and append values:
ages <- c(7, 8, 9)
ages_months <- c() # quick and dirty way to initialize vector
for (i in 1:length(ages)) {
ages_months[i] <- ages[i] * 12
}
ages_months
## [1] 84 96 108
‣ And, you can append values at the end of the vector using
c()
:
# Redo loop, but append values to end of vector with c()
ages <- c(7, 8, 9)
ages_months <- c() # quick and dirty way to initialize vector
for (i in 1:length(ages)) {
ages_months <- c(ages_months, ages[i]*12)
}
ages_months
## [1] 84 96 108
## [1] 9
number <- c(number, 10)
number
## [1] 9 10
‣ Discouraged because R does not know the final length of the vector,
so reallocates memory each time you append a value.
‣ Slow performance with large vectors. But for quick analysis, it’s
fine.
If Statements in
Loops
‣ If
statements can be integrated into loops in R.
‣ Example: Classifying ages as “Child” if under 18.
age_vec <- c(2, 12, 17, 24, 60) # Vector of ages
# for age in ages, if age < 18, print "Child"
for (age in age_vec){
if (age < 18) {
print("Child")
}
}
## [1] "Child"
## [1] "Child"
## [1] "Child"
‣ Use curly braces for clarity and adding more code.
age_vec <- c(2, 12, 17, 24, 60) # Vector of ages
# for age in ages, if age < 18, print "Child"
for (age in age_vec){
if (age < 18) {
print("Processing:") # put these in loop:
print(paste("Child, Age", age ))
}
}
## [1] "Processing:"
## [1] "Child, Age 2"
## [1] "Processing:"
## [1] "Child, Age 12"
## [1] "Processing:"
## [1] "Child, Age 17"
‣ Let’s add another condition with else if
as ‘Child’ or
‘Teen’ based on age.
# Add else if age >= 13 && age < 18, print "Teen"
for (age in age_vec) {
if (age < 13) {
print(paste("Child, Age", age))
} else if (age >= 13 && age < 18){
print(paste("Teen, Age", age))
}
}
## [1] "Child, Age 2"
## [1] "Child, Age 12"
## [1] "Teen, Age 17"
‣ Final else
statement for other ages, classifying as
‘Adult’.
# Add final else statement for "Adult"
for (age in age_vec) {
if (age < 13) {
print(paste("Child, Age", age))
} else if (age >= 13 && age < 18){
print(paste("Teen, Age", age))
} else {
print(paste("Adult, Age", age))
}
}
## [1] "Child, Age 2"
## [1] "Child, Age 12"
## [1] "Teen, Age 17"
## [1] "Adult, Age 24"
## [1] "Adult, Age 60"
‣ We can store these classifications in a vector using index-based
loop.
age_class <- vector("character", length(age_vec)) # vector of mode "character" and length of age_vec
# Refactor loop from above to store classifications in age_class
for (i in 1:length(age_vec)) {
if (age_vec[i] < 13) {
age_class[i] <- paste("Child, Age", age_vec[i])
} else if (age_vec[i] >= 13 && age_vec[i] < 18){
age_class[i] <- paste("Teen, Age", age_vec[i])
} else {
age_class[i] <- paste("Adult, Age", age_vec[i])
}
}
age_class
## [1] "Child, Age 2" "Child, Age 12" "Teen, Age 17" "Adult, Age 24"
## [5] "Adult, Age 60"
Temperature Classification
You have a vector of body temperatures in Celsius. Classify each
temperature as ‘Hypothermia’, ‘Normal’, or ‘Fever’ using a
for
loop combined with if
and
else
statements.
Use these rules:
- Below 36.0°C: ‘Hypothermia’
- Between 36.0°C and 37.5°C: ‘Normal’
- Above 37.5°C: ‘Fever’
body_temps <- c(35, 36.5, 37, 38, 39.5) # Body temperatures in Celsius
classif_vec <- vector("character", length(body_temps)) # character vec, length of body_temps
for (i in 1:length(body_temps)) {
# Add your if-else logic here
if (body_temps[i] < 36.0) {
classif_vec[i] <- paste(body_temps[i], "°C is Hypothermia")
}
else if (body_temps[i] >= 36.5 && body_temps[i] <= 37.5 ) {
classif_vec[i] <- paste(body_temps[i], "°C is Normal")
}
else {
classif_vec[i] <- paste(body_temps[i], "°C is Fever")
}
## add other conditions and their corresponding classifications
}
classif_vec
An expected output is below
35°C is Hypothermia
36.5°C is Normal
37°C is Normal
38°C is Fever
39.5°C is Fever
Real Loops Application:
Generating Multiple Plots
‣ Using loops to generate multiple plots for
different groups within a dataset.
‣ Example with the strep_tb
dataset from
medicaldata
package.
‣ Aim: create category inspection plots for each
radiologic 6-month improvement group using
inspectdf::inspect_cat()
.
‣ First, create a single plot for the first
radiologic improvement group.
cat_plot <-
medicaldata::strep_tb %>%
filter(radiologic_6m == "6_considerable_improvement") %>%
inspectdf::inspect_cat() %>%
inspectdf::show_plot()
# filter radiologic_6m to "6_Considerable_improvement"
# inspect_cat
# show_plot
‣ Want to create similar plots for each radiologic
improvement group.
‣ Identify all unique values of
medicaldata::strep_tb$radiologic_6m
radiologic_levels_6m <- unique(medicaldata::strep_tb$radiologic_6m)
‣ Initiate an empty list object to store the
plots.
cat_plot_list <- vector("list", length(radiologic_levels_6m))
‣ Optionally, set names of list elements to the radiologic
improvement groups.
names(cat_plot_list) <- radiologic_levels_6m
cat_plot_list
## $`6_Considerable_improvement`
## NULL
##
## $`5_Moderate_improvement`
## NULL
##
## $`4_No_change`
## NULL
##
## $`3_Moderate_deterioration`
## NULL
##
## $`2_Considerable_deterioration`
## NULL
##
## $`1_Death`
## NULL
‣ Let’s put it together
cat_plot_list <- vector("list", length(radiologic_levels_6m))
for (level in radiologic_levels_6m) {
# Generate plot for each level
cat_plot <-
medicaldata::strep_tb %>%
filter(radiologic_6m == level) %>%
inspectdf::inspect_cat() %>%
inspectdf::show_plot()
# Append to the list
cat_plot_list[[level]] <- cat_plot
}
‣ Access a specific plot using double bracket syntax
or by number.
cat_plot_list[["6_Considerable_improvement"]] # "6_Considerable_improvement"

cat_plot_list[["5_Moderate_improvement"]]

## NULL
‣ To display all plots at once, call the entire list.
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
##
## [[5]]
## NULL
##
## [[6]]
## NULL
##
## $`6_Considerable_improvement`
##
## $`5_Moderate_improvement`
##
## $`4_No_change`
##
## $`3_Moderate_deterioration`
##
## $`2_Considerable_deterioration`
##
## $`1_Death`






Visualizing TB Cases
In this exercise, you will use WHO data from the tidyr
package to create line graphs showing the number of new TB cases in
children over the years in South American countries.
First, we’ll prepare the data:
tb_child_cases <- tidyr::who2 %>%
transmute(country, year,
tb_cases_children = sp_m_014 + sp_f_014 + sn_m_014 + sn_f_014) %>%
filter(country %in% c("Brazil", "Colombia", "Argentina",
"Uruguay", "Chile", "Guyana")) %>%
filter(year >= 2006)
tb_child_cases
## # A tibble: 48 × 3
## country year tb_cases_children
## <chr> <dbl> <dbl>
## 1 Argentina 2006 880
## 2 Argentina 2007 1162
## 3 Argentina 2008 961
## 4 Argentina 2009 593
## 5 Argentina 2010 491
## 6 Argentina 2011 867
## 7 Argentina 2012 745
## 8 Argentina 2013 NA
## 9 Brazil 2006 2254
## 10 Brazil 2007 2237
## # ℹ 38 more rows
Now, fill in the blanks in the template below to create a line graph
for each country using a for
loop:
# Get list of countries. Hint: Use unique() on the country column
countries <- unique(tb_child_cases$country)
# Create list to store plots. Hint: Initialize an empty list
tb_child_cases_plots <- vector("list", length(countries))
names(tb_child_cases_plots) <- countries # Set names of list elements
# Loop through countries
for (name in countries) {
# Filter data for each country
tb_child_cases_filtered <- filter(tb_child_cases, country == name)
# Make plot
tb_child_cases_plot <- ggplot(tb_child_cases_filtered, aes(x = year, y = tb_cases_children)) +
geom_line() +
ggtitle(paste("TB Cases in Children -", name))
# Append to list. Hint: Use double brackets
tb_child_cases_plots[[name]] <- tb_child_cases_plot
}
tb_child_cases_plots
## $Argentina
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

##
## $Brazil
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

##
## $Chile
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

##
## $Colombia
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

##
## $Guyana
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

##
## $Uruguay
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

Wrap Up!
In this lesson, we delved into for loops in R, demonstrating their
utility from basic tasks to complex data analysis involving multiple
datasets and plot generation. Despite R’s preference for vectorized
operations, for loops are indispensable in certain scenarios. Hopefully,
this lesson has equipped you with the skills to confidently implement
for loops in various data processing contexts.
Answer Key
Hours to Minutes
Basic Loop
hours <- c(3, 4, 5) # Vector of hours
for (hour in hours) {
minutes <- hour * 60
print(minutes)
}
## [1] 180
## [1] 240
## [1] 300
Hours to Minutes
Indexed Loop
hours <- c(3, 4, 5) # Vector of hours
for (i in 1:length(hours)) {
minutes <- hours[i] * 60
print(minutes)
}
## [1] 180
## [1] 240
## [1] 300
BMI Calculation
Loop
weights <- c(30, 32, 35) # Weights in kg
heights <- c(1.2, 1.3, 1.4) # Heights in meters
for(i in 1:length(weights)) {
bmi <- weights[i] / (heights[i] ^ 2)
print(paste("Weight:", weights[i],
"Height:", heights[i],
"BMI:", bmi))
}
## [1] "Weight: 30 Height: 1.2 BMI: 20.8333333333333"
## [1] "Weight: 32 Height: 1.3 BMI: 18.9349112426035"
## [1] "Weight: 35 Height: 1.4 BMI: 17.8571428571429"
Height cm to m
height_cm <- c(180, 170, 190, 160, 150) # Heights in cm
height_m <- vector("numeric", length = length(height_cm))
for (i in 1:length(height_cm)) {
height_m[i] <- height_cm[i] / 100
}
height_m
## [1] 1.8 1.7 1.9 1.6 1.5
Temperature
Classification
body_temps <- c(35, 36.5, 37, 38, 39.5) # Body temperatures in Celsius
classif_vec <- vector(mode = "character", length = length(body_temps))
for (i in 1:length(body_temps)) {
if (body_temps[i] < 36.0) {
classif_vec[i] <- paste(body_temps[i], "°C is Hypothermia")
} else if (body_temps[i] <= 37.5) {
classif_vec[i] <- paste(body_temps[i], "°C is Normal")
} else {
classif_vec[i] <- paste(body_temps[i], "°C is Fever")
}
}
classif_vec
## [1] "35 °C is Hypothermia" "36.5 °C is Normal" "37 °C is Normal"
## [4] "38 °C is Fever" "39.5 °C is Fever"
Visualizing TB
Cases
# Assuming tb_child_cases is a dataframe with the necessary columns
countries <- unique(tb_child_cases$country)
# Create list to store plots
tb_child_cases_plots <- vector("list", length(countries))
names(tb_child_cases_plots) <- countries
# Loop through countries
for (countryname in countries) {
# Filter data for each country
tb_child_cases_filtered <- filter(tb_child_cases, country == countryname)
# Make plot
tb_child_cases_plot <- ggplot(tb_child_cases_filtered, aes(x = year, y = tb_cases_children)) +
geom_line() +
ggtitle(paste("TB Cases in Children -", countryname))
# Append to list
tb_child_cases_plots[[countryname]] <- tb_child_cases_plot
}
tb_child_cases_plots[["Uruguay"]]
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).

Contributors
The following team members contributed to this lesson:
References
Some material in this lesson was adapted from the following
sources:
LS0tDQp0aXRsZTogJ0xvb3BzLCBBY3Jvc3MsIGFuZCBDb25kaXRpb25hbHMnDQphdXRob3I6DQogIC0gbmFtZTogIktlbmUgRGF2aWQgTndvc3UiDQogIC0gbmFtZTogIlNhYmluYSBSb2RyaWd1ZXogVmVsYXNxdWV6Ig0KZGF0ZTogIjIwMjQtMTEtMTgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9mb2xkaW5nOiAic2hvdyINCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY3NzOiAhZXhwciBoZXJlOjpoZXJlKCJnbG9iYWwvc3R5bGUvc3R5bGUuY3NzIikNCiAgICBoaWdobGlnaHQ6IGthdGUNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUNCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KYGBge3IsIGVjaG8gPSBGLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9DQojIExvYWQgcGFja2FnZXMgDQppZighcmVxdWlyZShwYWNtYW4pKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFjbWFuOjpwX2xvYWQocmxhbmcsIHRpZHl2ZXJzZSwga25pdHIsIGhlcmUsIHJlYWN0YWJsZSwgZ3QsIGZsZXh0YWJsZSkNCg0KIyMgZnVuY3Rpb25zDQpzb3VyY2UoaGVyZTo6aGVyZSgiZ2xvYmFsL2Z1bmN0aW9ucy9taXNjX2Z1bmN0aW9ucy5SIikpDQoNCiMjIGRlZmF1bHQgcmVuZGVyDQpyZWdpc3RlclMzbWV0aG9kKCJyZWFjdGFibGVfNV9yb3dzIiwgImRhdGEuZnJhbWUiLCByZWFjdGFibGVfNV9yb3dzKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNsYXNzLnNvdXJjZSA9ICJ0Z2MtY29kZS1ibG9jayIsIGVycm9yID0gVCkNCg0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNCkF0IHRoZSBoZWFydCBvZiBtYW55IHByb2dyYW1taW5nIHRhc2tzIGlzIHRoZSBjb25jZXB0IG9mIHJlcGVhdGluZyBhIHRhc2sgbXVsdGlwbGUgdGltZXMuIEEgYGZvcmAgbG9vcCBpbiBSIGFsbG93cyB1cyB0byBkbyBqdXN0IHRoYXQuIExvb3BzIGVuYWJsZSBlZmZpY2llbnQgcmVwZXRpdGlvbiwgc2F2aW5nIHRpbWUgYW5kIGVmZm9ydC4NCg0KV2hldGhlciB5b3UncmUgYSBiZWdpbm5lciBvciBhbiBleHBlcmllbmNlZCBjb2RlciwgbWFzdGVyaW5nIHRoZXNlIGNvbmNlcHRzIGlzIGVzc2VudGlhbCBmb3Igd3JpdGluZyBpbnRlbGxpZ2VudCBSIGNvZGUuDQoNCkxldCdzIGRpdmUgaW4gYW5kIGVuaGFuY2UgeW91ciBjb2Rpbmcgc2tpbGxzIQ0KDQojIExlYXJuaW5nIE9iamVjdGl2ZXMNCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxlc3NvbiwgeW91IHdpbGwgYmUgYWJsZSB0bzoNCg0KLSAgIEV4cGxhaW4gdGhlIHN5bnRheCBhbmQgc3RydWN0dXJlIG9mIGEgYmFzaWMgYGZvcmAgbG9vcCBpbiBSDQotICAgVXNlIGluZGV4IHZhcmlhYmxlcyB0byBpdGVyYXRlIHRocm91Z2ggbXVsdGlwbGUgdmVjdG9ycyBzaW11bHRhbmVvdXNseSBpbiBhIGxvb3ANCi0gICBJbnRlZ3JhdGUgYGlmL2Vsc2VgIGNvbmRpdGlvbmFsIHN0YXRlbWVudHMgd2l0aGluIGEgbG9vcA0KLSAgIFN0b3JlIGxvb3AgcmVzdWx0cyBpbiB2ZWN0b3JzIGFuZCBsaXN0cw0KLSAgIEFwcGx5IGxvb3BzIHRvIHRhc2tzIGxpa2UgYW5hbHl6aW5nIG11bHRpcGxlIGRhdGFzZXRzIGFuZCBnZW5lcmF0aW5nIG11bHRpcGxlIHBsb3RzDQotICAgRGVidWcgbG9vcHMgYnkgaXNvbGF0aW5nIGFuZCB0ZXN0aW5nIHNpbmdsZSBpdGVyYXRpb25zDQoNCiMjIFBhY2thZ2VzDQoNCuKAoyBXZSB3aWxsIHVzZSBtdWx0aXBsZSBwYWNrYWdlcyBpbiB0aGlzIGxlc3Nvbg0KDQrigKMgKipFbnN1cmUgdGhlIGZvbGxvd2luZyBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIGFuZCBsb2FkZWQ6KioNCg0KYGBge3J9DQojIExvYWQgbmVjZXNzYXJ5IHBhY2thZ2VzDQppZighcmVxdWlyZShwYWNtYW4pKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCBoZXJlLCBvcGVueGxzeCwgdG9vbHMsIG91dGJyZWFrcywgbWVkaWNhbGRhdGEpDQpgYGANCg0KIyMgSW50cm8gdG8gYGZvcmAgTG9vcHMNCg0K4oCjIExldCdzIHN0YXJ0IHdpdGggYSBzaW1wbGUgZXhhbXBsZSBvZiB1c2luZyBgZm9yYCBsb29wcyBpbiBSDQoNCuKAoyBTdXBwb3NlIHdlIGhhdmUgYSAqKnZlY3RvciBvZiBjaGlsZHJlbidzIGFnZXMqKiBhbmQgd2FudCB0byAqKmNvbnZlcnQgdGhlc2UgdG8gbW9udGhzKioNCg0K4oCjIEZpcnN0LCBjcmVhdGUgYSB2ZWN0b3Igb2YgYWdlcyBpbiB5ZWFycw0KDQpgYGB7cn0NCmFnZXMgPC0gYyg3LCA4LCA5KSAgICAgICAgICAgICAgICAgICAgICAgICMgYWdlcyA3IDggYW5kIDkNCmBgYA0KDQrigKMgV2UgY291bGQgZWFzaWx5IGNvbnZlcnQgYWdlcyB0byBtb250aHMgdXNpbmcgYCpgIG9wZXJhdGlvbiBpbiBSDQoNCmBgYHtyfQ0KYWdlcyAqIDEyDQpgYGANCg0K4oCjIFdoYXQgUiBpcyBkb2luZyAoY29uY2VwdHVhbGx5KSB0aG91Z2ggaXMgcnVubmluZyBhIGZvciBsb29wLiBMZXQncyB3cml0ZSBpdCBvdXQgZXhwbGljaXRseQ0KDQpgYGB7cn0NCmZvciAoYWdlIGluIGFnZXMpIHByaW50KGFnZSAqIDEyKQ0KYGBgDQoNCuKAoyBgYWdlYCBpcyBhIHRlbXBvcmFyeSB2YXJpYWJsZSB0aGF0IHRha2VzIGVhY2ggZWxlbWVudCBpbiBgYWdlc2ANCg0K4oCjIFlvdSBjYW4gY2hvb3NlICoqYW55IG5hbWUqKiBmb3IgdGhpcyB2YXJpYWJsZQ0KDQpgYGB7cn0NCmZvciAocmFuZG9tX25hbWUgaW4gYWdlcykgcHJpbnQocmFuZG9tX25hbWUgKiAxMikgICMgcmFuZG9tX25hbWUNCmBgYA0KDQrigKMgSWYgdGhlIGxvb3AncyBjb250ZW50IGlzICoqbW9yZSB0aGFuIG9uZSBsaW5lKiosIHVzZSAqKmN1cmx5IGJyYWNrZXRzKiogYHt9YA0KDQpgYGB7cn0NCmZvciAoYWdlIGluIGFnZXMpIHsgICAjIG11bHRpcGxlIGxpbmUgbG9vcCANCiAgYWdlX21vbnRocyA8LSBhZ2UgKiAxMg0KICBwcmludChhZ2VfbW9udGhzKQ0KfSANCmBgYA0KDQrigKMgVGhlIGdlbmVyYWwgc3RydWN0dXJlIG9mIGFueSBgZm9yYCBsb29wOg0KDQohW10oaW1hZ2VzL2Zvcl9sb29wX3N5bnRheC5wbmcpe3dpZHRoPSI0MjAifQ0KDQo6OjogcHJhY3RpY2UNCg0KKihOT1RFOiBBbnN3ZXJzIGFyZSBhdCB0aGUgYm90dG9tIG9mIHRoZSBwYWdlLiBUcnkgdG8gYW5zd2VyIHRoZSBxdWVzdGlvbnMgeW91cnNlbGYgYmVmb3JlIGNoZWNraW5nLikqDQoNCiMjIyBIb3VycyB0byBNaW51dGVzIEJhc2ljIExvb3Agey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KVHJ5IGNvbnZlcnRpbmcgaG91cnMgdG8gbWludXRlcyB1c2luZyBhIGBmb3JgIGxvb3AuIFN0YXJ0IHdpdGggdGhpcyB2ZWN0b3Igb2YgaG91cnM6DQoNCmBgYHtyIGV2YWwgPSBGfQ0KaG91cnMgPC0gYygzLCA0LCA1KSAjIFZlY3RvciBvZiBob3Vycw0KIyBZb3VyIGNvZGUgaGVyZQ0KDQpmb3IgKGhvdXIgaW4gaG91cnMpIHsNCiAgbWludXRlcyA8LSBob3VyICogNjAgDQogIHByaW50KG1pbnV0ZXMpICMgY29udmVydCBob3VycyB0byBtaW51dGVzIGFuZCBwcmludChtaW51dGVzKQ0KfQ0KYGBgDQo6OjoNCg0K4oCjIFNpZGUtbm90ZTogTG9vcHMgY2FuIGJlIG5lc3RlZCB3aXRoaW4gZWFjaCBvdGhlci4gRm9yIGluc3RhbmNlOg0KDQpgYGB7cn0NCiMgZm9yIGkgaW4gMToyLCBmb3IgaiBpbiAxOjIsIHByaW50IGkgKiBqDQpmb3IgKGkgaW4gMToyKSB7DQogIGZvciAoaiBpbiAxOjIpIHsNCiAgICBwcmludChpICogaikNCiAgfQ0KfQ0KYGBgDQoNCuKAoyBUaGlzIGNyZWF0ZXMgYSBjb21iaW5hdGlvbiBvZiBgaWAgYW5kIGBqYCB2YWx1ZXMgYXMgc2hvd24gaW4gdGhpcyB0YWJsZToNCg0KfCBpICAgfCBqICAgfCBpIFwqIGogfA0KfC0tLS0tfC0tLS0tfC0tLS0tLS0tfA0KfCAxICAgfCAxICAgfCAxICAgICAgfA0KfCAxICAgfCAyICAgfCAyICAgICAgfA0KfCAyICAgfCAxICAgfCAyICAgICAgfA0KfCAyICAgfCAyICAgfCA0ICAgICAgfA0KDQrigKMgTmVzdGVkIGxvb3BzIGFyZSBsZXNzIGNvbW1vbiB0aG91Z2gsIGFuZCBvZnRlbiBoYXZlIG1vcmUgZWZmaWNpZW50IGFsdGVybmF0aXZlcy4NCg0KOjo6DQoNCiMgQXJlIGBmb3JgIExvb3BzIFVzZWZ1bCBpbiBSPw0KDQrigKMgUiBhbHJlYWR5IGhhcyAqdmVjdG9yaXplZCogb3BlcmF0aW9ucyENCg0K4oCjIEV4YW1wbGU6IEFnZSBjb252ZXJzaW9uIHdpdGhvdXQgYSBsb29wLiAoRG9lcyBub3Qgd29yayBieSBkZWZhdWx0IGluIG1vc3QgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzKQ0KDQpgYGB7cn0NCiMgYWdlcyAqIDEyDQphZ2VzICogMTINCg0KYGBgDQoNCuKAoyBNb3Jlb3ZlciwgdXN1YWxseSB3b3JraW5nIHdpdGggZGF0YSBmcmFtZXMgYW5kIGB0aWR5dmVyc2VgIG9wZXJhdGlvbnM6DQoNCmBgYHtyfQ0KYWdlc19kZiA8LSB0aWJibGUoYWdlID0gYWdlcykNCiMgbXV0YXRlIGFnZXNfZGYgdG8gYWRkIGFnZV9tb250aHMgY29sdW1uDQphZ2VzX2RmICU+JSANCiAgbXV0YXRlKGFnZV9tb250aHMgPSBhZ2UgKiAxMikNCmBgYA0KDQrigKMgTG9vcHMgd2lsbCBiZSBxdWl0ZSB1c2VmdWwgaW4gc3BlY2lmaWMgc2NlbmFyaW9zOg0KDQotICAgb3BlcmF0aW9ucyBvbiBtdWx0aXBsZSBkYXRhIGZyYW1lcw0KLSAgIHdvcmtpbmcgd2l0aCBub24tZGF0YWZyYW1lIG9iamVjdHMgKGUuZy4gcGxvdHMpDQoNCuKAoyBXZSdsbCBzZWUgdGhpcyBsYXRlciBpbiB0aGUgbGVzc29uLiBGb3Igbm93LCB3aWxsIGZvY3VzIG9uIHNpbXBsZSwgInRveSIgZXhhbXBsZXMuDQoNCuKAoyAqKkxvb3BzIHZzIGZ1bmN0aW9uIG1hcHBpbmc/KioNCg0K4oCjIE9mdGVuLCBsb29wIHRhc2tzIGNhbiBiZSByZXBsYWNlZCB3aXRoIGN1c3RvbSBmdW5jdGlvbnMgd2hpY2ggYXJlIHRoZW4gbWFwcGVkIGFjcm9zcyBhIHZlY3RvciBvciBkYXRhIGZyYW1lLg0KDQrigKMgQnV0IGxvb3BzIGFyZSBlYXN5IHRvIGxlYXJuLCB0aGluayBhYm91dCBhbmQgZGVidWcsIGV2ZW4gZm9yIGJlZ2lubmVycy4NCg0KIyMgTG9vcGluZyB3aXRoIGFuIEluZGV4DQoNCuKAoyBPZnRlbiB1c2VmdWwgdG8gbG9vcCB0aHJvdWdoIGEgdmVjdG9yIHVzaW5nIGFuIGluZGV4LCB3aGljaCBpcyBhIGNvdW50ZXIgZm9yIHRoZSBjdXJyZW50IGl0ZXJhdGlvbi4NCg0K4oCjIEV4YW1wbGU6IGNvbnZlcnRpbmcgYWdlcyBpbiB5ZWFycyB0byBtb250aHMgdXNpbmcgYW4gaW5kZXg6DQoNCmBgYHtyfQ0KIyBSZWNhbGw6DQphZ2VzIDwtIGMoNywgOCwgOSkgIyBWZWN0b3Igb2YgYWdlcw0KYGBgDQoNCuKAoyBDcmVhdGUgYSBzZXF1ZW5jZSBvZiBpbmRpY2VzIHRoZSBzYW1lIGxlbmd0aCBhcyB0aGUgYWdlcyB2ZWN0b3I6DQoNCmBgYHtyfQ0KMTpsZW5ndGgoYWdlcykgIyAxIHRvIHRoZSBsZW5ndGggb2YgYWdlcw0KaW5kaWNlcyA8LSAxOmxlbmd0aChhZ2VzKSAjIHRoZW4gYXNzaWduIHRvIG9iamVjdCBjYWxsZWQgaW5kaWNlcw0KYGBgDQoNCuKAoyBVc2UgdGhlIGluZGV4IGluIGEgYGZvcmAgbG9vcCB0byBjb252ZXJ0IGVhY2ggYWdlIHRvIG1vbnRoczoNCg0KYGBge3J9DQojIGZvciBpIGluIGluZGljZXMsIHByaW50IGFnZXNbaV0gKiAxMg0KZm9yIChpIGluIGluZGljZXMpIHByaW50KGFnZXNbaV0gKiAxMikNCmBgYA0KDQrigKMgVGhlIHZhcmlhYmxlIG5hbWUgaW4gdGhlIGxvb3AgKGUuZy4sIGBpYCwgYGpgLCBgaW5kZXhgKSBpcyBhcmJpdHJhcnkuDQoNCuKAoyBPZiBjb3Vyc2UsIGluZGV4LWJhc2VkIGxvb3BzIGNhbiBiZSB1c2VkIGRpcmVjdGx5IHdpdGhvdXQgYSBzZXBhcmF0ZSB2YXJpYWJsZToNCg0KYGBge3J9DQojIGZvciBpIGluIDE6bGVuZ3RoKGFnZXMpLCBwcmludCBhZ2VzW2ldICogMTINCmZvciAoaSBpbiAxOmxlbmd0aChhZ2VzKSkgcHJpbnQoYWdlc1tpXSAqIDEyKQ0KYGBgDQoNCuKAoyBTdWNoIGluZGV4ZWQgbG9vcHMgd2lsbCBiZSBoZWxwZnVsIGZvciB3b3JraW5nIHdpdGggbXVsdGlwbGUgdmVjdG9ycyBzaW11bHRhbmVvdXNseS4NCg0KOjo6IHByYWN0aWNlDQojIyMgSG91cnMgdG8gTWludXRlcyBJbmRleGVkIExvb3Agey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KUmV3cml0ZSB5b3VyIGxvb3AgZnJvbSBsYXN0IHF1ZXN0aW9uIHVzaW5nIGluZGljZXM6DQoNCmBgYHtyLCBldmFsID0gRn0NCmhvdXJzIDwtIGMoMywgNCwgNSkgIyBWZWN0b3Igb2YgaG91cnMNCg0KIyBZb3VyIGNvZGUgaGVyZQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoaG91cnMpKSB7DQogIHByaW50KGhvdXJzW2ldICogNjApDQp9DQpgYGANCjo6Og0KDQrigKMgU2lkZS1ub3RlOiBUaGUgZnVuY3Rpb24gYHNlcV9hbG9uZygpYCBpcyBhIHNob3J0Y3V0IGZvciBjcmVhdGluZyBhIHNlcXVlbmNlIG9mIGluZGljZXMuDQoNCuKAoyBFcXVpdmFsZW50IHRvIGAxOmxlbmd0aCgpYDoNCg0KYGBge3J9DQojIFRoZXNlIHR3byBhcmUgZXF1aXZhbGVudCB3YXlzIHRvIGdlbmVyYXRlIHNlcXVlbmNlIG9mIGluZGljZXMgZm9yIGBhZ2VzYA0KMTpsZW5ndGgoYWdlcykNCnNlcV9hbG9uZyhhZ2VzKQ0KYGBgDQoNCiMgTG9vcGluZyBvbiBNdWx0aXBsZSBWZWN0b3JzDQoNCuKAoyBMb29waW5nIHdpdGggaW5kaWNlcyBhbGxvd3MgdXMgdG8gd29yayB3aXRoIG11bHRpcGxlIHZlY3RvcnMgc2ltdWx0YW5lb3VzbHkuDQoNCmBgYHtyfQ0KIyBDb25zaWRlcjoNCmFnZXMgPC0gYyg3LCA4LCA5KSAjIGFnZXMgaW4geWVhcnMNCmhlaWdodHMgPC0gYygxMjAsIDEzMCwgMTQwKSAjIGhlaWdodHMgaW4gY20NCmBgYA0KDQrigKMgV2UgY2FuIGxvb3AgdGhyb3VnaCBib3RoIHVzaW5nIHRoZSBpbmRleCBtZXRob2Q6DQoNCmBgYHtyfQ0KIyBmb3IgaSBpbiAxOmxlbmd0aChhZ2VzKSwgcHJpbnQgYSBwYXN0ZWQgc3RyaW5nIHdpdGggYWdlIGFuZCBoZWlnaHQNCg0KZm9yKGkgaW4gMTpsZW5ndGgoYWdlcykpIHsNCiAgYWdlIDwtIGFnZXNbaV0NCiAgaGVpZ2h0IDwtIGhlaWdodHNbaV0NCiAgcHJpbnQocGFzdGUoIkFnZToiLCBhZ2UsICJIZWlnaHQ6IiwgaGVpZ2h0KSkNCn0NCmBgYA0KDQrigKMgSW4gZWFjaCBpdGVyYXRpb246DQoNCi0gICBgaWAgaXMgdGhlIGluZGV4Lg0KLSAgIFdlIGV4dHJhY3QgdGhlIGl0aCBlbGVtZW50IGZyb20gZWFjaCB2ZWN0b3INCi0gICBXZSBwYXN0ZSB0aGUgdHdvIHRvZ2V0aGVyDQotICAgV2UgcHJpbnQgdGhlIHJlc3VsdA0KDQpBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gc2tpcCB0aGUgdmFyaWFibGUgYXNzaWdubWVudCBhbmQgdXNlIHRoZSBpbmRpY2VzIGluIHRoZSBgcHJpbnQoKWAgc3RhdGVtZW50IGRpcmVjdGx5Og0KDQpgYGB7cn0NCmZvcihpIGluIDE6bGVuZ3RoKGFnZXMpKSB7DQogIHByaW50KHBhc3RlKCJBZ2U6IiwgYWdlc1tpXSwgIkhlaWdodDoiLCBoZWlnaHRzW2ldKSkNCn0NCmBgYA0KDQo6OjogcHJhY3RpY2UNCiMjIyBCTUkgQ2FsY3VsYXRpb24gTG9vcCB7LnVubGlzdGVkIC51bm51bWJlcmVkfQ0KDQpVc2luZyBhIGZvciBsb29wLCBjYWxjdWxhdGUgdGhlIEJvZHkgTWFzcyBJbmRleCAoQk1JKSBvZiB0aGUgdGhyZWUgaW5kaXZpZHVhbHMgc2hvd24gYmVsb3cuIFRoZSBmb3JtdWwgZm9yIEJNSSBpcyBgQk1JID0gd2VpZ2h0IC8gKGhlaWdodCBeIDIpYC4NCg0KYGBge3IgZXZhbCA9IEZ9DQp3ZWlnaHRzIDwtIGMoMzAsIDMyLCAzNSkgIyBXZWlnaHRzIGluIGtnDQpoZWlnaHRzIDwtIGMoMS4yLCAxLjMsIDEuNCkgIyBIZWlnaHRzIGluIG1ldGVycw0KDQpmb3IoaSBpbiAxOmxlbmd0aCh3ZWlnaHRzKSkgew0KICB3ZWlnaHQgPC0gd2VpZ2h0c1tpXQ0KICBoZWlnaHQgPC0gaGVpZ2h0c1tpXQ0KICBibWkgPC0gd2VpZ2h0LyhoZWlnaHReMikNCiAgcHJpbnQocGFzdGUoIldlaWdodDoiLCB3ZWlnaHQsDQogICAgICAgICAgICAgICJIZWlnaHQ6IiwgaGVpZ2h0LA0KICAgICAgICAgICAgICAiQk1JOiIsIGJtaQ0KICApKQ0KICB9DQoNCmBgYA0KDQo6OjoNCg0KIyMgU3RvcmluZyBMb29wIFJlc3VsdHMNCg0K4oCjIFVzdWFsbHkgd2Ugd2FudCB0byBzdG9yZSBsb29wIHJlc3VsdHMgcmF0aGVyIHRoYW4ganVzdCBwcmludGluZy4NCg0K4oCjIEV4YW1wbGU6IGNvbnZlcnRpbmcgYWdlcyB0byBtb250aHMuDQoNCmBgYHtyfQ0KIyBSZWNhbGwgdGhpcyBsb29wOg0KYWdlcyA8LSBjKDcsIDgsIDkpICMgVmVjdG9yIG9mIGFnZXMNCmZvcihpIGluIDE6bGVuZ3RoKGFnZXMpKSB7DQogIHByaW50KGFnZXNbaV0gKiAxMikNCn0NCmBgYA0KDQrigKMgTGV0J3MgdHJ5IHRvIHN0b3JlIHRoaXMgaW5mbyBpbiBhIHZlY3Rvci4NCg0K4oCjIENyZWF0ZSBhbiBlbXB0eSB2ZWN0b3IgdG8gc3RvcmUgcmVzdWx0cy4NCg0KYGBge3J9DQphZ2VzX21vbnRocyA8LSB2ZWN0b3IobW9kZSA9ICJudW1lcmljIiwgbGVuZ3RoID0gbGVuZ3RoKGFnZXMpKSAjIHZlY3RvciBvZiBtb2RlICJudW1lcmljIiBhbmQgbGVuZ3RoIDMNCg0KYWdlc19tb250aHMNCmBgYA0KDQrigKMgSG93IGRvIHdlIHN0b3JlIHZhbHVlcyBpbiB0aGlzIHZlY3Rvcj8NCg0KYGBge3J9DQphZ2VzX21vbnRoc1sxXSA8LSA5OSAgICAgICAgICAgICAgICAgICAgICMgU3RvcmUgOTkgaW4gdGhlIGZpcnN0IGVsZW1lbnQgb2YgYWdlc19tb250aHMNCmFnZXNfbW9udGhzWzJdIDwtIDEwMCAgICAgICAgICAgICAgICAgICAgICAjIFN0b3JlIDEwMCBpbiB0aGUgc2Vjb25kIGVsZW1lbnQgb2YgYWdlc19tb250aHMNCg0KYWdlc19tb250aHMNCmBgYA0KDQrigKMgTm93LCBsZXQncyBleGVjdXRlIHRoZSBsb29wLCBzdG9yaW5nIHRoZSByZXN1bHRzIGluIGBhZ2VzX21vbnRoc2A6DQoNCmBgYHtyfQ0KIGFnZXNfbW9udGhzIDwtICB2ZWN0b3IoIm51bWVyaWMiLCBsZW5ndGgoYWdlcykpICAgICAgICAgICAgICMgQ3JlYXRlIGBhZ2VfbW9udGhzYCB2ZWN0b3Igb2YgbW9kZSAibnVtZXJpYyIgYW5kIGxlbmd0aCBvZiBhZ2VzDQoNCmZvciAoaSBpbiAxOmxlbmd0aChhZ2VzKSl7DQogIGFnZXNfbW9udGhzW2ldIDwtIGFnZXNbaV0gKiAxMg0KfSAgICAgICAgICAgICAgIyBmb3IgaSBpbiAxOmxlbmd0aChhZ2VzKQ0KICAgICAgICAgICAgICAjIHN0b3JlIGFnZXNbaV0gKiAxMiBpbiBhZ2VzX21vbnRoc1tpXQ0KYWdlc19tb250aHMNCmBgYA0KDQo6OjogcHJhY3RpY2UNCiMjIyBIZWlnaHQgY20gdG8gbSB7LnVubGlzdGVkIC51bm51bWJlcmVkfQ0KDQpVc2UgYSBmb3IgbG9vcCB0byBjb252ZXJ0IGhlaWdodCBtZWFzdXJlbWVudHMgZnJvbSBjbSB0byBtLiBTdG9yZSB0aGUgcmVzdWx0cyBpbiBhIHZlY3RvciBjYWxsZWQgYGhlaWdodF9tZXRlcnNgLg0KDQpgYGB7ciBldmFsID0gRn0NCmhlaWdodF9jbSA8LSBjKDE4MCwgMTcwLCAxOTAsIDE2MCwgMTUwKSAjIEhlaWdodHMgaW4gY20gDQoNCmhlaWdodF9tIDwtIHZlY3RvcigibnVtZXJpYyIsIGxlbmd0aChoZWlnaHRfY20pKSAjIG51bWVyaWMgdmVjdG9yIG9mIHNhbWUgbGVuZ3RoIGFzIGhlaWdodF9jbQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoaGVpZ2h0X2NtKSkgew0KICBoZWlnaHRfbVtpXSA8LSBoZWlnaHRfY21baV0vMTAwDQp9DQoNCmhlaWdodF9tDQpgYGANCjo6Og0KDQrigKMgV2F0Y2ggb3V0ISBDcmVhdGUgeW91ciBlbXB0eSBvYmplY3QgKipvdXRzaWRlKiogdGhlIGxvb3AgdG8gc2F2ZSBhbGwgaXRlcmF0aW9uIHJlc3VsdHMuDQoNCmBgYHtyfQ0KIyBDb25zaWRlciB0aGlzOg0KYWdlcyA8LSBjKDcsIDgsIDkpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChhZ2VzKSkgew0KICBhZ2VzX21vbnRocyA8LSB2ZWN0b3IoIm51bWVyaWMiLCBsZW5ndGgoYWdlcykpDQogIGFnZXNfbW9udGhzW2ldIDwtIGFnZXNbaV0gKiAxMg0KfQ0KYWdlc19tb250aHMgDQpgYGANCg0K4oCjIERvIHlvdSBzZWUgdGhlIHByb2JsZW0/DQoNCuKAoyBTaWRlIE5vdGUuIEluIGEgcnVzaD8gSW5pdGlhbGl6ZSB5b3VyIHZlY3RvciB3aXRoIGBjKClgIGFuZCBhcHBlbmQgdmFsdWVzOg0KDQpgYGB7cn0NCmFnZXMgPC0gYyg3LCA4LCA5KQ0KYWdlc19tb250aHMgPC0gYygpICMgcXVpY2sgYW5kIGRpcnR5IHdheSB0byBpbml0aWFsaXplIHZlY3Rvcg0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoYWdlcykpIHsNCiAgYWdlc19tb250aHNbaV0gPC0gYWdlc1tpXSAqIDEyDQp9DQphZ2VzX21vbnRocw0KYGBgDQoNCuKAoyBBbmQsIHlvdSBjYW4gYXBwZW5kIHZhbHVlcyBhdCB0aGUgZW5kIG9mIHRoZSB2ZWN0b3IgdXNpbmcgYGMoKWA6DQoNCmBgYHtyfQ0KIyBSZWRvIGxvb3AsIGJ1dCBhcHBlbmQgdmFsdWVzIHRvIGVuZCBvZiB2ZWN0b3Igd2l0aCBjKCkNCmFnZXMgPC0gYyg3LCA4LCA5KQ0KYWdlc19tb250aHMgPC0gYygpICMgcXVpY2sgYW5kIGRpcnR5IHdheSB0byBpbml0aWFsaXplIHZlY3Rvcg0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoYWdlcykpIHsNCiAgYWdlc19tb250aHMgPC0gYyhhZ2VzX21vbnRocywgYWdlc1tpXSoxMikNCn0NCmFnZXNfbW9udGhzDQoNCm51bWJlciA8LSA5DQpudW1iZXINCm51bWJlciA8LSBjKG51bWJlciwgMTApDQpudW1iZXINCmBgYA0KDQrigKMgRGlzY291cmFnZWQgYmVjYXVzZSBSIGRvZXMgbm90IGtub3cgdGhlIGZpbmFsIGxlbmd0aCBvZiB0aGUgdmVjdG9yLCBzbyByZWFsbG9jYXRlcyBtZW1vcnkgZWFjaCB0aW1lIHlvdSBhcHBlbmQgYSB2YWx1ZS4NCg0K4oCjIFNsb3cgcGVyZm9ybWFuY2Ugd2l0aCBsYXJnZSB2ZWN0b3JzLiBCdXQgZm9yIHF1aWNrIGFuYWx5c2lzLCBpdCdzIGZpbmUuDQoNCiMgSWYgU3RhdGVtZW50cyBpbiBMb29wcw0KDQrigKMgYElmYCBzdGF0ZW1lbnRzIGNhbiBiZSBpbnRlZ3JhdGVkIGludG8gbG9vcHMgaW4gUi4NCg0K4oCjIEV4YW1wbGU6IENsYXNzaWZ5aW5nIGFnZXMgYXMgIkNoaWxkIiBpZiB1bmRlciAxOC4NCg0KYGBge3J9DQphZ2VfdmVjIDwtIGMoMiwgMTIsIDE3LCAyNCwgNjApICMgVmVjdG9yIG9mIGFnZXMNCg0KIyBmb3IgYWdlIGluIGFnZXMsIGlmIGFnZSA8IDE4LCBwcmludCAiQ2hpbGQiDQpmb3IgKGFnZSBpbiBhZ2VfdmVjKXsNCiAgaWYgKGFnZSA8IDE4KSB7DQogICAgcHJpbnQoIkNoaWxkIikNCiAgfQ0KfQ0KYGBgDQoNCuKAoyBVc2UgY3VybHkgYnJhY2VzIGZvciBjbGFyaXR5IGFuZCBhZGRpbmcgbW9yZSBjb2RlLg0KDQpgYGB7cn0NCmFnZV92ZWMgPC0gYygyLCAxMiwgMTcsIDI0LCA2MCkgIyBWZWN0b3Igb2YgYWdlcw0KDQojIGZvciBhZ2UgaW4gYWdlcywgaWYgYWdlIDwgMTgsIHByaW50ICJDaGlsZCINCmZvciAoYWdlIGluIGFnZV92ZWMpew0KICBpZiAoYWdlIDwgMTgpIHsNCiAgICBwcmludCgiUHJvY2Vzc2luZzoiKSAjIHB1dCB0aGVzZSBpbiBsb29wOg0KICAgIHByaW50KHBhc3RlKCJDaGlsZCwgQWdlIiwgYWdlICkpDQogIH0NCn0NCmBgYA0KDQrigKMgTGV0J3MgYWRkIGFub3RoZXIgY29uZGl0aW9uIHdpdGggYGVsc2UgaWZgIGFzICdDaGlsZCcgb3IgJ1RlZW4nIGJhc2VkIG9uIGFnZS4NCg0KYGBge3J9DQojIEFkZCBlbHNlIGlmIGFnZSA+PSAxMyAmJiBhZ2UgPCAxOCwgcHJpbnQgIlRlZW4iDQpmb3IgKGFnZSBpbiBhZ2VfdmVjKSB7DQogIGlmIChhZ2UgPCAxMykgew0KICAgIHByaW50KHBhc3RlKCJDaGlsZCwgQWdlIiwgYWdlKSkNCiAgfSBlbHNlIGlmIChhZ2UgPj0gMTMgJiYgYWdlIDwgMTgpew0KICAgICAgcHJpbnQocGFzdGUoIlRlZW4sIEFnZSIsIGFnZSkpDQogICAgfQ0KfQ0KYGBgDQoNCuKAoyBGaW5hbCBgZWxzZWAgc3RhdGVtZW50IGZvciBvdGhlciBhZ2VzLCBjbGFzc2lmeWluZyBhcyAnQWR1bHQnLg0KDQpgYGB7cn0NCiMgQWRkIGZpbmFsIGVsc2Ugc3RhdGVtZW50IGZvciAiQWR1bHQiDQpmb3IgKGFnZSBpbiBhZ2VfdmVjKSB7DQogIGlmIChhZ2UgPCAxMykgew0KICAgIHByaW50KHBhc3RlKCJDaGlsZCwgQWdlIiwgYWdlKSkNCiAgfSBlbHNlIGlmIChhZ2UgPj0gMTMgJiYgYWdlIDwgMTgpew0KICAgICAgcHJpbnQocGFzdGUoIlRlZW4sIEFnZSIsIGFnZSkpDQogIH0gZWxzZSB7DQogICAgICBwcmludChwYXN0ZSgiQWR1bHQsIEFnZSIsIGFnZSkpDQogICAgfQ0KfSANCmBgYA0KDQrigKMgV2UgY2FuIHN0b3JlIHRoZXNlIGNsYXNzaWZpY2F0aW9ucyBpbiBhIHZlY3RvciB1c2luZyBpbmRleC1iYXNlZCBsb29wLg0KDQpgYGB7cn0NCmFnZV9jbGFzcyA8LSB2ZWN0b3IoImNoYXJhY3RlciIsIGxlbmd0aChhZ2VfdmVjKSkgICMgdmVjdG9yIG9mIG1vZGUgImNoYXJhY3RlciIgYW5kIGxlbmd0aCBvZiBhZ2VfdmVjDQogIA0KIyBSZWZhY3RvciBsb29wIGZyb20gYWJvdmUgdG8gc3RvcmUgY2xhc3NpZmljYXRpb25zIGluIGFnZV9jbGFzcw0KZm9yIChpIGluIDE6bGVuZ3RoKGFnZV92ZWMpKSB7DQogIGlmIChhZ2VfdmVjW2ldIDwgMTMpIHsNCiAgICBhZ2VfY2xhc3NbaV0gPC0gcGFzdGUoIkNoaWxkLCBBZ2UiLCBhZ2VfdmVjW2ldKQ0KICB9IGVsc2UgaWYgKGFnZV92ZWNbaV0gPj0gMTMgJiYgYWdlX3ZlY1tpXSA8IDE4KXsNCiAgICAgYWdlX2NsYXNzW2ldIDwtIHBhc3RlKCJUZWVuLCBBZ2UiLCBhZ2VfdmVjW2ldKQ0KICB9IGVsc2Ugew0KICAgICAgYWdlX2NsYXNzW2ldIDwtIHBhc3RlKCJBZHVsdCwgQWdlIiwgYWdlX3ZlY1tpXSkNCiAgICB9DQp9DQoNCmFnZV9jbGFzcw0KYGBgDQoNCg0KOjo6IHByYWN0aWNlDQojIyMgVGVtcGVyYXR1cmUgQ2xhc3NpZmljYXRpb24gey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KWW91IGhhdmUgYSB2ZWN0b3Igb2YgYm9keSB0ZW1wZXJhdHVyZXMgaW4gQ2Vsc2l1cy4gQ2xhc3NpZnkgZWFjaCB0ZW1wZXJhdHVyZSBhcyAnSHlwb3RoZXJtaWEnLCAnTm9ybWFsJywgb3IgJ0ZldmVyJyB1c2luZyBhIGBmb3JgIGxvb3AgY29tYmluZWQgd2l0aCBgaWZgIGFuZCBgZWxzZWAgc3RhdGVtZW50cy4NCg0KVXNlIHRoZXNlIHJ1bGVzOg0KDQotICAgQmVsb3cgMzYuMMKwQzogJ0h5cG90aGVybWlhJw0KLSAgIEJldHdlZW4gMzYuMMKwQyBhbmQgMzcuNcKwQzogJ05vcm1hbCcNCi0gICBBYm92ZSAzNy41wrBDOiAnRmV2ZXInDQoNCmBgYHtyIGV2YWwgPSBGfQ0KYm9keV90ZW1wcyA8LSBjKDM1LCAzNi41LCAzNywgMzgsIDM5LjUpICMgQm9keSB0ZW1wZXJhdHVyZXMgaW4gQ2Vsc2l1cw0KY2xhc3NpZl92ZWMgPC0gdmVjdG9yKCJjaGFyYWN0ZXIiLCBsZW5ndGgoYm9keV90ZW1wcykpICMgY2hhcmFjdGVyIHZlYywgbGVuZ3RoIG9mIGJvZHlfdGVtcHMNCg0KZm9yIChpIGluIDE6bGVuZ3RoKGJvZHlfdGVtcHMpKSB7DQogICAgIyBBZGQgeW91ciBpZi1lbHNlIGxvZ2ljIGhlcmUNCiAgICBpZiAoYm9keV90ZW1wc1tpXSA8IDM2LjApIHsNCiAgICAgICAgY2xhc3NpZl92ZWNbaV0gPC0gcGFzdGUoYm9keV90ZW1wc1tpXSwgIsKwQyBpcyBIeXBvdGhlcm1pYSIpDQogICAgfSANCiAgZWxzZSBpZiAoYm9keV90ZW1wc1tpXSA+PSAzNi41ICYmIGJvZHlfdGVtcHNbaV0gPD0gMzcuNSApIHsNCiAgICBjbGFzc2lmX3ZlY1tpXSA8LSBwYXN0ZShib2R5X3RlbXBzW2ldLCAiwrBDIGlzIE5vcm1hbCIpDQogIH0NCiAgZWxzZSB7DQogICAgY2xhc3NpZl92ZWNbaV0gPC0gcGFzdGUoYm9keV90ZW1wc1tpXSwgIsKwQyBpcyBGZXZlciIpDQogIH0NCiAgICAjIyBhZGQgb3RoZXIgY29uZGl0aW9ucyBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBjbGFzc2lmaWNhdGlvbnMNCn0NCg0KY2xhc3NpZl92ZWMNCmBgYA0KDQpBbiBleHBlY3RlZCBvdXRwdXQgaXMgYmVsb3cNCg0KYGBgICAgICAgICAgDQozNcKwQyBpcyBIeXBvdGhlcm1pYQ0KMzYuNcKwQyBpcyBOb3JtYWwNCjM3wrBDIGlzIE5vcm1hbA0KMzjCsEMgaXMgRmV2ZXINCjM5LjXCsEMgaXMgRmV2ZXINCmBgYA0KOjo6DQoNCiMgUmVhbCBMb29wcyBBcHBsaWNhdGlvbjogR2VuZXJhdGluZyBNdWx0aXBsZSBQbG90cw0KDQrigKMgVXNpbmcgbG9vcHMgdG8gKipnZW5lcmF0ZSBtdWx0aXBsZSBwbG90cyoqIGZvciBkaWZmZXJlbnQgZ3JvdXBzIHdpdGhpbiBhIGRhdGFzZXQuDQoNCuKAoyBFeGFtcGxlIHdpdGggdGhlIGBzdHJlcF90YmAgZGF0YXNldCBmcm9tIGBtZWRpY2FsZGF0YWAgcGFja2FnZS4NCg0K4oCjIEFpbTogY3JlYXRlICoqY2F0ZWdvcnkgaW5zcGVjdGlvbiBwbG90cyoqIGZvciBlYWNoICoqcmFkaW9sb2dpYyA2LW1vbnRoIGltcHJvdmVtZW50IGdyb3VwKiogdXNpbmcgYGluc3BlY3RkZjo6aW5zcGVjdF9jYXQoKWAuDQoNCuKAoyBGaXJzdCwgY3JlYXRlIGEgKipzaW5nbGUgcGxvdCoqIGZvciB0aGUgKipmaXJzdCByYWRpb2xvZ2ljIGltcHJvdmVtZW50IGdyb3VwKiouDQoNCmBgYHtyfQ0KY2F0X3Bsb3QgPC0gDQogIG1lZGljYWxkYXRhOjpzdHJlcF90YiAlPiUNCiAgZmlsdGVyKHJhZGlvbG9naWNfNm0gPT0gIjZfY29uc2lkZXJhYmxlX2ltcHJvdmVtZW50IikgJT4lIA0KICBpbnNwZWN0ZGY6Omluc3BlY3RfY2F0KCkgJT4lIA0KICBpbnNwZWN0ZGY6OnNob3dfcGxvdCgpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZpbHRlciByYWRpb2xvZ2ljXzZtIHRvICI2X0NvbnNpZGVyYWJsZV9pbXByb3ZlbWVudCINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5zcGVjdF9jYXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2hvd19wbG90DQpgYGANCg0K4oCjIFdhbnQgdG8gY3JlYXRlIHNpbWlsYXIgcGxvdHMgZm9yICoqZWFjaCByYWRpb2xvZ2ljIGltcHJvdmVtZW50IGdyb3VwKiouDQoNCuKAoyBJZGVudGlmeSBhbGwgdW5pcXVlIHZhbHVlcyBvZiBgbWVkaWNhbGRhdGE6OnN0cmVwX3RiJHJhZGlvbG9naWNfNm1gDQoNCmBgYHtyfQ0KcmFkaW9sb2dpY19sZXZlbHNfNm0gPC0gdW5pcXVlKG1lZGljYWxkYXRhOjpzdHJlcF90YiRyYWRpb2xvZ2ljXzZtKQ0KYGBgDQoNCuKAoyBJbml0aWF0ZSBhbiAqKmVtcHR5IGxpc3Qgb2JqZWN0KiogdG8gc3RvcmUgdGhlIHBsb3RzLg0KDQpgYGB7cn0NCmNhdF9wbG90X2xpc3QgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKHJhZGlvbG9naWNfbGV2ZWxzXzZtKSkNCmBgYA0KDQrigKMgT3B0aW9uYWxseSwgc2V0IG5hbWVzIG9mIGxpc3QgZWxlbWVudHMgdG8gdGhlIHJhZGlvbG9naWMgaW1wcm92ZW1lbnQgZ3JvdXBzLg0KDQpgYGB7cn0NCm5hbWVzKGNhdF9wbG90X2xpc3QpIDwtIHJhZGlvbG9naWNfbGV2ZWxzXzZtDQoNCmNhdF9wbG90X2xpc3QNCmBgYA0KDQrigKMgTGV0J3MgcHV0IGl0IHRvZ2V0aGVyDQoNCmBgYHtyfQ0KY2F0X3Bsb3RfbGlzdCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGgocmFkaW9sb2dpY19sZXZlbHNfNm0pKQ0KDQpmb3IgKGxldmVsIGluIHJhZGlvbG9naWNfbGV2ZWxzXzZtKSB7DQogIA0KICAjIEdlbmVyYXRlIHBsb3QgZm9yIGVhY2ggbGV2ZWwNCiBjYXRfcGxvdCA8LSANCiAgICBtZWRpY2FsZGF0YTo6c3RyZXBfdGIgJT4lIA0KICAgIGZpbHRlcihyYWRpb2xvZ2ljXzZtID09IGxldmVsKSAlPiUgDQogICAgaW5zcGVjdGRmOjppbnNwZWN0X2NhdCgpICU+JQ0KICAgIGluc3BlY3RkZjo6c2hvd19wbG90KCkNCiAgDQogICMgQXBwZW5kIHRvIHRoZSBsaXN0DQogIGNhdF9wbG90X2xpc3RbW2xldmVsXV0gPC0gY2F0X3Bsb3QNCn0NCmBgYA0KDQrigKMgQWNjZXNzIGEgc3BlY2lmaWMgcGxvdCB1c2luZyAqKmRvdWJsZSBicmFja2V0IHN5bnRheCoqIG9yIGJ5IG51bWJlci4NCg0KYGBge3J9DQpjYXRfcGxvdF9saXN0W1siNl9Db25zaWRlcmFibGVfaW1wcm92ZW1lbnQiXV0gIyAiNl9Db25zaWRlcmFibGVfaW1wcm92ZW1lbnQiDQoNCmNhdF9wbG90X2xpc3RbWyI1X01vZGVyYXRlX2ltcHJvdmVtZW50Il1dDQpgYGANCg0KYGBge3J9DQpjYXRfcGxvdF9saXN0W1sxXV0NCmBgYA0KDQrigKMgVG8gZGlzcGxheSBhbGwgcGxvdHMgYXQgb25jZSwgY2FsbCB0aGUgZW50aXJlIGxpc3QuDQoNCmBgYHtyIGZpZy5oZWlnaHQgPSAyLCBmaWcuc2hvdz0naG9sZCcsIG1lc3NhZ2U9Rn0NCmNhdF9wbG90X2xpc3QNCmBgYA0KDQo6OjogcHJhY3RpY2UgDQojIyMgVmlzdWFsaXppbmcgVEIgQ2FzZXMgey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgdXNlIFdITyBkYXRhIGZyb20gdGhlIGB0aWR5cmAgcGFja2FnZSB0byBjcmVhdGUgbGluZSBncmFwaHMgc2hvd2luZyB0aGUgbnVtYmVyIG9mIG5ldyBUQiBjYXNlcyBpbiBjaGlsZHJlbiBvdmVyIHRoZSB5ZWFycyBpbiBTb3V0aCBBbWVyaWNhbiBjb3VudHJpZXMuDQoNCkZpcnN0LCB3ZSdsbCBwcmVwYXJlIHRoZSBkYXRhOg0KDQpgYGB7cn0NCnRiX2NoaWxkX2Nhc2VzIDwtIHRpZHlyOjp3aG8yICU+JSANCiAgdHJhbnNtdXRlKGNvdW50cnksIHllYXIsIA0KICAgICAgICAgICAgdGJfY2FzZXNfY2hpbGRyZW4gPSBzcF9tXzAxNCArIHNwX2ZfMDE0ICsgc25fbV8wMTQgKyBzbl9mXzAxNCkgJT4lIA0KICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIkJyYXppbCIsICJDb2xvbWJpYSIsICJBcmdlbnRpbmEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICJVcnVndWF5IiwgIkNoaWxlIiwgIkd1eWFuYSIpKSAgJT4lIA0KICBmaWx0ZXIoeWVhciA+PSAyMDA2KQ0KDQp0Yl9jaGlsZF9jYXNlcw0KYGBgDQoNCk5vdywgZmlsbCBpbiB0aGUgYmxhbmtzIGluIHRoZSB0ZW1wbGF0ZSBiZWxvdyB0byBjcmVhdGUgYSBsaW5lIGdyYXBoIGZvciBlYWNoIGNvdW50cnkgdXNpbmcgYSBgZm9yYCBsb29wOg0KDQpgYGB7cn0NCiMgR2V0IGxpc3Qgb2YgY291bnRyaWVzLiBIaW50OiBVc2UgdW5pcXVlKCkgb24gdGhlIGNvdW50cnkgY29sdW1uDQpjb3VudHJpZXMgPC0gdW5pcXVlKHRiX2NoaWxkX2Nhc2VzJGNvdW50cnkpDQojIENyZWF0ZSBsaXN0IHRvIHN0b3JlIHBsb3RzLiBIaW50OiBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QNCnRiX2NoaWxkX2Nhc2VzX3Bsb3RzIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChjb3VudHJpZXMpKQ0KDQpuYW1lcyh0Yl9jaGlsZF9jYXNlc19wbG90cykgPC0gY291bnRyaWVzICMgU2V0IG5hbWVzIG9mIGxpc3QgZWxlbWVudHMNCg0KIyBMb29wIHRocm91Z2ggY291bnRyaWVzDQpmb3IgKG5hbWUgaW4gY291bnRyaWVzKSB7IA0KICANCiAgIyBGaWx0ZXIgZGF0YSBmb3IgZWFjaCBjb3VudHJ5DQogIHRiX2NoaWxkX2Nhc2VzX2ZpbHRlcmVkIDwtICBmaWx0ZXIodGJfY2hpbGRfY2FzZXMsIGNvdW50cnkgPT0gbmFtZSkNCiAgDQogICMgTWFrZSBwbG90DQogIHRiX2NoaWxkX2Nhc2VzX3Bsb3QgPC0gZ2dwbG90KHRiX2NoaWxkX2Nhc2VzX2ZpbHRlcmVkLCBhZXMoeCA9IHllYXIsIHkgPSB0Yl9jYXNlc19jaGlsZHJlbikpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZ2d0aXRsZShwYXN0ZSgiVEIgQ2FzZXMgaW4gQ2hpbGRyZW4gLSIsIG5hbWUpKSANCiAgICANCiAgDQogICMgQXBwZW5kIHRvIGxpc3QuIEhpbnQ6IFVzZSBkb3VibGUgYnJhY2tldHMNCiAgdGJfY2hpbGRfY2FzZXNfcGxvdHNbW25hbWVdXSA8LSB0Yl9jaGlsZF9jYXNlc19wbG90IA0KfQ0KDQp0Yl9jaGlsZF9jYXNlc19wbG90cw0KYGBgDQo6OjoNCg0KIyBXcmFwIFVwIQ0KDQpJbiB0aGlzIGxlc3Nvbiwgd2UgZGVsdmVkIGludG8gZm9yIGxvb3BzIGluIFIsIGRlbW9uc3RyYXRpbmcgdGhlaXIgdXRpbGl0eSBmcm9tIGJhc2ljIHRhc2tzIHRvIGNvbXBsZXggZGF0YSBhbmFseXNpcyBpbnZvbHZpbmcgbXVsdGlwbGUgZGF0YXNldHMgYW5kIHBsb3QgZ2VuZXJhdGlvbi4gRGVzcGl0ZSBSJ3MgcHJlZmVyZW5jZSBmb3IgdmVjdG9yaXplZCBvcGVyYXRpb25zLCBmb3IgbG9vcHMgYXJlIGluZGlzcGVuc2FibGUgaW4gY2VydGFpbiBzY2VuYXJpb3MuIEhvcGVmdWxseSwgdGhpcyBsZXNzb24gaGFzIGVxdWlwcGVkIHlvdSB3aXRoIHRoZSBza2lsbHMgdG8gY29uZmlkZW50bHkgaW1wbGVtZW50IGZvciBsb29wcyBpbiB2YXJpb3VzIGRhdGEgcHJvY2Vzc2luZyBjb250ZXh0cy4NCg0KIyBBbnN3ZXIgS2V5DQoNCiMjIyBIb3VycyB0byBNaW51dGVzIEJhc2ljIExvb3ANCg0KYGBge3J9DQpob3VycyA8LSBjKDMsIDQsIDUpICMgVmVjdG9yIG9mIGhvdXJzDQoNCmZvciAoaG91ciBpbiBob3Vycykgew0KICBtaW51dGVzIDwtIGhvdXIgKiA2MA0KICBwcmludChtaW51dGVzKQ0KfQ0KYGBgDQoNCiMjIyBIb3VycyB0byBNaW51dGVzIEluZGV4ZWQgTG9vcA0KDQpgYGB7cn0NCmhvdXJzIDwtIGMoMywgNCwgNSkgIyBWZWN0b3Igb2YgaG91cnMNCg0KZm9yIChpIGluIDE6bGVuZ3RoKGhvdXJzKSkgew0KICBtaW51dGVzIDwtIGhvdXJzW2ldICogNjANCiAgcHJpbnQobWludXRlcykNCn0NCmBgYA0KDQojIyMgQk1JIENhbGN1bGF0aW9uIExvb3ANCg0KYGBge3J9DQp3ZWlnaHRzIDwtIGMoMzAsIDMyLCAzNSkgIyBXZWlnaHRzIGluIGtnDQpoZWlnaHRzIDwtIGMoMS4yLCAxLjMsIDEuNCkgIyBIZWlnaHRzIGluIG1ldGVycw0KDQpmb3IoaSBpbiAxOmxlbmd0aCh3ZWlnaHRzKSkgew0KICBibWkgPC0gd2VpZ2h0c1tpXSAvIChoZWlnaHRzW2ldIF4gMikNCiAgDQogIHByaW50KHBhc3RlKCJXZWlnaHQ6Iiwgd2VpZ2h0c1tpXSwNCiAgICAgICAgICAgICAgIkhlaWdodDoiLCBoZWlnaHRzW2ldLA0KICAgICAgICAgICAgICAiQk1JOiIsIGJtaSkpDQp9DQpgYGANCg0KIyMjIEhlaWdodCBjbSB0byBtDQoNCmBgYHtyfQ0KaGVpZ2h0X2NtIDwtIGMoMTgwLCAxNzAsIDE5MCwgMTYwLCAxNTApICMgSGVpZ2h0cyBpbiBjbSANCg0KaGVpZ2h0X20gPC0gdmVjdG9yKCJudW1lcmljIiwgbGVuZ3RoID0gbGVuZ3RoKGhlaWdodF9jbSkpIA0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoaGVpZ2h0X2NtKSkgew0KICBoZWlnaHRfbVtpXSA8LSBoZWlnaHRfY21baV0gLyAxMDANCn0NCmhlaWdodF9tDQpgYGANCg0KIyMjIFRlbXBlcmF0dXJlIENsYXNzaWZpY2F0aW9uDQoNCmBgYHtyfQ0KYm9keV90ZW1wcyA8LSBjKDM1LCAzNi41LCAzNywgMzgsIDM5LjUpICMgQm9keSB0ZW1wZXJhdHVyZXMgaW4gQ2Vsc2l1cw0KY2xhc3NpZl92ZWMgPC0gdmVjdG9yKG1vZGUgPSAiY2hhcmFjdGVyIiwgbGVuZ3RoID0gbGVuZ3RoKGJvZHlfdGVtcHMpKQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoYm9keV90ZW1wcykpIHsNCiAgICBpZiAoYm9keV90ZW1wc1tpXSA8IDM2LjApIHsNCiAgICAgICAgY2xhc3NpZl92ZWNbaV0gPC0gcGFzdGUoYm9keV90ZW1wc1tpXSwgIsKwQyBpcyBIeXBvdGhlcm1pYSIpDQogICAgfSBlbHNlIGlmIChib2R5X3RlbXBzW2ldIDw9IDM3LjUpIHsNCiAgICAgICAgY2xhc3NpZl92ZWNbaV0gPC0gcGFzdGUoYm9keV90ZW1wc1tpXSwgIsKwQyBpcyBOb3JtYWwiKQ0KICAgIH0gZWxzZSB7DQogICAgICAgIGNsYXNzaWZfdmVjW2ldIDwtIHBhc3RlKGJvZHlfdGVtcHNbaV0sICLCsEMgaXMgRmV2ZXIiKQ0KICAgIH0NCn0NCg0KY2xhc3NpZl92ZWMNCmBgYA0KDQojIyMgVmlzdWFsaXppbmcgVEIgQ2FzZXMNCg0KYGBge3J9DQojIEFzc3VtaW5nIHRiX2NoaWxkX2Nhc2VzIGlzIGEgZGF0YWZyYW1lIHdpdGggdGhlIG5lY2Vzc2FyeSBjb2x1bW5zDQpjb3VudHJpZXMgPC0gdW5pcXVlKHRiX2NoaWxkX2Nhc2VzJGNvdW50cnkpDQoNCiMgQ3JlYXRlIGxpc3QgdG8gc3RvcmUgcGxvdHMNCnRiX2NoaWxkX2Nhc2VzX3Bsb3RzIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChjb3VudHJpZXMpKQ0KbmFtZXModGJfY2hpbGRfY2FzZXNfcGxvdHMpIDwtIGNvdW50cmllcw0KDQojIExvb3AgdGhyb3VnaCBjb3VudHJpZXMNCmZvciAoY291bnRyeW5hbWUgaW4gY291bnRyaWVzKSB7IA0KICANCiAgIyBGaWx0ZXIgZGF0YSBmb3IgZWFjaCBjb3VudHJ5DQogIHRiX2NoaWxkX2Nhc2VzX2ZpbHRlcmVkIDwtIGZpbHRlcih0Yl9jaGlsZF9jYXNlcywgY291bnRyeSA9PSBjb3VudHJ5bmFtZSkNCiAgDQogICMgTWFrZSBwbG90DQogIHRiX2NoaWxkX2Nhc2VzX3Bsb3QgPC0gZ2dwbG90KHRiX2NoaWxkX2Nhc2VzX2ZpbHRlcmVkLCBhZXMoeCA9IHllYXIsIHkgPSB0Yl9jYXNlc19jaGlsZHJlbikpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZ2d0aXRsZShwYXN0ZSgiVEIgQ2FzZXMgaW4gQ2hpbGRyZW4gLSIsIGNvdW50cnluYW1lKSkNCiAgDQogICMgQXBwZW5kIHRvIGxpc3QNCiAgdGJfY2hpbGRfY2FzZXNfcGxvdHNbW2NvdW50cnluYW1lXV0gPC0gdGJfY2hpbGRfY2FzZXNfcGxvdCANCn0NCg0KdGJfY2hpbGRfY2FzZXNfcGxvdHNbWyJVcnVndWF5Il1dDQpgYGANCg0KIyBDb250cmlidXRvcnMgey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KVGhlIGZvbGxvd2luZyB0ZWFtIG1lbWJlcnMgY29udHJpYnV0ZWQgdG8gdGhpcyBsZXNzb246DQoNCmByIHRnY19jb250cmlidXRvcnNfbGlzdChpZHMgPSBjKCJzYWJpbmEiLCAia2VuZGF2aWRuIikpYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBSZWZlcmVuY2VzIHsudW5udW1iZXJlZCAudW5saXN0ZWR9DQoNClNvbWUgbWF0ZXJpYWwgaW4gdGhpcyBsZXNzb24gd2FzIGFkYXB0ZWQgZnJvbSB0aGUgZm9sbG93aW5nIHNvdXJjZXM6DQoNCi0gICBCYXJuaWVyLCBKdWxpZW4uICJJbnRyb2R1Y3Rpb24gw6AgUiBldCBhdSB0aWR5dmVyc2UuIiA8aHR0cHM6Ly9qdWJhLmdpdGh1Yi5pby90aWR5dmVyc2U+DQoNCi0gICBXaWNraGFtLCBIYWRsZXk7IEdyb2xlbXVuZCwgR2FycmV0dC4gIlIgZm9yIERhdGEgU2NpZW5jZS4iIDxodHRwczovL3I0ZHMuaGFkLmNvLm56Lz4NCg0KLSAgIFdpY2toYW0sIEhhZGxleTsgR3JvbGVtdW5kLCBHYXJyZXR0LiAiUiBmb3IgRGF0YSBTY2llbmNlICgyZSkuIiA8aHR0cHM6Ly9yNGRzLmhhZGxleS5uei8+DQoNCg==