1 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!

2 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

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

2.2 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

ages * 12
## [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:

i j i * j
1 1 1
1 2 2
2 1 2
2 2 4

‣ Nested loops are less common though, and often have more efficient alternatives.

:::

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

# ages * 12
ages * 12
## [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.

3.1 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
seq_along(ages)
## [1] 1 2 3

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

4.1 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
number <- 9
number
## [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.

5 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

6 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"]]

cat_plot_list[[1]]
## NULL

‣ To display all plots at once, call the entire list.

cat_plot_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()`).

7 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.

8 Answer Key

8.0.1 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

8.0.2 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

8.0.3 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"

8.0.4 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

8.0.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"

8.0.6 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==