Problem definition

In this project we are trying to identify the various determinants contributing to the prevalence of obesity among adults in the United States, including social, economic, cultural, environmental, and behavioral factors

Abstract and Introduction

We will use the BRFSS (Behavioral Risk Factor Surveillance System) dataset. It is a cross-sectional telephone survey that state health departments conduct monthly over landline telephones and cellular telephones with a standardised questionnaire and technical and methodologic assistance from the CDC (Centers for Disease Control and Prevention). BRFSS is used to collect prevalence data among U.S. residents regarding their risk behaviours and preventive health practices that can affect their health status. We decided on the topic of the factors that influence obesity among adults in the U.S. Studying the factors influencing obesity among adults in the U.S. is crucial. Obesity is a significant public health issue in the U.S., contributing to various chronic diseases like heart disease, diabetes, and certain cancers. Understanding the factors contributing to obesity can inform public health policies and interventions aimed at prevention and management. Obesity-related healthcare costs are substantial, impacting both individuals and the healthcare system as a whole.

WHY IS IT IMPORTANT TO KNOW ABOUT OBESITY? By identifying the factors influencing obesity, healthcare providers can develop more effective strategies for prevention and treatment, potentially reducing healthcare expenditures. Obesity can have profound social and economic consequences, including reduced quality of life, discrimination, and decreased productivity. Exploring the factors behind obesity can help address underlying societal issues such as access to healthy foods, socioeconomic disparities, and cultural influences.

Researching the factors influencing obesity fosters innovation in healthcare and public health. It encourages the development of new technologies, interventions, and policies aimed at combating obesity and promoting healthier lifestyles. Overall, investigating the factors influencing obesity among adults in the U.S. is essential for improving public health outcomes, reducing healthcare costs, addressing social and economic disparities, promoting individual well-being, and driving innovation in healthcare and public health practices.This is a fundamental public health question and can provide insights into preventive strategies and policymaking.

How do we measure obesity in simple terms? Obesity is typically defined by the Body Mass Index (BMI) with a threshold value (e.g., BMI ≥ 30 as obese). Here’s a detailed breakdown of how you might approach analyzing this question using different statistical models, specifying potential variables and offering some analytical tips.

Uploading and preparing the data set

We will upload all the necessary libraries we will use in our analysis here.

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(readxl)
library(gplots)
## 
## Attaching package: 'gplots'
## The following object is masked from 'package:stats':
## 
##     lowess
library(ggplot2)
library(maps)
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode
library(caret)
## Loading required package: lattice
library(MASS)
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
library(boot)
## 
## Attaching package: 'boot'
## The following object is masked from 'package:lattice':
## 
##     melanoma
## The following object is masked from 'package:car':
## 
##     logit
library(geepack)
library(tidyr)
library(nnet)
library(mlogit)
## Loading required package: dfidx
## 
## Attaching package: 'dfidx'
## The following object is masked from 'package:MASS':
## 
##     select
## The following object is masked from 'package:stats':
## 
##     filter
library(RColorBrewer)
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
library(knitr)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
# Specify the path to your csv file
file_path <- "Demo+Health data.xlsx"

Loading the date

# Read the Excel file
data <- read_excel(file_path)
head(data)
## # A tibble: 6 × 15
##   `_state` genhlth poorhlth cvdcrhd4 diabete4 marital educa employ1 income3
##      <dbl>   <dbl>    <dbl>    <dbl>    <dbl>   <dbl> <dbl>   <dbl>   <dbl>
## 1        1       2       NA        2        1       1     6       7      99
## 2        1       1       NA        2        3       3     4       2       5
## 3        1       2        2        2        3       1     6       7      10
## 4        1       1       NA        2        3       1     4       7      77
## 5        1       4       88        2        3       1     5       5       5
## 6        1       5       88        2        1       1     4       7      99
## # ℹ 6 more variables: smokday2 <dbl>, `_totinda` <dbl>, `_sex` <dbl>,
## #   `_ageg5yr` <dbl>, `_bmi5` <dbl>, `_bmi5cat` <dbl>
dim(data)
## [1] 445132     15

We see that we have 445 132 observations, which is good to work on a big data project. So let us carry on. HOPE YOU WILL ENJOY OUR RIDE :) !

Data Cleaning

# Get the column names
column_names <- names(data)

# Get the data types of each column
data_types <- sapply(data, class)

# Get the number of non-null values in each column
non_null_counts <- sapply(data, function(x) sum(!is.na(x)))

# Create a summary data frame
summary_data <- data.frame(column_names, data_types, non_null_counts)

# Rename the columns of the summary data frame
colnames(summary_data) <- c("Column Name", "Data Type", "Non-Null Count")

# Print the summary data frame
print(summary_data)
##          Column Name Data Type Non-Null Count
## _state        _state   numeric         445132
## genhlth      genhlth   numeric         445129
## poorhlth    poorhlth   numeric         255746
## cvdcrhd4    cvdcrhd4   numeric         445130
## diabete4    diabete4   numeric         445129
## marital      marital   numeric         445124
## educa          educa   numeric         445127
## employ1      employ1   numeric         438936
## income3      income3   numeric         432200
## smokday2    smokday2   numeric         164053
## _totinda    _totinda   numeric         445132
## _sex            _sex   numeric         445132
## _ageg5yr    _ageg5yr   numeric         445132
## _bmi5          _bmi5   numeric         396326
## _bmi5cat    _bmi5cat   numeric         396326
#we see that we have the same number of observations
#non null data, so we do not have missing values.
# Get the number of missing values in each column
missing_counts <- colSums(is.na(data))

# Sort the missing value counts in descending order
missing_counts <- sort(missing_counts, decreasing = TRUE)

# Filter out columns with no missing values
missing_counts <- missing_counts[missing_counts > 0]

# Print the result
print(missing_counts)
## smokday2 poorhlth    _bmi5 _bmi5cat  income3  employ1  marital    educa 
##   281079   189386    48806    48806    12932     6196        8        5 
##  genhlth diabete4 cvdcrhd4 
##        3        3        2
# Create a new column for index
data$index <- seq_len(nrow(data))

# Print the dataframe with the new index
print(data)
## # A tibble: 445,132 × 16
##    `_state` genhlth poorhlth cvdcrhd4 diabete4 marital educa employ1 income3
##       <dbl>   <dbl>    <dbl>    <dbl>    <dbl>   <dbl> <dbl>   <dbl>   <dbl>
##  1        1       2       NA        2        1       1     6       7      99
##  2        1       1       NA        2        3       3     4       2       5
##  3        1       2        2        2        3       1     6       7      10
##  4        1       1       NA        2        3       1     4       7      77
##  5        1       4       88        2        3       1     5       5       5
##  6        1       5       88        2        1       1     4       7      99
##  7        1       2       NA        2        3       2     6       7       8
##  8        1       3       NA        2        3       3     4       7       7
##  9        1       3       NA        2        3       1     6       1       7
## 10        1       3        2        2        1       1     6       7       7
## # ℹ 445,122 more rows
## # ℹ 7 more variables: smokday2 <dbl>, `_totinda` <dbl>, `_sex` <dbl>,
## #   `_ageg5yr` <dbl>, `_bmi5` <dbl>, `_bmi5cat` <dbl>, index <int>
# Remove rows with null values
data <- na.omit(data)

# Print the dataframe after removing null values
print(data)
## # A tibble: 94,694 × 16
##    `_state` genhlth poorhlth cvdcrhd4 diabete4 marital educa employ1 income3
##       <dbl>   <dbl>    <dbl>    <dbl>    <dbl>   <dbl> <dbl>   <dbl>   <dbl>
##  1        1       4       15        2        3       1     4       8       7
##  2        1       4       88        2        1       3     3       7       6
##  3        1       4       88        1        1       2     4       8       2
##  4        1       3       88        1        3       1     3       4      77
##  5        1       5       14        1        1       2     5       8      77
##  6        1       5       30        2        1       1     6       5      99
##  7        1       3       15        2        3       3     5       7       2
##  8        1       3       88        2        3       1     4       7       7
##  9        1       4       88        2        1       1     4       7       1
## 10        1       4        4        2        1       1     4       7      99
## # ℹ 94,684 more rows
## # ℹ 7 more variables: smokday2 <dbl>, `_totinda` <dbl>, `_sex` <dbl>,
## #   `_ageg5yr` <dbl>, `_bmi5` <dbl>, `_bmi5cat` <dbl>, index <int>
#New dimension after deleting the missing values
dim(data)
## [1] 94694    16
# Re_index
data$index <- paste0("row", seq_len(nrow(data)))

We will remove the values for the questions when the respondents let it blank and they are coded under a number, in this case the omi function can not delete them so we had to do it manually for each variabel following the dataset’s codebook.

# Define the columns and values to remove
columns_to_remove <- c('genhlth', 'poorhlth', 'cvdcrhd4', 'diabete4', 'marital', 
                       'educa', 'employ1', 'income3', 'smokday2', '_totinda', '_sex', '_ageg5yr')

#List with values to remove
values_to_remove <- list(
  genhlth = c(9, 7),  
  poorhlth = c(88, 77, 99),  
  cvdcrhd4 = c(7, 9),        
  diabete4 = c(7, 9),        
  marital = c(9),            
  educa = c(9),              
  employ1 = c(9),            
  income3 = c(77, 99),       
  smokday2 = c(7, 9),       
  "_totinda" = c(9),        
  "_ageg5yr"= c(14)        
)

# Function to filter out rows with specific values in each column
filter_values <- function(data, column_name, values) {
  data %>%
    filter(!(!!rlang::sym(column_name) %in% values))
}

# Apply the function to each column
filtered_data <- data
for (column_name in columns_to_remove) {
  filtered_data <- filter_values(filtered_data, column_name, values_to_remove[[column_name]])
}

# Print the first few rows of the filtered data frame
print(head(filtered_data))
## # A tibble: 6 × 16
##   `_state` genhlth poorhlth cvdcrhd4 diabete4 marital educa employ1 income3
##      <dbl>   <dbl>    <dbl>    <dbl>    <dbl>   <dbl> <dbl>   <dbl>   <dbl>
## 1        1       4       15        2        3       1     4       8       7
## 2        1       3       15        2        3       3     5       7       2
## 3        1       4       15        1        3       2     4       7       2
## 4        1       3        1        2        1       1     4       7       3
## 5        1       3        1        2        3       3     4       7       2
## 6        1       4        1        1        3       3     6       7       3
## # ℹ 7 more variables: smokday2 <dbl>, `_totinda` <dbl>, `_sex` <dbl>,
## #   `_ageg5yr` <dbl>, `_bmi5` <dbl>, `_bmi5cat` <dbl>, index <chr>
#The dimension after deleting the missing values with a specific number
dim(filtered_data)
## [1] 39551    16

We will change the names of the columns, so it will be easier for us to understand and work with.

names(filtered_data)
##  [1] "_state"   "genhlth"  "poorhlth" "cvdcrhd4" "diabete4" "marital" 
##  [7] "educa"    "employ1"  "income3"  "smokday2" "_totinda" "_sex"    
## [13] "_ageg5yr" "_bmi5"    "_bmi5cat" "index"
# Change column names
names(filtered_data) <- c("state", "genhlth", "poorhlth", "hrtdisease", "diabetic", "marital", "edu",   "employstatus", "income", "smokeday", "totinda", "sex", "age5cat", "bmi5", "bmi", "index", "obesity_status")
## Warning: The `value` argument of `names<-()` must have the same length as `x` as of
## tibble 3.0.0.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Print new column names
print(names(filtered_data)) 
##  [1] "state"        "genhlth"      "poorhlth"     "hrtdisease"   "diabetic"    
##  [6] "marital"      "edu"          "employstatus" "income"       "smokeday"    
## [11] "totinda"      "sex"          "age5cat"      "bmi5"         "bmi"         
## [16] "index"
summary(filtered_data)
##      state          genhlth         poorhlth       hrtdisease   
##  Min.   : 1.00   Min.   :1.000   Min.   : 1.00   Min.   :1.000  
##  1st Qu.:18.00   1st Qu.:3.000   1st Qu.: 3.00   1st Qu.:2.000  
##  Median :29.00   Median :3.000   Median :10.00   Median :2.000  
##  Mean   :30.67   Mean   :3.277   Mean   :12.67   Mean   :1.885  
##  3rd Qu.:45.00   3rd Qu.:4.000   3rd Qu.:20.00   3rd Qu.:2.000  
##  Max.   :78.00   Max.   :5.000   Max.   :30.00   Max.   :2.000  
##     diabetic        marital           edu         employstatus  
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:3.000   1st Qu.:1.000   1st Qu.:4.000   1st Qu.:1.000  
##  Median :3.000   Median :2.000   Median :5.000   Median :5.000  
##  Mean   :2.638   Mean   :2.535   Mean   :4.852   Mean   :4.417  
##  3rd Qu.:3.000   3rd Qu.:4.000   3rd Qu.:6.000   3rd Qu.:7.000  
##  Max.   :4.000   Max.   :6.000   Max.   :6.000   Max.   :8.000  
##      income         smokeday        totinda           sex       
##  Min.   : 1.00   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.: 4.00   1st Qu.:1.000   1st Qu.:1.000   1st Qu.:1.000  
##  Median : 6.00   Median :3.000   Median :1.000   Median :2.000  
##  Mean   : 5.85   Mean   :2.371   Mean   :1.361   Mean   :1.533  
##  3rd Qu.: 8.00   3rd Qu.:3.000   3rd Qu.:2.000   3rd Qu.:2.000  
##  Max.   :11.00   Max.   :3.000   Max.   :2.000   Max.   :2.000  
##     age5cat            bmi5           bmi           index          
##  Min.   : 1.000   Min.   :1205   Min.   :1.000   Length:39551      
##  1st Qu.: 5.000   1st Qu.:2439   1st Qu.:2.000   Class :character  
##  Median : 8.000   Median :2831   Median :3.000   Mode  :character  
##  Mean   : 7.548   Mean   :2959   Mean   :3.097                     
##  3rd Qu.:10.000   3rd Qu.:3330   3rd Qu.:4.000                     
##  Max.   :13.000   Max.   :9566   Max.   :4.000

We will make less categories for the the specific columns and mapping the states as they were coded as numbers from 1 to 78, we mapped them to their corsponding names.

#Mapping the states
state_mapping <- c(
  `1` = "Alabama", `2` = "Alaska", `4` = "Arizona", `5` = "Arkansas", 
  `6` = "California", `8` = "Colorado", `9` = "Connecticut", `10` = "Delaware", 
  `11` = "District of Columbia", `12` = "Florida", `13` = "Georgia", `15` = "Hawaii", 
  `16` = "Idaho", `17` = "Illinois", `18` = "Indiana", `19` = "Iowa", 
  `20` = "Kansas", `21` = "Kentucky", `22` = "Louisiana", `23` = "Maine", 
  `24` = "Maryland", `25` = "Massachusetts", `26` = "Michigan", `27` = "Minnesota", 
  `28` = "Mississippi", `29` = "Missouri", `30` = "Montana", `31` = "Nebraska", 
  `32` = "Nevada", `33` = "New Hampshire", `34` = "New Jersey", `35` = "New Mexico", 
  `36` = "New York", `37` = "North Carolina", `38` = "North Dakota", `39` = "Ohio", 
  `40` = "Oklahoma", `41` = "Oregon", `42` = "Pennsylvania", `44` = "Rhode Island", 
  `45` = "South Carolina", `46` = "South Dakota", `47` = "Tennessee", `48` = "Texas", 
  `49` = "Utah", `50` = "Vermont", `51` = "Virginia", `53` = "Washington", 
  `54` = "West Virginia", `55` = "Wisconsin", `56` = "Wyoming", `66` = "Guam", 
  `72` = "Puerto Rico", `78` = "Virgin Islands"
)

edu_mapping <- c(
  "Never attended school or only kindergarten" = "Elementary or less",
  "Grades 1 through 8 (Elementary)" = "Elementary or less",
  "Grades 9 through 11 (Some high school)" = "High school or equivalent",
  "Grade 12 or GED (High school graduate)" = "High school or equivalent",
  "College 1 year to 3 years (Some college or technical school)" = "College or more",
  "College 4 years or more (College graduate)" = "College or more"
)

# Convert the edu column from numeric codes to descriptive names using the mapping
edu_levels <- c(
  "Never attended school or only kindergarten",
  "Grades 1 through 8 (Elementary)",
  "Grades 9 through 11 (Some high school)",
  "Grade 12 or GED (High school graduate)",
  "College 1 year to 3 years (Some college or technical school)",
  "College 4 years or more (College graduate)"
)

# Ensure age5cat is correctly labeled
age5cat_labels <- c(
  "Age 18 to 24", "Age 25 to 29", "Age 30 to 34", "Age 35 to 39", 
  "Age 40 to 44", "Age 45 to 49", "Age 50 to 54", "Age 55 to 59", 
  "Age 60 to 64", "Age 65 to 69", "Age 70 to 74", "Age 75 to 79", 
  "Age 80 or older"
)

# Convert age5cat to a factor with the correct levels and labels
filtered_data$age5cat <- factor(filtered_data$age5cat, levels = 1:13, labels = age5cat_labels)

# Create the age5cat mapping to fewer categories
age5cat_mapping <- c(
  "Age 18 to 24" = "Age 18 to 29",
  "Age 25 to 29" = "Age 18 to 29",
  "Age 30 to 34" = "Age 30 to 39",
  "Age 35 to 39" = "Age 30 to 39",
  "Age 40 to 44" = "Age 40 to 54",
  "Age 45 to 49" = "Age 40 to 54",
  "Age 50 to 54" = "Age 40 to 54",
  "Age 55 to 59" = "Age 55 to 69",
  "Age 60 to 64" = "Age 55 to 69",
  "Age 65 to 69" = "Age 55 to 69",
  "Age 70 to 74" = "Age 70 and older",
  "Age 75 to 79" = "Age 70 and older",
  "Age 80 or older" = "Age 70 and older"
)

filtered_data <- filtered_data %>% mutate(obesity_status = factor(ifelse(bmi == 4, 1, 0), levels = c(0, 1), labels = c("Not Obese", "Obese")))


# Assuming filtered_data is your dataframe and income is the current income factor
# Define new income categories
filtered_data$income_group <- cut(as.numeric(filtered_data$income), 
                                  breaks = c(0, 4, 7, 11), 
                                  labels = c("Low Income", "Middle Income", "High Income"),
                                  include.lowest = TRUE)

# Create a new variable 'diabetic_status' based on the existing 'diabetic' variable
filtered_data$diabetic_status <- ifelse(filtered_data$diabetic %in% c(1, 2), 1, 
                                        ifelse(filtered_data$diabetic %in% c(3, 4), 2, NA))
# Map the age5cat column to fewer categories
filtered_data$age5cat <- as.factor(age5cat_mapping[as.character(filtered_data$age5cat)])

# Ensure edu is a factor with the correct levels and labels
filtered_data$edu <- factor(filtered_data$edu, levels = 1:6, labels = edu_levels)

# Map the edu column to fewer categories
filtered_data$edu <- as.factor(edu_mapping[as.character(filtered_data$edu)])

# Map state codes to state names
filtered_data$state <- as.factor(state_mapping[as.character(filtered_data$state)])

filtered_data$sex <- factor(filtered_data$sex, levels = c(1, 2), labels = c("Male", "Female"))

# Define the marital status labels as a factor with appropriate levels
filtered_data$marital <- factor(filtered_data$marital, 
                                levels = 1:6, 
                                labels = c("Married", "Divorced", "Widowed", "Separated", "Never married", "Member of an unmarried couple"))

# Define the employment status labels as a factor with appropriate levels
filtered_data$employstatus <- factor(filtered_data$employstatus, 
                                     levels = 1:8, 
                                     labels = c("Employed for wages", "Self-employed", 
                                                "Out of work for 1 year or more", "Out of work for less than 1 year", 
                                                "A homemaker", "A student", "Retired", "Unable to work"))

# Define the heart disease status labels
filtered_data$hrtdisease <- factor(filtered_data$hrtdisease, levels = c(1, 2), labels = c("Yes", "No"))


# Display the structure of the filtered_data data frame to confirm changes
str(filtered_data)
## tibble [39,551 × 19] (S3: tbl_df/tbl/data.frame)
##  $ state          : Factor w/ 54 levels "Alabama","Alaska",..: 1 1 1 1 1 1 1 1 1 1 ...
##   ..- attr(*, "names")= chr [1:39551] "1" "1" "1" "1" ...
##  $ genhlth        : num [1:39551] 4 3 4 3 3 4 4 5 2 5 ...
##  $ poorhlth       : num [1:39551] 15 15 15 1 1 1 30 30 5 30 ...
##  $ hrtdisease     : Factor w/ 2 levels "Yes","No": 2 2 1 2 2 1 1 1 2 2 ...
##  $ diabetic       : num [1:39551] 3 3 3 1 3 3 1 1 3 3 ...
##  $ marital        : Factor w/ 6 levels "Married","Divorced",..: 1 3 2 1 3 3 1 1 1 1 ...
##  $ edu            : Factor w/ 3 levels "College or more",..: 3 1 3 3 3 1 3 1 3 1 ...
##   ..- attr(*, "names")= chr [1:39551] "Grade 12 or GED (High school graduate)" "College 1 year to 3 years (Some college or technical school)" "Grade 12 or GED (High school graduate)" "Grade 12 or GED (High school graduate)" ...
##  $ employstatus   : Factor w/ 8 levels "Employed for wages",..: 8 7 7 7 7 7 7 8 7 2 ...
##  $ income         : num [1:39551] 7 2 2 3 2 3 5 7 5 8 ...
##  $ smokeday       : num [1:39551] 3 1 1 3 3 1 3 1 3 3 ...
##  $ totinda        : num [1:39551] 2 2 2 1 1 1 2 2 2 2 ...
##  $ sex            : Factor w/ 2 levels "Male","Female": 1 2 2 1 2 1 1 2 2 1 ...
##  $ age5cat        : Factor w/ 5 levels "Age 18 to 29",..: 4 4 4 4 5 5 5 3 5 5 ...
##   ..- attr(*, "names")= chr [1:39551] "Age 55 to 59" "Age 60 to 64" "Age 65 to 69" "Age 65 to 69" ...
##  $ bmi5           : num [1:39551] 2859 4561 3112 3626 3219 ...
##  $ bmi            : num [1:39551] 3 4 4 4 4 2 4 4 3 3 ...
##  $ index          : chr [1:39551] "row1" "row7" "row11" "row17" ...
##  $ obesity_status : Factor w/ 2 levels "Not Obese","Obese": 1 2 2 2 2 1 2 2 1 1 ...
##  $ income_group   : Factor w/ 3 levels "Low Income","Middle Income",..: 2 1 1 1 1 1 2 2 2 3 ...
##  $ diabetic_status: num [1:39551] 2 2 2 1 2 2 1 1 2 2 ...
##  - attr(*, "na.action")= 'omit' Named int [1:350438] 1 2 3 4 5 6 7 8 9 10 ...
##   ..- attr(*, "names")= chr [1:350438] "1" "2" "3" "4" ...

We will convert the column to factors (mainly they are numeric).

# Convert specified columns to factors in the filtered_data data frame
filtered_data$genhlth <- as.factor(filtered_data$genhlth)
filtered_data$poorhlth <- as.factor(filtered_data$poorhlth)
filtered_data$hrtdisease <- as.factor(filtered_data$hrtdisease)
filtered_data$diabetic <- as.factor(filtered_data$diabetic)
filtered_data$marital <- as.factor(filtered_data$marital)
filtered_data$employstatus <- as.factor(filtered_data$employstatus)
filtered_data$income <- as.factor(filtered_data$income)
filtered_data$smokeday <- as.factor(filtered_data$smokeday)
filtered_data$totinda <- as.factor(filtered_data$totinda)
filtered_data$sex <- as.factor(filtered_data$sex)
filtered_data$bmi5 <- as.factor(filtered_data$bmi5)
filtered_data$obesity_status <- factor(filtered_data$obesity_status)
filtered_data$diabetic_status <- as.factor(filtered_data$diabetic_status)

# Display the structure of the filtered_data data frame to confirm changes
str(filtered_data)
## tibble [39,551 × 19] (S3: tbl_df/tbl/data.frame)
##  $ state          : Factor w/ 54 levels "Alabama","Alaska",..: 1 1 1 1 1 1 1 1 1 1 ...
##   ..- attr(*, "names")= chr [1:39551] "1" "1" "1" "1" ...
##  $ genhlth        : Factor w/ 5 levels "1","2","3","4",..: 4 3 4 3 3 4 4 5 2 5 ...
##  $ poorhlth       : Factor w/ 30 levels "1","2","3","4",..: 15 15 15 1 1 1 30 30 5 30 ...
##  $ hrtdisease     : Factor w/ 2 levels "Yes","No": 2 2 1 2 2 1 1 1 2 2 ...
##  $ diabetic       : Factor w/ 4 levels "1","2","3","4": 3 3 3 1 3 3 1 1 3 3 ...
##  $ marital        : Factor w/ 6 levels "Married","Divorced",..: 1 3 2 1 3 3 1 1 1 1 ...
##  $ edu            : Factor w/ 3 levels "College or more",..: 3 1 3 3 3 1 3 1 3 1 ...
##   ..- attr(*, "names")= chr [1:39551] "Grade 12 or GED (High school graduate)" "College 1 year to 3 years (Some college or technical school)" "Grade 12 or GED (High school graduate)" "Grade 12 or GED (High school graduate)" ...
##  $ employstatus   : Factor w/ 8 levels "Employed for wages",..: 8 7 7 7 7 7 7 8 7 2 ...
##  $ income         : Factor w/ 11 levels "1","2","3","4",..: 7 2 2 3 2 3 5 7 5 8 ...
##  $ smokeday       : Factor w/ 3 levels "1","2","3": 3 1 1 3 3 1 3 1 3 3 ...
##  $ totinda        : Factor w/ 2 levels "1","2": 2 2 2 1 1 1 2 2 2 2 ...
##  $ sex            : Factor w/ 2 levels "Male","Female": 1 2 2 1 2 1 1 2 2 1 ...
##  $ age5cat        : Factor w/ 5 levels "Age 18 to 29",..: 4 4 4 4 5 5 5 3 5 5 ...
##   ..- attr(*, "names")= chr [1:39551] "Age 55 to 59" "Age 60 to 64" "Age 65 to 69" "Age 65 to 69" ...
##  $ bmi5           : Factor w/ 2512 levels "1205","1216",..: 948 2019 1131 1490 1205 383 1291 1459 903 888 ...
##  $ bmi            : num [1:39551] 3 4 4 4 4 2 4 4 3 3 ...
##  $ index          : chr [1:39551] "row1" "row7" "row11" "row17" ...
##  $ obesity_status : Factor w/ 2 levels "Not Obese","Obese": 1 2 2 2 2 1 2 2 1 1 ...
##  $ income_group   : Factor w/ 3 levels "Low Income","Middle Income",..: 2 1 1 1 1 1 2 2 2 3 ...
##  $ diabetic_status: Factor w/ 2 levels "1","2": 2 2 2 1 2 2 1 1 2 2 ...
##  - attr(*, "na.action")= 'omit' Named int [1:350438] 1 2 3 4 5 6 7 8 9 10 ...
##   ..- attr(*, "names")= chr [1:350438] "1" "2" "3" "4" ...

EDA

First we wanted to see the distribution of our variables

# Plot bar chart for gender distribution
ggplot(filtered_data, aes(x = sex, fill = sex)) +
  geom_bar() +
  geom_text(stat = 'count', aes(label = ..count..), 
            vjust = -0.5, size = 5, color = "black", fontface = "bold") +  
  scale_fill_manual(values = c("Male" = "blue4", "Female" = "red4")) +  
  labs(title = "Gender Distribution in Our Dataset", x = "Gender", y = "Count") +
  theme_minimal() +  
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),  
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 10),
    legend.position = "none"  # Remove the legend
  ) +
  ylim(0, max(table(filtered_data$sex)) * 1.1)
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

The bar chart shows that the dataset consists of 18,479 males (46.7%) and 21,072 females (53.3%), indicating a higher representation of females.

# Plot the bar chart
ggplot(filtered_data, aes(x = marital)) +
  geom_bar(fill = "blue4") +
  geom_text(stat = 'count', aes(label = ..count..), vjust = -0.5, size = 5, color = "black", fontface = "bold") +
  labs(title = "Distribution of Marital Status", x = "Marital Status", y = "Count") +
  ylim(0, max(table(filtered_data$marital)) * 1.1) +  
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", margin = margin(b = 10)),  
    axis.title = element_text(size = 14),  
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1),  
    axis.text.y = element_text(size = 12),  
    panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "grey80"),  
    panel.grid.minor = element_blank(),  
    legend.position = "none"  
  )
## Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.


The bar chart shows the distribution of marital status in our dataset, we have 16,238 individuals are married (38.4%), 8,368 are divorced (19.8%), 4,142 are widowed (9.8%), 1,399 are separated (3.3%), 7,152 have never married (16.9%), and 2,252 are members of an unmarried couple (5.3%). The majority of individuals in the dataset are married, followed by those who are divorced and never married.

# Plot the bar chart
ggplot(filtered_data, aes(x = edu)) +
  geom_bar(fill = "blue4") +
  geom_text(stat = 'count', aes(label = ..count..), 
            vjust = -0.5, size = 5, color = "black", fontface = "bold") +
  labs(title = "Distribution of Education Status", x = "Education Level", y = "Count") +
  ylim(0, max(table(filtered_data$edu)) * 1.1) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", margin = margin(b = 10)),  
    axis.title = element_text(size = 14),  
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1),  
    axis.text.y = element_text(size = 12),  
    panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "grey80"),  
    panel.grid.minor = element_blank(),  
    legend.position = "none"  
  )


The bar chart shows the distribution of education status in the dataset we have 25,386 individuals have a college education or more (64.8%), 693 have an elementary education or less (1.8%), and 13,472 have a high school education or equivalent (33.4%). The majority of individuals in the dataset have a college education or higher.

# Plot the bar chart
ggplot(filtered_data, aes(x = employstatus)) +
  geom_bar(fill = "blue4") +
  geom_text(stat = 'count', aes(label = ..count..), 
            vjust = -0.5, size = 4, color = "black", fontface = "bold") +
  labs(title = "Distribution of Employment Status", x = "Employment Status", y = "Count") +
  ylim(0, max(table(filtered_data$employstatus)) * 1.1) +  
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", margin = margin(b = 10)),  
    axis.title = element_text(size = 14),  
    axis.text.x = element_text(size = 10, angle = 45, hjust = 1, vjust = 1),  
    axis.text.y = element_text(size = 12),  
    panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "grey80"),  
    panel.grid.minor = element_blank(),  
    legend.position = "none"  
  )


The bar chart shows the distribution of employment status in the dataset as here we have 13,734 individuals are employed for wages (34.9%), 2,855 are self-employed (7.3%), 1,542 have been out of work for 1 year or more (3.9%), 1,236 have been out of work for less than 1 year (3.1%), 1,330 are homemakers (3.4%), 451 are students (1.1%), 10,888 are retired (27.6%), and 7,515 are unable to work (19.1%). The majority of individuals in the dataset are employed for wages, followed by those who are retired.

# Plot the bar chart
ggplot(filtered_data, aes(x = income_group)) +
  geom_bar(fill = "blue4") +
  geom_text(stat = 'count', aes(label = ..count..), 
            vjust = -0.5, size = 4, color = "black", fontface = "bold") +
  labs(title = "Distribution of Income Status", x = "Income Status", y = "Frequency") +
  ylim(0, max(table(filtered_data$income_group)) * 1.1) +  
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", margin = margin(b = 10)),  
    axis.title = element_text(size = 14),  
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1),  
    axis.text.y = element_text(size = 12),  
    panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "grey80"),  
    panel.grid.minor = element_blank(),  
    legend.position = "none"  
  )


The bar chart shows the distribution of income status in the dataset: 11,349 individuals have low income (29.0%), 17,526 have middle income (44.7%), and 10,676 have high income (27.2%). The majority of individuals in the dataset have middle income, followed by low income and high income.

# Plot the pie chart with improved aesthetics
ggplot(filtered_data, aes(x = "", fill = hrtdisease)) +
  geom_bar(width = 1, stat = "count", color = "black") +
  coord_polar("y") +
  geom_text(stat = 'count', aes(label = paste0(round(..count.. / sum(..count..) * 100, 1), "%")), 
            position = position_stack(vjust = 0.5), size = 5, color = "white", fontface = "bold") +
  scale_fill_manual(values = c("Yes" = "red4", "No" = "blue4")) +
  labs(title = "Distribution of Coronary Heart Disease (CHD) or Myocardial Infarction (MI)") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", margin = margin(t = 20, b = 10)),  
    axis.title = element_blank(),  
    axis.text = element_blank(),  
    axis.ticks = element_blank(),
    panel.grid = element_blank(),  
    legend.title = element_blank(),  
    legend.position = "bottom"
  )


The pie chart shows the distribution of coronary heart disease (CHD) or myocardial infarction (MI) status in the dataset: 11.5% of individuals have CHD or MI, while 88.5% do not have these conditions. This indicates that the vast majority of individuals in the dataset do not have CHD or MI.

# Plot the pie chart with improved aesthetics
ggplot(filtered_data, aes(x = "", fill = factor(totinda))) +
  geom_bar(width = 1, stat = "count", color = "black") +
  coord_polar("y") +
  geom_text(stat = 'count', aes(label = paste0(round(..count.. / sum(..count..) * 100, 1), "%")), 
            position = position_stack(vjust = 0.5), size = 5, color = "white", fontface = "bold") +
  scale_fill_manual(
    values = c("1" = "blue4", "2" = "red4"), 
    labels = c("1" = "Had physical activity or exercise", 
               "2" = "No physical activity or exercise")
  ) +
  labs(title = "Distribution of Physical Activity or Exercise During the Past 30 Days") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", margin = margin(t = 20, b = 10)),  
    axis.title = element_blank(),  
    axis.text = element_blank(),  
    axis.ticks = element_blank(),
    panel.grid = element_blank(),  
    legend.title = element_blank(),  
    legend.position = "bottom"
  )


The pie chart shows the distribution of physical activity or exercise during the past 30 days: 63.9% of individuals had physical activity or exercise, while 36.1% did not have any physical activity or exercise. This indicates that a majority of individuals in the dataset engaged in physical activity or exercise during the past month.

# Plot the pie chart with improved aesthetics
ggplot(filtered_data, aes(x = "", fill = factor(diabetic_status))) +
  geom_bar(width = 1, stat = "count", color = "black") +
  coord_polar("y") +
  geom_text(stat = 'count', aes(label = paste0(round(..count.. / sum(..count..) * 100, 1), "%")), 
            position = position_stack(vjust = 0.5), size = 5, color = "white", fontface = "bold") +
  scale_fill_manual(
    values = c("2" = "blue4", "1" = "red4"), 
    labels = c("1" = "Yes", "2" = "No")
  ) +
  labs(title = "Distribution of Diabetes") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", margin = margin(t = 20, b = 10)),  
    axis.title = element_blank(),  
    axis.text = element_blank(),  
    axis.ticks = element_blank(),
    panel.grid = element_blank(),  
    legend.title = element_blank(),  
    legend.position = "bottom"
  )


The pie chart shows the distribution of diabetes status in the dataset: 20.1% of individuals have diabetes, while 79.9% do not have diabetes. This indicates that a significant majority of individuals in the dataset do not have diabetes.

# Plot the bar chart
ggplot(filtered_data, aes(x = factor(genhlth, levels = 1:5, labels = c("Excellent", "Very good", "Good", "Fair", "Poor")))) +
  geom_bar(fill = "blue4") +
  geom_text(stat = 'count', aes(label = ..count..), vjust = -0.5, size = 4, color = "black", fontface = "bold") +
  labs(title = "Distribution of General Health Status", x = "Health Status", y = "Frequency") +
  ylim(0, max(table(filtered_data$genhlth)) * 1.1) +  
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", margin = margin(b = 10)),  
    axis.title = element_text(size = 14),  
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1),  
    axis.text.y = element_text(size = 12),  
    panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "grey80"),  
    panel.grid.minor = element_blank(),  
    legend.position = "none"  
  )


The bar chart shows the distribution of general health status in the dataset: 1,927 individuals reported their health as excellent (4.9%), 7,924 as very good (20.1%), 12,899 as good (32.8%), 10,872 as fair (27.6%), and 5,929 as poor (15.1%). The most common health status reported is good, followed by fair.

# Plot the bar chart
ggplot(filtered_data, aes(x = factor(smokeday, levels = 1:3, labels = c("Every day", "Some days", "Not at all")))) +
  geom_bar(fill = "blue4") +
  geom_text(stat = 'count', aes(label = ..count..), vjust = -0.5, size = 4, color = "black", fontface = "bold") +
  labs(title = "Distribution of Smoking Status", x = "Smoking Status", y = "Frequency") +
  ylim(0, max(table(filtered_data$smokeday)) * 1.1) +  
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", margin = margin(b = 10)),  
    axis.title = element_text(size = 14),  
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1),  
    axis.text.y = element_text(size = 12),  
    panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "grey80"),  
    panel.grid.minor = element_blank(),  
    legend.position = "none"  
  )


The bar chart shows the distribution of smoking status in the dataset: 10,408 individuals smoke every day (21.4%), 4,062 smoke on some days (8.3%), and 25,081 do not smoke at all (70.3%). The majority of individuals in the dataset do not smoke.

Obesity rate over the states

In this part of the project, we wanted to include a map with the obesity rates for each respective state, so we understand the distribution of the obesity in the United States of America for creating a general view.

# Summarize the obesity rate by state
obesity_by_state <- filtered_data %>%
  group_by(state) %>%
  summarize(
    total_individuals = n(),
    obese_individuals = sum(obesity_status == "Obese"),
    obesity_rate = mean(obesity_status == "Obese")
  ) %>%
  arrange(desc(obesity_rate))

# Print the summarized obesity rates per state
print(obesity_by_state)
## # A tibble: 54 × 4
##    state         total_individuals obese_individuals obesity_rate
##    <fct>                     <int>             <int>        <dbl>
##  1 North Dakota                308               149        0.484
##  2 Wisconsin                  1084               509        0.470
##  3 Iowa                        805               377        0.468
##  4 Kentucky                    435               201        0.462
##  5 Ohio                       1676               762        0.455
##  6 South Dakota                603               273        0.453
##  7 West Virginia               630               278        0.441
##  8 Wyoming                     364               160        0.440
##  9 Delaware                    335               147        0.439
## 10 Oklahoma                    582               254        0.436
## # ℹ 44 more rows
# Load US map data
us_map <- map_data("state")

# Merge map data with obesity data
us_map$region <- tolower(us_map$region)
obesity_by_state$state <- tolower(obesity_by_state$state)
map_data <- merge(us_map, obesity_by_state, by.x = "region", by.y = "state", all.x = TRUE)

# Plot the map
ggplot(map_data, aes(x = long, y = lat, group = group, fill = obesity_rate)) +
  geom_polygon(color = "white", size = 0.2) +
  scale_fill_continuous(name = "Obesity Rate", low = "lightblue2", high = "darkred") +
  coord_fixed(1.3) + # Maintain aspect ratio
  theme_minimal() +
  labs(
    title = "Obesity Rates by State",
    x = NULL,
    y = NULL
  ) +
  theme(
    plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
    legend.position = "bottom",
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 10),
    axis.text = element_blank(),
    axis.ticks = element_blank(),
    panel.grid = element_blank(),
    panel.border = element_blank()
  ) +
  guides(fill = guide_colorbar(barwidth = 10, barheight = 1))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

The map provided above shows the obesity rates by state in the United States and some other parts of countries bordered. It is represented with varying shades of red representing different levels of obesity rates.

GENERAL TREND: Most states have high obesity rates, as indicated by the predominant dark red shading across the map.

States with the darkest red color have the highest obesity rates, close to 0.45. These states are mainly located in the South and parts of the Midwest. Specifically, North Dakota, Wisconsin and Iowa are the states with the highest rate of obesity.

Contingency Tables

Now, we will make some contingency tables. This type of tables show the distribution of individuals observed for the combinations of the categories of the 2 variables or more.

Joint Probability

Let us start with The joint probability

# Joint Probability table for obesity_status and sex
joint_prob_sex <- prop.table(table(filtered_data$obesity_status, filtered_data$sex))
kable(joint_prob_sex, digits = 3, caption = "Joint Probability of Obesity Status and Sex") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Joint Probability of Obesity Status and Sex
Male Female
Not Obese 0.292 0.307
Obese 0.175 0.225
# Joint Probability table for obesity_status and age5cat
joint_prob_age <- prop.table(table(filtered_data$obesity_status, filtered_data$age5cat))
kable(joint_prob_age, digits = 3, caption = "Joint Probability of Obesity Status and Age Category") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Joint Probability of Obesity Status and Age Category
Age 18 to 29 Age 30 to 39 Age 40 to 54 Age 55 to 69 Age 70 and older
Not Obese 0.048 0.085 0.140 0.190 0.136
Obese 0.021 0.055 0.114 0.142 0.069
# Joint Probability table for obesity_status and income_group
joint_prob_income <- prop.table(table(filtered_data$obesity_status, filtered_data$income_group))
kable(joint_prob_income, digits = 3, caption = "Joint Probability of Obesity Status and Income Group") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Joint Probability of Obesity Status and Income Group
Low Income Middle Income High Income
Not Obese 0.167 0.264 0.168
Obese 0.120 0.179 0.102

In the Joint Probability, the probability is calculated of two events occurring simultaneously. It is calculated by dividing the count of occurrences of both events by the total number of observations.

Sex Differences in Obesity: There seems to be a difference in obesity rates between males and females, with females having a higher probability of being obese. (0.225 > 0.175). These probabilities show associations, but they don’t imply causation. For example, being female doesn’t cause obesity, nor does being male prevent it. Other factors not included in this table could contribute to obesity rates.

WE HAVE 5 categories of the age: Generally, the probability of being obese tends to increase with age, with the highest probability among individuals aged 55 to 69 (0.142). Age 70 and older is low rate.

MIDDLE INCOME has the highest obesity: For the probability of being obese, the middle-income group has the highest probability (0.179), followed by the low-income group (0.120) and the high-income group (0.102).

Conditional Probability

# Conditional Probability of obesity_status given sex
conditional_prob_obesity_sex <- prop.table(table(filtered_data$obesity_status, filtered_data$sex), 2)
kable(conditional_prob_obesity_sex, digits = 3, caption = "Conditional Probability of Obesity Status given Sex") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Conditional Probability of Obesity Status given Sex
Male Female
Not Obese 0.626 0.577
Obese 0.374 0.423
# Conditional Probability of obesity_status given age5cat
conditional_prob_obesity_age <- prop.table(table(filtered_data$obesity_status, filtered_data$age5cat), 2)
kable(conditional_prob_obesity_age, digits = 3, caption = "Conditional Probability of Obesity Status given Age Category") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Conditional Probability of Obesity Status given Age Category
Age 18 to 29 Age 30 to 39 Age 40 to 54 Age 55 to 69 Age 70 and older
Not Obese 0.699 0.607 0.551 0.572 0.665
Obese 0.301 0.393 0.449 0.428 0.335
# Conditional Probability of obesity_status given income_group
conditional_prob_obesity_income <- prop.table(table(filtered_data$obesity_status, filtered_data$income_group), 2)
kable(conditional_prob_obesity_income, digits = 3, caption = "Conditional Probability of Obesity Status given Income Group") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Conditional Probability of Obesity Status given Income Group
Low Income Middle Income High Income
Not Obese 0.582 0.596 0.624
Obese 0.418 0.404 0.376

In here we wanted to see the probability of being obese occurring given that another event has already occurred which is Conditional Probability. It is calculated by dividing the joint probability of the two events by the probability of the conditioning event.

Sex Differences in Obesity: The joint probabilities indicated that females have a higher probability of being obese (42.3%) compared to males (37.4%).

Age Differences in Obesity: The conditional probabilities show an increasing trend of obesity with age, peaking at 44.9% for the 40 to 54 age group, and then decreasing to 33.5% for those aged 70 and older.

Income Group Differences in Obesity: The conditional probabilities reveal that the low-income group has the highest probability of being obese (41.8%), followed by the middle-income group (40.4%), and the high-income group (37.6%).

The Models

Multinomial Logistic Regression

library(VGAM)
## Loading required package: stats4
## Loading required package: splines
## 
## Attaching package: 'VGAM'
## The following objects are masked from 'package:mlogit':
## 
##     has.intercept, lrtest
## The following objects are masked from 'package:boot':
## 
##     logit, simplex
## The following object is masked from 'package:caret':
## 
##     predictors
## The following object is masked from 'package:car':
## 
##     logit
# Fit a multinomial logistic regression model
multinom_model <- vglm(bmi ~ hrtdisease + diabetic_status + age5cat + sex + edu + income_group + totinda + smokeday + genhlth, family = multinomial(refLevel = 4), data = filtered_data)

# Summary of the multinomial logistic regression model
summary(multinom_model)
## 
## Call:
## vglm(formula = bmi ~ hrtdisease + diabetic_status + age5cat + 
##     sex + edu + income_group + totinda + smokeday + genhlth, 
##     family = multinomial(refLevel = 4), data = filtered_data)
## 
## Coefficients: 
##                                  Estimate Std. Error z value Pr(>|z|)    
## (Intercept):1                  -2.4048836  0.2738714  -8.781  < 2e-16 ***
## (Intercept):2                   0.3469836  0.1053895   3.292 0.000993 ***
## (Intercept):3                   0.1628343  0.1023726   1.591 0.111698    
## hrtdiseaseNo:1                  0.1890897  0.1182309   1.599 0.109748    
## hrtdiseaseNo:2                  0.2441503  0.0464568   5.255 1.48e-07 ***
## hrtdiseaseNo:3                  0.0841481  0.0400669   2.100 0.035712 *  
## diabetic_status2:1              1.7364858  0.1311493  13.241  < 2e-16 ***
## diabetic_status2:2              1.2419499  0.0396102  31.354  < 2e-16 ***
## diabetic_status2:3              0.6141595  0.0314411  19.534  < 2e-16 ***
## age5catAge 30 to 39:1          -1.2300101  0.1436864  -8.560  < 2e-16 ***
## age5catAge 30 to 39:2          -0.5111406  0.0587848  -8.695  < 2e-16 ***
## age5catAge 30 to 39:3          -0.0548685  0.0619160  -0.886 0.375522    
## age5catAge 40 to 54:1          -1.5911949  0.1359982 -11.700  < 2e-16 ***
## age5catAge 40 to 54:2          -0.6783984  0.0556025 -12.201  < 2e-16 ***
## age5catAge 40 to 54:3           0.0329873  0.0580859   0.568 0.570100    
## age5catAge 55 to 69:1          -0.9617011  0.1221174  -7.875 3.40e-15 ***
## age5catAge 55 to 69:2          -0.3719874  0.0547527  -6.794 1.09e-11 ***
## age5catAge 55 to 69:3           0.3002255  0.0575674   5.215 1.84e-07 ***
## age5catAge 70 and older:1      -0.0007404  0.1309952  -0.006 0.995490    
## age5catAge 70 and older:2       0.3218763  0.0592592   5.432 5.58e-08 ***
## age5catAge 70 and older:3       0.7882089  0.0615636  12.803  < 2e-16 ***
## sexFemale:1                     0.3233321  0.0754030   4.288 1.80e-05 ***
## sexFemale:2                    -0.0091983  0.0270983  -0.339 0.734278    
## sexFemale:3                    -0.3652744  0.0250112 -14.604  < 2e-16 ***
## eduElementary or less:1         0.6573484  0.1949345   3.372 0.000746 ***
## eduElementary or less:2        -0.1387187  0.1050661  -1.320 0.186735    
## eduElementary or less:3        -0.1111001  0.0970766  -1.144 0.252434    
## eduHigh school or equivalent:1  0.2405442  0.0773762   3.109 0.001879 ** 
## eduHigh school or equivalent:2 -0.0325848  0.0297005  -1.097 0.272592    
## eduHigh school or equivalent:3  0.0153035  0.0275344   0.556 0.578351    
## income_groupMiddle Income:1    -0.3693724  0.0819891  -4.505 6.63e-06 ***
## income_groupMiddle Income:2    -0.1812428  0.0329446  -5.501 3.77e-08 ***
## income_groupMiddle Income:3    -0.0172501  0.0311541  -0.554 0.579782    
## income_groupHigh Income:1      -0.6049170  0.1191839  -5.075 3.86e-07 ***
## income_groupHigh Income:2      -0.2528280  0.0400332  -6.315 2.69e-10 ***
## income_groupHigh Income:3       0.0601733  0.0370133   1.626 0.104009    
## totinda2:1                     -0.1692019  0.0787452  -2.149 0.031656 *  
## totinda2:2                     -0.4930243  0.0301621 -16.346  < 2e-16 ***
## totinda2:3                     -0.3726183  0.0274927 -13.553  < 2e-16 ***
## smokeday2:1                     0.0606895  0.1082555   0.561 0.575061    
## smokeday2:2                     0.0224077  0.0482167   0.465 0.642125    
## smokeday2:3                     0.0778567  0.0466524   1.669 0.095143 .  
## smokeday3:1                    -1.0055988  0.0840500 -11.964  < 2e-16 ***
## smokeday3:2                    -0.5320842  0.0322583 -16.494  < 2e-16 ***
## smokeday3:3                    -0.3145317  0.0304770 -10.320  < 2e-16 ***
## genhlth2:1                     -0.6544326  0.1925174  -3.399 0.000675 ***
## genhlth2:2                     -0.5631199  0.0688547  -8.178 2.88e-16 ***
## genhlth2:3                     -0.2429530  0.0697862  -3.481 0.000499 ***
## genhlth3:1                     -1.1204722  0.1840688  -6.087 1.15e-09 ***
## genhlth3:2                     -1.2546673  0.0669237 -18.748  < 2e-16 ***
## genhlth3:3                     -0.7275233  0.0674643 -10.784  < 2e-16 ***
## genhlth4:1                     -0.8042856  0.1844020  -4.362 1.29e-05 ***
## genhlth4:2                     -1.3016736  0.0691119 -18.834  < 2e-16 ***
## genhlth4:3                     -0.9270128  0.0695222 -13.334  < 2e-16 ***
## genhlth5:1                     -0.1831667  0.1914507  -0.957 0.338703    
## genhlth5:2                     -0.9798165  0.0745895 -13.136  < 2e-16 ***
## genhlth5:3                     -0.8701905  0.0748015 -11.633  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Names of linear predictors: log(mu[,1]/mu[,4]), log(mu[,2]/mu[,4]), 
## log(mu[,3]/mu[,4])
## 
## Residual deviance: 87081.85 on 118596 degrees of freedom
## 
## Log-likelihood: -43540.93 on 118596 degrees of freedom
## 
## Number of Fisher scoring iterations: 7 
## 
## Warning: Hauck-Donner effect detected in the following estimate(s):
## '(Intercept):1'
## 
## 
## Reference group is level  4  of the response
# Load necessary library
library(VGAM)

# Extract coefficients
coef_multinom <- coef(multinom_model)

# Extract standard errors
std_err <- sqrt(diag(vcov(multinom_model)))
# Convert coefficients to a matrix with 3 columns (for 3 categories: Underweight, Normal Weight, Overweight)
coef_matrix <- matrix(coef_multinom, ncol = 3, byrow = TRUE)
std_err_matrix <- matrix(std_err, ncol = 3, byrow = TRUE)

# Name the columns appropriately
colnames(coef_matrix) <- c("Underweight", "Normal Weight", "Overweight")
colnames(std_err_matrix) <- c("Underweight", "Normal Weight", "Overweight")

# Convert to data frames for easier manipulation
coef_df <- as.data.frame(coef_matrix)
std_err_df <- as.data.frame(std_err_matrix)

# Extract predictor names from the row names of the coefficients matrix
predictor_names <- rownames(coef(multinom_model))

# Assign predictor names to the data frames
rownames(coef_df) <- predictor_names
rownames(std_err_df) <- predictor_names
# Calculate odds ratios and confidence intervals
odds_ratios <- exp(coef_df)
conf_int_lower <- exp(coef_df - 1.96 * std_err_df)
conf_int_upper <- exp(coef_df + 1.96 * std_err_df)
# Convert to long format for plotting
library(reshape2)
## 
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
## 
##     smiths
# Adding a predictor column for melting
odds_ratios$Predictor <- rownames(odds_ratios)
conf_int_lower$Predictor <- rownames(conf_int_lower)
conf_int_upper$Predictor <- rownames(conf_int_upper)

# Melting the data frames
odds_ratios_long <- melt(odds_ratios, id.vars = "Predictor", variable.name = "BMI_Category", value.name = "Odds_Ratio")
conf_int_lower_long <- melt(conf_int_lower, id.vars = "Predictor", variable.name = "BMI_Category", value.name = "CI_Lower")
conf_int_upper_long <- melt(conf_int_upper, id.vars = "Predictor", variable.name = "BMI_Category", value.name = "CI_Upper")
# Merge the data frames
conf_int_df <- merge(odds_ratios_long, conf_int_lower_long, by = c("Predictor", "BMI_Category"))
conf_int_df <- merge(conf_int_df, conf_int_upper_long, by = c("Predictor", "BMI_Category"))
library(ggplot2)

# Plot
ggplot(conf_int_df, aes(x = Odds_Ratio, y = Predictor, color = BMI_Category)) +
  geom_point(size = 2) +
  geom_errorbarh(aes(xmin = CI_Lower, xmax = CI_Upper), height = 0.2) +
  theme_minimal() +
  labs(title = "Forest Plot of Odds Ratios with Confidence Intervals", x = "Odds Ratio", y = "Predictor") +
  scale_x_continuous(trans = 'log10') +
  facet_wrap(~ BMI_Category, scales = "free_y")

# Load necessary libraries
library(VGAM)
library(reshape2)
library(ggplot2)

# Extract coefficients and standard errors
coef_multinom <- coef(multinom_model)
std_err <- sqrt(diag(vcov(multinom_model)))

# Reshape coefficients and standard errors into matrices
coef_matrix <- matrix(coef_multinom, ncol = 3, byrow = TRUE)
std_err_matrix <- matrix(std_err, ncol = 3, byrow = TRUE)

# Name the columns appropriately
colnames(coef_matrix) <- c("Underweight", "Normal Weight", "Overweight")
colnames(std_err_matrix) <- c("Underweight", "Normal Weight", "Overweight")

# Convert to data frames for easier manipulation
coef_df <- as.data.frame(coef_matrix)
std_err_df <- as.data.frame(std_err_matrix)

# Define predictor names
predictor_names <- c("(Intercept)", "Heart Disease", "Diabetes Status", "Age 30 to 39", "Age 40 to 54", 
                     "Age 55 to 69", "Age 70 and older", "Sex", "Elementary Education", "High School Education", 
                     "Middle Income", "High Income", "Physical Activity", "Smokeday2", "Smokeday3", 
                     "General Health 2", "General Health 3", "General Health 4", "General Health 5")

# Assign predictor names to the data frames
rownames(coef_df) <- predictor_names
rownames(std_err_df) <- predictor_names

# Calculate odds ratios and confidence intervals
odds_ratios <- exp(coef_df)
conf_int_lower <- exp(coef_df - 1.96 * std_err_df)
conf_int_upper <- exp(coef_df + 1.96 * std_err_df)

# Adding a predictor column for melting
odds_ratios$Predictor <- rownames(odds_ratios)
conf_int_lower$Predictor <- rownames(conf_int_lower)
conf_int_upper$Predictor <- rownames(conf_int_upper)

# Melting the data frames
odds_ratios_long <- melt(odds_ratios, id.vars = "Predictor", variable.name = "BMI_Category", value.name = "Odds_Ratio")
conf_int_lower_long <- melt(conf_int_lower, id.vars = "Predictor", variable.name = "BMI_Category", value.name = "CI_Lower")
conf_int_upper_long <- melt(conf_int_upper, id.vars = "Predictor", variable.name = "BMI_Category", value.name = "CI_Upper")

# Merge the data frames
conf_int_df <- merge(odds_ratios_long, conf_int_lower_long, by = c("Predictor", "BMI_Category"))
conf_int_df <- merge(conf_int_df, conf_int_upper_long, by = c("Predictor", "BMI_Category"))

# Function to create individual plots for each BMI category with different colors
create_forest_plot <- function(df, category, color) {
  ggplot(df[df$BMI_Category == category, ], aes(x = Odds_Ratio, y = Predictor)) +
    geom_point(size = 2, color = color) +
    geom_errorbarh(aes(xmin = CI_Lower, xmax = CI_Upper), height = 0.2, color = color) +
    theme_minimal() +
    labs(title = paste("Forest Plot of Odds Ratios with Confidence Intervals\n", category), 
         x = "Odds Ratio", y = "Predictor") +
    scale_x_continuous(trans = 'log10') +
    theme(axis.text.y = element_text(size = 10, angle = 0, hjust = 1)) +
    theme(legend.position = "none")
}

# Create individual plots with different colors
plot_underweight <- create_forest_plot(conf_int_df, "Underweight", "red")
plot_normal_weight <- create_forest_plot(conf_int_df, "Normal Weight", "darkgreen")
plot_overweight <- create_forest_plot(conf_int_df, "Overweight", "blue2")

# Display individual plots
print(plot_underweight)

print(plot_normal_weight)

print(plot_overweight)

Ordinal Logistic Regression

# Fit Model 1 (Age, Gender, Marital, Education)
fit1 <- vglm(bmi ~ age5cat + sex + marital + edu, family = cumulative(parallel = TRUE), data = filtered_data)

# Fit Model 2 (Add health variables)
fit2 <- vglm(bmi ~ age5cat + sex + marital + edu + genhlth + hrtdisease + diabetic_status + smokeday, family = cumulative(parallel = TRUE), data = filtered_data)

# Fit Model 3 (Add Employment Status and income)
fit3 <- vglm(bmi ~ age5cat + sex + marital + edu + genhlth + hrtdisease + diabetic_status + smokeday + income_group + employstatus, family = cumulative(parallel = TRUE), data = filtered_data)

# Calculate AIC and BIC for all three models
aic_fit1 <- AIC(fit1)
aic_fit2 <- AIC(fit2)
aic_fit3 <- AIC(fit3)

bic_fit1 <- BIC(fit1)
bic_fit2 <- BIC(fit2)
bic_fit3 <- BIC(fit3)

# Print AIC and BIC values for comparison
cat("Model 1 AIC:", aic_fit1, "\n")
## Model 1 AIC: 91360.78
cat("Model 2 AIC:", aic_fit2, "\n")
## Model 2 AIC: 88514.37
cat("Model 3 AIC:", aic_fit3, "\n")
## Model 3 AIC: 88376.06
cat("Model 1 BIC:", bic_fit1, "\n")
## Model 1 BIC: 91489.56
cat("Model 2 BIC:", bic_fit2, "\n")
## Model 2 BIC: 88711.83
cat("Model 3 BIC:", bic_fit3, "\n")
## Model 3 BIC: 88650.79
# Determine the best model based on AIC and BIC
if (aic_fit1 < aic_fit2 && aic_fit1 < aic_fit3 && bic_fit1 < bic_fit2 && bic_fit1 < bic_fit3) {
    cat("Model 1 is better based on AIC and BIC.\n")
    summary(fit1)
} else if (aic_fit2 < aic_fit1 && aic_fit2 < aic_fit3 && bic_fit2 < bic_fit1 && bic_fit2 < bic_fit3) {
    cat("Model 2 is better based on AIC and BIC.\n")
    summary(fit2)
} else {
    cat("Model 3 is better based on AIC and BIC.\n")
    summary(fit3)
}
## Model 3 is better based on AIC and BIC.
## 
## Call:
## vglm(formula = bmi ~ age5cat + sex + marital + edu + genhlth + 
##     hrtdisease + diabetic_status + smokeday + income_group + 
##     employstatus, family = cumulative(parallel = TRUE), data = filtered_data)
## 
## Coefficients: 
##                                              Estimate Std. Error z value
## (Intercept):1                                -3.69759    0.08574 -43.126
## (Intercept):2                                -0.72634    0.07975  -9.107
## (Intercept):3                                 0.71596    0.07981   8.971
## age5catAge 30 to 39                          -0.46114    0.04446 -10.373
## age5catAge 40 to 54                          -0.59425    0.04355 -13.647
## age5catAge 55 to 69                          -0.36814    0.04540  -8.109
## age5catAge 70 and older                       0.05793    0.05312   1.090
## sexFemale                                    -0.03967    0.01962  -2.022
## maritalDivorced                               0.12163    0.02721   4.471
## maritalWidowed                                0.16147    0.03507   4.604
## maritalSeparated                              0.12488    0.05356   2.332
## maritalNever married                          0.18672    0.03005   6.213
## maritalMember of an unmarried couple          0.19952    0.04348   4.589
## eduElementary or less                        -0.02087    0.07386  -0.283
## eduHigh school or equivalent                 -0.01607    0.02122  -0.757
## genhlth2                                     -0.36019    0.04707  -7.652
## genhlth3                                     -0.85422    0.04581 -18.646
## genhlth4                                     -0.94138    0.04757 -19.791
## genhlth5                                     -0.71036    0.05184 -13.704
## hrtdiseaseNo                                  0.15178    0.03198   4.746
## diabetic_status2                              0.93148    0.02606  35.745
## smokeday2                                     0.04171    0.03452   1.208
## smokeday3                                    -0.40383    0.02314 -17.450
## income_groupMiddle Income                    -0.08259    0.02559  -3.227
## income_groupHigh Income                      -0.04537    0.03275  -1.385
## employstatusSelf-employed                     0.36847    0.03865   9.535
## employstatusOut of work for 1 year or more    0.25824    0.05226   4.941
## employstatusOut of work for less than 1 year  0.13620    0.05636   2.417
## employstatusA homemaker                       0.22110    0.05511   4.012
## employstatusA student                         0.31627    0.09035   3.501
## employstatusRetired                           0.20287    0.03402   5.963
## employstatusUnable to work                    0.03510    0.03362   1.044
##                                              Pr(>|z|)    
## (Intercept):1                                 < 2e-16 ***
## (Intercept):2                                 < 2e-16 ***
## (Intercept):3                                 < 2e-16 ***
## age5catAge 30 to 39                           < 2e-16 ***
## age5catAge 40 to 54                           < 2e-16 ***
## age5catAge 55 to 69                          5.11e-16 ***
## age5catAge 70 and older                      0.275551    
## sexFemale                                    0.043223 *  
## maritalDivorced                              7.80e-06 ***
## maritalWidowed                               4.15e-06 ***
## maritalSeparated                             0.019727 *  
## maritalNever married                         5.21e-10 ***
## maritalMember of an unmarried couple         4.46e-06 ***
## eduElementary or less                        0.777548    
## eduHigh school or equivalent                 0.448844    
## genhlth2                                     1.98e-14 ***
## genhlth3                                      < 2e-16 ***
## genhlth4                                      < 2e-16 ***
## genhlth5                                      < 2e-16 ***
## hrtdiseaseNo                                 2.08e-06 ***
## diabetic_status2                              < 2e-16 ***
## smokeday2                                    0.226869    
## smokeday3                                     < 2e-16 ***
## income_groupMiddle Income                    0.001249 ** 
## income_groupHigh Income                      0.165965    
## employstatusSelf-employed                     < 2e-16 ***
## employstatusOut of work for 1 year or more   7.75e-07 ***
## employstatusOut of work for less than 1 year 0.015662 *  
## employstatusA homemaker                      6.03e-05 ***
## employstatusA student                        0.000464 ***
## employstatusRetired                          2.48e-09 ***
## employstatusUnable to work                   0.296433    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Names of linear predictors: logitlink(P[Y<=1]), logitlink(P[Y<=2]), 
## logitlink(P[Y<=3])
## 
## Residual deviance: 88312.06 on 118621 degrees of freedom
## 
## Log-likelihood: -44156.03 on 118621 degrees of freedom
## 
## Number of Fisher scoring iterations: 5 
## 
## No Hauck-Donner effect found in any of the estimates
## 
## 
## Exponentiated coefficients:
##                          age5catAge 30 to 39 
##                                    0.6305625 
##                          age5catAge 40 to 54 
##                                    0.5519764 
##                          age5catAge 55 to 69 
##                                    0.6920195 
##                      age5catAge 70 and older 
##                                    1.0596363 
##                                    sexFemale 
##                                    0.9611102 
##                              maritalDivorced 
##                                    1.1293322 
##                               maritalWidowed 
##                                    1.1752334 
##                             maritalSeparated 
##                                    1.1330172 
##                         maritalNever married 
##                                    1.2052865 
##         maritalMember of an unmarried couple 
##                                    1.2208138 
##                        eduElementary or less 
##                                    0.9793491 
##                 eduHigh school or equivalent 
##                                    0.9840568 
##                                     genhlth2 
##                                    0.6975454 
##                                     genhlth3 
##                                    0.4256160 
##                                     genhlth4 
##                                    0.3900893 
##                                     genhlth5 
##                                    0.4914685 
##                                 hrtdiseaseNo 
##                                    1.1639074 
##                             diabetic_status2 
##                                    2.5382754 
##                                    smokeday2 
##                                    1.0425929 
##                                    smokeday3 
##                                    0.6677603 
##                    income_groupMiddle Income 
##                                    0.9207249 
##                      income_groupHigh Income 
##                                    0.9556413 
##                    employstatusSelf-employed 
##                                    1.4455167 
##   employstatusOut of work for 1 year or more 
##                                    1.2946513 
## employstatusOut of work for less than 1 year 
##                                    1.1459120 
##                      employstatusA homemaker 
##                                    1.2474479 
##                        employstatusA student 
##                                    1.3719977 
##                          employstatusRetired 
##                                    1.2249103 
##                   employstatusUnable to work 
##                                    1.0357211
# Extract intercepts from the best model (fit3)
intercepts <- coef(fit3)[grepl("Intercept", names(coef(fit3)))]
intercept_names <- names(intercepts)

# Create a data frame for plotting
intercept_data <- data.frame(
  Category = factor(intercept_names, levels = intercept_names),
  Estimate = intercepts
)

# Plot the intercepts
library(ggplot2)

ggplot(intercept_data, aes(x = Category, y = Estimate)) +
  geom_point(color = "blue", size = 3) +
  geom_line(group = 1, color = "blue", linetype = "dashed") +
  labs(title = "Intercepts for BMI Categories",
       x = "BMI Category Thresholds",
       y = "Intercept Estimate") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Load necessary libraries
library(VGAM)
library(ggplot2)
library(dplyr)

# Extract coefficients
coef_values <- coef(fit3)
intercepts <- coef_values[grepl("Intercept", names(coef_values))]
betas <- coef_values[!grepl("Intercept", names(coef_values))]
beta_names <- names(betas)

# Create a data frame for plotting betas with different intercepts
plot_data <- data.frame(
  Coefficient = rep(beta_names, 3),
  Estimate = rep(betas, 3),
  Category = rep(c("Underweight", "Normal_weight", "Overweight"), each = length(betas)),
  Intercept = rep(intercepts, each = length(betas))
)

# Calculate the y-values for the lines with different intercepts
plot_data$Fitted_Value <- plot_data$Estimate + plot_data$Intercept

# Plot
ggplot(plot_data, aes(x = Coefficient, y = Fitted_Value, color = Category, group = Category)) +
  geom_point(size = 3) +
  geom_line() +
  labs(title = "Betas for BMI Categories with Different Intercepts",
       x = "Predictor",
       y = "Fitted Value") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

LS0tDQp0aXRsZTogfCANCiAgPGNlbnRlcj4gSGVhbHRoIERhdGEgU2NpZW5jZSBQcm9qZWN0IDwvY2VudGVyPg0Kc3VidGl0bGU6IHwgDQogIDxjZW50ZXI+IEZhY3RvcnMgaW5mbHVlbmNpbmcgb2Jlc2l0eSBhbW9uZyBhZHVsdHMgaW4gdGhlIFVTIDwvY2VudGVyPg0KYXV0aG9yOiB8IA0KICA8Y2VudGVyPiBBbWFsIEFobWVkIC0gOTA0MTQ2IDwvY2VudGVyPg0KICA8Y2VudGVyPiBBcm5lbGEgSGFsaWxpIC0gODg3NzQ0IDwvY2VudGVyPg0KICA8Y2VudGVyPiBNYWhhIEdhc2ltIC0gODk4Mzk2IDwvY2VudGVyPg0KZGF0ZTogfCANCiAgPGNlbnRlcj5gciBTeXMuRGF0ZSgpYDwvY2VudGVyPg0Kb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQNCi0tLQ0KDQoNCiMgUHJvYmxlbSBkZWZpbml0aW9uDQoNCj4gSW4gdGhpcyBwcm9qZWN0IHdlIGFyZSB0cnlpbmcgdG8gaWRlbnRpZnkgdGhlIHZhcmlvdXMgZGV0ZXJtaW5hbnRzIGNvbnRyaWJ1dGluZyB0byB0aGUgcHJldmFsZW5jZSBvZiBvYmVzaXR5IGFtb25nIGFkdWx0cyBpbiB0aGUgVW5pdGVkIFN0YXRlcywgaW5jbHVkaW5nIHNvY2lhbCwgZWNvbm9taWMsIGN1bHR1cmFsLCBlbnZpcm9ubWVudGFsLCBhbmQgYmVoYXZpb3JhbCBmYWN0b3JzIDxicj4gDQoNCiMgQWJzdHJhY3QgYW5kIEludHJvZHVjdGlvbg0KDQo+IFdlIHdpbGwgdXNlIHRoZSBCUkZTUyAoQmVoYXZpb3JhbCBSaXNrIEZhY3RvciBTdXJ2ZWlsbGFuY2UgU3lzdGVtKSBkYXRhc2V0LiBJdCBpcyBhIGNyb3NzLXNlY3Rpb25hbCB0ZWxlcGhvbmUgc3VydmV5IHRoYXQgc3RhdGUgaGVhbHRoIGRlcGFydG1lbnRzIGNvbmR1Y3QgbW9udGhseSBvdmVyIGxhbmRsaW5lIHRlbGVwaG9uZXMgYW5kIGNlbGx1bGFyIHRlbGVwaG9uZXMgd2l0aCBhIHN0YW5kYXJkaXNlZCBxdWVzdGlvbm5haXJlIGFuZCB0ZWNobmljYWwgYW5kIG1ldGhvZG9sb2dpYyBhc3Npc3RhbmNlIGZyb20gdGhlIENEQyAoQ2VudGVycyBmb3IgRGlzZWFzZSBDb250cm9sIGFuZCBQcmV2ZW50aW9uKS4NCkJSRlNTIGlzIHVzZWQgdG8gY29sbGVjdCBwcmV2YWxlbmNlIGRhdGEgYW1vbmcgVS5TLiByZXNpZGVudHMgcmVnYXJkaW5nIHRoZWlyIHJpc2sgYmVoYXZpb3VycyBhbmQgcHJldmVudGl2ZSBoZWFsdGggcHJhY3RpY2VzIHRoYXQgY2FuIGFmZmVjdCB0aGVpciBoZWFsdGggc3RhdHVzLiBXZSBkZWNpZGVkIG9uIHRoZSB0b3BpYyBvZiB0aGUgZmFjdG9ycyB0aGF0IGluZmx1ZW5jZSBvYmVzaXR5IGFtb25nIGFkdWx0cyBpbiB0aGUgVS5TLiBTdHVkeWluZyB0aGUgZmFjdG9ycyBpbmZsdWVuY2luZyBvYmVzaXR5IGFtb25nIGFkdWx0cyBpbiB0aGUgVS5TLiBpcyBjcnVjaWFsLg0KT2Jlc2l0eSBpcyBhIHNpZ25pZmljYW50IHB1YmxpYyBoZWFsdGggaXNzdWUgaW4gdGhlIFUuUy4sIGNvbnRyaWJ1dGluZyB0byB2YXJpb3VzIGNocm9uaWMgZGlzZWFzZXMgbGlrZSBoZWFydCBkaXNlYXNlLCBkaWFiZXRlcywgYW5kIGNlcnRhaW4gY2FuY2Vycy4gVW5kZXJzdGFuZGluZyB0aGUgZmFjdG9ycyBjb250cmlidXRpbmcgdG8gb2Jlc2l0eSBjYW4gaW5mb3JtIHB1YmxpYyBoZWFsdGggcG9saWNpZXMgYW5kIGludGVydmVudGlvbnMgYWltZWQgYXQgcHJldmVudGlvbiBhbmQgbWFuYWdlbWVudC4gT2Jlc2l0eS1yZWxhdGVkIGhlYWx0aGNhcmUgY29zdHMgYXJlIHN1YnN0YW50aWFsLCBpbXBhY3RpbmcgYm90aCBpbmRpdmlkdWFscyBhbmQgdGhlIGhlYWx0aGNhcmUgc3lzdGVtIGFzIGEgd2hvbGUuIA0KPGJyPiA8YnI+IA0KV0hZIElTIElUIElNUE9SVEFOVCBUTyBLTk9XIEFCT1VUIE9CRVNJVFk/DQpCeSBpZGVudGlmeWluZyB0aGUgZmFjdG9ycyBpbmZsdWVuY2luZyBvYmVzaXR5LCBoZWFsdGhjYXJlIHByb3ZpZGVycyBjYW4gZGV2ZWxvcCBtb3JlIGVmZmVjdGl2ZSBzdHJhdGVnaWVzIGZvciBwcmV2ZW50aW9uIGFuZCB0cmVhdG1lbnQsIHBvdGVudGlhbGx5IHJlZHVjaW5nIGhlYWx0aGNhcmUgZXhwZW5kaXR1cmVzLiBPYmVzaXR5IGNhbiBoYXZlIHByb2ZvdW5kIHNvY2lhbCBhbmQgZWNvbm9taWMgY29uc2VxdWVuY2VzLCBpbmNsdWRpbmcgcmVkdWNlZCBxdWFsaXR5IG9mIGxpZmUsIGRpc2NyaW1pbmF0aW9uLCBhbmQgZGVjcmVhc2VkIHByb2R1Y3Rpdml0eS4gRXhwbG9yaW5nIHRoZSBmYWN0b3JzIGJlaGluZCBvYmVzaXR5IGNhbiBoZWxwIGFkZHJlc3MgdW5kZXJseWluZyBzb2NpZXRhbCBpc3N1ZXMgc3VjaCBhcyBhY2Nlc3MgdG8gaGVhbHRoeSBmb29kcywgc29jaW9lY29ub21pYyBkaXNwYXJpdGllcywgYW5kIGN1bHR1cmFsIGluZmx1ZW5jZXMuIA0KPGJyPiA8YnI+IA0KUmVzZWFyY2hpbmcgdGhlIGZhY3RvcnMgaW5mbHVlbmNpbmcgb2Jlc2l0eSBmb3N0ZXJzIGlubm92YXRpb24gaW4gaGVhbHRoY2FyZSBhbmQgcHVibGljIGhlYWx0aC4gSXQgZW5jb3VyYWdlcyB0aGUgZGV2ZWxvcG1lbnQgb2YgbmV3IHRlY2hub2xvZ2llcywgaW50ZXJ2ZW50aW9ucywgYW5kIHBvbGljaWVzIGFpbWVkIGF0IGNvbWJhdGluZyBvYmVzaXR5IGFuZCBwcm9tb3RpbmcgaGVhbHRoaWVyIGxpZmVzdHlsZXMuIE92ZXJhbGwsIGludmVzdGlnYXRpbmcgdGhlIGZhY3RvcnMgaW5mbHVlbmNpbmcgb2Jlc2l0eSBhbW9uZyBhZHVsdHMgaW4gdGhlIFUuUy4gaXMgZXNzZW50aWFsIGZvciBpbXByb3ZpbmcgcHVibGljIGhlYWx0aCBvdXRjb21lcywgcmVkdWNpbmcgaGVhbHRoY2FyZSBjb3N0cywgYWRkcmVzc2luZyBzb2NpYWwgYW5kIGVjb25vbWljIGRpc3Bhcml0aWVzLCBwcm9tb3RpbmcgaW5kaXZpZHVhbCB3ZWxsLWJlaW5nLCBhbmQgZHJpdmluZyBpbm5vdmF0aW9uIGluIGhlYWx0aGNhcmUgYW5kIHB1YmxpYyBoZWFsdGggcHJhY3RpY2VzLlRoaXMgaXMgYSBmdW5kYW1lbnRhbCBwdWJsaWMgaGVhbHRoIHF1ZXN0aW9uIGFuZCBjYW4gcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHByZXZlbnRpdmUgc3RyYXRlZ2llcyBhbmQgcG9saWN5bWFraW5nLiANCjxicj4gPGJyPiANCkhvdyBkbyB3ZSBtZWFzdXJlIG9iZXNpdHkgaW4gc2ltcGxlIHRlcm1zPw0KT2Jlc2l0eSBpcyB0eXBpY2FsbHkgZGVmaW5lZCBieSB0aGUgQm9keSBNYXNzIEluZGV4IChCTUkpIHdpdGggYSB0aHJlc2hvbGQgdmFsdWUgKGUuZy4sIEJNSSDiiaUgMzAgYXMgb2Jlc2UpLiBIZXJlJ3MgYSBkZXRhaWxlZCBicmVha2Rvd24gb2YgaG93IHlvdSBtaWdodCBhcHByb2FjaCBhbmFseXppbmcgdGhpcyBxdWVzdGlvbiB1c2luZyBkaWZmZXJlbnQgc3RhdGlzdGljYWwgbW9kZWxzLCBzcGVjaWZ5aW5nIHBvdGVudGlhbCB2YXJpYWJsZXMgYW5kIG9mZmVyaW5nIHNvbWUgYW5hbHl0aWNhbCB0aXBzLjxicj4gPGJyPiANCg0KDQojIFVwbG9hZGluZyBhbmQgcHJlcGFyaW5nIHRoZSBkYXRhIHNldA0KDQpXZSB3aWxsIHVwbG9hZCBhbGwgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXMgd2Ugd2lsbCB1c2UgaW4gb3VyIGFuYWx5c2lzIGhlcmUuDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShncGxvdHMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KG1hcHMpDQpsaWJyYXJ5KGNhcikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KE1BU1MpDQpsaWJyYXJ5KGJvb3QpDQpsaWJyYXJ5KGdlZXBhY2spDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShubmV0KQ0KbGlicmFyeShtbG9naXQpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBTcGVjaWZ5IHRoZSBwYXRoIHRvIHlvdXIgY3N2IGZpbGUNCmZpbGVfcGF0aCA8LSAiRGVtbytIZWFsdGggZGF0YS54bHN4Ig0KYGBgDQoNCg0KIyMgTG9hZGluZyB0aGUgZGF0ZSANCg0KYGBge3J9DQojIFJlYWQgdGhlIEV4Y2VsIGZpbGUNCmRhdGEgPC0gcmVhZF9leGNlbChmaWxlX3BhdGgpDQpoZWFkKGRhdGEpDQpgYGANCg0KYGBge3J9DQpkaW0oZGF0YSkNCmBgYA0KV2Ugc2VlIHRoYXQgd2UgaGF2ZSA0NDUgMTMyIG9ic2VydmF0aW9ucywgd2hpY2ggaXMgZ29vZCB0byB3b3JrIG9uIA0KYSBiaWcgZGF0YSBwcm9qZWN0LiBTbyBsZXQgdXMgY2Fycnkgb24uIEhPUEUgWU9VIFdJTEwgRU5KT1kgT1VSIFJJREUgOikgIQ0KDQoNCiMjIERhdGEgQ2xlYW5pbmcNCg0KYGBge3J9DQojIEdldCB0aGUgY29sdW1uIG5hbWVzDQpjb2x1bW5fbmFtZXMgPC0gbmFtZXMoZGF0YSkNCg0KIyBHZXQgdGhlIGRhdGEgdHlwZXMgb2YgZWFjaCBjb2x1bW4NCmRhdGFfdHlwZXMgPC0gc2FwcGx5KGRhdGEsIGNsYXNzKQ0KDQojIEdldCB0aGUgbnVtYmVyIG9mIG5vbi1udWxsIHZhbHVlcyBpbiBlYWNoIGNvbHVtbg0Kbm9uX251bGxfY291bnRzIDwtIHNhcHBseShkYXRhLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkNCg0KIyBDcmVhdGUgYSBzdW1tYXJ5IGRhdGEgZnJhbWUNCnN1bW1hcnlfZGF0YSA8LSBkYXRhLmZyYW1lKGNvbHVtbl9uYW1lcywgZGF0YV90eXBlcywgbm9uX251bGxfY291bnRzKQ0KDQojIFJlbmFtZSB0aGUgY29sdW1ucyBvZiB0aGUgc3VtbWFyeSBkYXRhIGZyYW1lDQpjb2xuYW1lcyhzdW1tYXJ5X2RhdGEpIDwtIGMoIkNvbHVtbiBOYW1lIiwgIkRhdGEgVHlwZSIsICJOb24tTnVsbCBDb3VudCIpDQoNCiMgUHJpbnQgdGhlIHN1bW1hcnkgZGF0YSBmcmFtZQ0KcHJpbnQoc3VtbWFyeV9kYXRhKQ0KI3dlIHNlZSB0aGF0IHdlIGhhdmUgdGhlIHNhbWUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucw0KI25vbiBudWxsIGRhdGEsIHNvIHdlIGRvIG5vdCBoYXZlIG1pc3NpbmcgdmFsdWVzLg0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgR2V0IHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4NCm1pc3NpbmdfY291bnRzIDwtIGNvbFN1bXMoaXMubmEoZGF0YSkpDQoNCiMgU29ydCB0aGUgbWlzc2luZyB2YWx1ZSBjb3VudHMgaW4gZGVzY2VuZGluZyBvcmRlcg0KbWlzc2luZ19jb3VudHMgPC0gc29ydChtaXNzaW5nX2NvdW50cywgZGVjcmVhc2luZyA9IFRSVUUpDQoNCiMgRmlsdGVyIG91dCBjb2x1bW5zIHdpdGggbm8gbWlzc2luZyB2YWx1ZXMNCm1pc3NpbmdfY291bnRzIDwtIG1pc3NpbmdfY291bnRzW21pc3NpbmdfY291bnRzID4gMF0NCg0KIyBQcmludCB0aGUgcmVzdWx0DQpwcmludChtaXNzaW5nX2NvdW50cykNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBmb3IgaW5kZXgNCmRhdGEkaW5kZXggPC0gc2VxX2xlbihucm93KGRhdGEpKQ0KDQojIFByaW50IHRoZSBkYXRhZnJhbWUgd2l0aCB0aGUgbmV3IGluZGV4DQpwcmludChkYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZW1vdmUgcm93cyB3aXRoIG51bGwgdmFsdWVzDQpkYXRhIDwtIG5hLm9taXQoZGF0YSkNCg0KIyBQcmludCB0aGUgZGF0YWZyYW1lIGFmdGVyIHJlbW92aW5nIG51bGwgdmFsdWVzDQpwcmludChkYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KI05ldyBkaW1lbnNpb24gYWZ0ZXIgZGVsZXRpbmcgdGhlIG1pc3NpbmcgdmFsdWVzDQpkaW0oZGF0YSkNCmBgYA0KDQpgYGB7cn0NCiMgUmVfaW5kZXgNCmRhdGEkaW5kZXggPC0gcGFzdGUwKCJyb3ciLCBzZXFfbGVuKG5yb3coZGF0YSkpKQ0KYGBgDQoNCg0KV2Ugd2lsbCByZW1vdmUgdGhlIHZhbHVlcyBmb3IgdGhlIHF1ZXN0aW9ucyB3aGVuIHRoZSByZXNwb25kZW50cyBsZXQgaXQgYmxhbmsgYW5kIHRoZXkgYXJlIGNvZGVkIHVuZGVyIGEgbnVtYmVyLCBpbiB0aGlzIGNhc2UgdGhlIGBvbWlgIGZ1bmN0aW9uIGNhbiBub3QgZGVsZXRlIHRoZW0gc28gd2UgaGFkIHRvIGRvIGl0IG1hbnVhbGx5IGZvciBlYWNoIHZhcmlhYmVsIGZvbGxvd2luZyB0aGUgZGF0YXNldCdzIGNvZGVib29rLg0KYGBge3J9DQojIERlZmluZSB0aGUgY29sdW1ucyBhbmQgdmFsdWVzIHRvIHJlbW92ZQ0KY29sdW1uc190b19yZW1vdmUgPC0gYygnZ2VuaGx0aCcsICdwb29yaGx0aCcsICdjdmRjcmhkNCcsICdkaWFiZXRlNCcsICdtYXJpdGFsJywgDQogICAgICAgICAgICAgICAgICAgICAgICdlZHVjYScsICdlbXBsb3kxJywgJ2luY29tZTMnLCAnc21va2RheTInLCAnX3RvdGluZGEnLCAnX3NleCcsICdfYWdlZzV5cicpDQoNCiNMaXN0IHdpdGggdmFsdWVzIHRvIHJlbW92ZQ0KdmFsdWVzX3RvX3JlbW92ZSA8LSBsaXN0KA0KICBnZW5obHRoID0gYyg5LCA3KSwgIA0KICBwb29yaGx0aCA9IGMoODgsIDc3LCA5OSksICANCiAgY3ZkY3JoZDQgPSBjKDcsIDkpLCAgICAgICAgDQogIGRpYWJldGU0ID0gYyg3LCA5KSwgICAgICAgIA0KICBtYXJpdGFsID0gYyg5KSwgICAgICAgICAgICANCiAgZWR1Y2EgPSBjKDkpLCAgICAgICAgICAgICAgDQogIGVtcGxveTEgPSBjKDkpLCAgICAgICAgICAgIA0KICBpbmNvbWUzID0gYyg3NywgOTkpLCAgICAgICANCiAgc21va2RheTIgPSBjKDcsIDkpLCAgICAgICANCiAgIl90b3RpbmRhIiA9IGMoOSksICAgICAgICANCiAgIl9hZ2VnNXlyIj0gYygxNCkgICAgICAgIA0KKQ0KDQojIEZ1bmN0aW9uIHRvIGZpbHRlciBvdXQgcm93cyB3aXRoIHNwZWNpZmljIHZhbHVlcyBpbiBlYWNoIGNvbHVtbg0KZmlsdGVyX3ZhbHVlcyA8LSBmdW5jdGlvbihkYXRhLCBjb2x1bW5fbmFtZSwgdmFsdWVzKSB7DQogIGRhdGEgJT4lDQogICAgZmlsdGVyKCEoISFybGFuZzo6c3ltKGNvbHVtbl9uYW1lKSAlaW4lIHZhbHVlcykpDQp9DQoNCiMgQXBwbHkgdGhlIGZ1bmN0aW9uIHRvIGVhY2ggY29sdW1uDQpmaWx0ZXJlZF9kYXRhIDwtIGRhdGENCmZvciAoY29sdW1uX25hbWUgaW4gY29sdW1uc190b19yZW1vdmUpIHsNCiAgZmlsdGVyZWRfZGF0YSA8LSBmaWx0ZXJfdmFsdWVzKGZpbHRlcmVkX2RhdGEsIGNvbHVtbl9uYW1lLCB2YWx1ZXNfdG9fcmVtb3ZlW1tjb2x1bW5fbmFtZV1dKQ0KfQ0KDQojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgZmlsdGVyZWQgZGF0YSBmcmFtZQ0KcHJpbnQoaGVhZChmaWx0ZXJlZF9kYXRhKSkNCmBgYA0KDQoNCmBgYHtyfQ0KI1RoZSBkaW1lbnNpb24gYWZ0ZXIgZGVsZXRpbmcgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggYSBzcGVjaWZpYyBudW1iZXINCmRpbShmaWx0ZXJlZF9kYXRhKQ0KYGBgDQpXZSB3aWxsIGNoYW5nZSB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMsIHNvIGl0IHdpbGwgYmUgZWFzaWVyIGZvciB1cyB0byB1bmRlcnN0YW5kIGFuZCB3b3JrIHdpdGguDQoNCmBgYHtyfQ0KbmFtZXMoZmlsdGVyZWRfZGF0YSkNCiMgQ2hhbmdlIGNvbHVtbiBuYW1lcw0KbmFtZXMoZmlsdGVyZWRfZGF0YSkgPC0gYygic3RhdGUiLCAiZ2VuaGx0aCIsICJwb29yaGx0aCIsICJocnRkaXNlYXNlIiwJImRpYWJldGljIiwJIm1hcml0YWwiLCAiZWR1IiwJImVtcGxveXN0YXR1cyIsICJpbmNvbWUiLCAic21va2VkYXkiLCAidG90aW5kYSIsICJzZXgiLCAiYWdlNWNhdCIsICJibWk1IiwgImJtaSIsICJpbmRleCIsICJvYmVzaXR5X3N0YXR1cyIpDQoNCiMgUHJpbnQgbmV3IGNvbHVtbiBuYW1lcw0KcHJpbnQobmFtZXMoZmlsdGVyZWRfZGF0YSkpIA0KYGBgDQoNCg0KYGBge3J9DQpzdW1tYXJ5KGZpbHRlcmVkX2RhdGEpDQpgYGANCldlIHdpbGwgbWFrZSBsZXNzIGNhdGVnb3JpZXMgZm9yIHRoZSB0aGUgc3BlY2lmaWMgY29sdW1ucyBhbmQgbWFwcGluZyB0aGUgc3RhdGVzIGFzIHRoZXkgd2VyZSBjb2RlZCBhcyBudW1iZXJzIGZyb20gMSB0byA3OCwgd2UgbWFwcGVkIHRoZW0gdG8gdGhlaXIgY29yc3BvbmRpbmcgbmFtZXMuDQoNCmBgYHtyfQ0KI01hcHBpbmcgdGhlIHN0YXRlcw0Kc3RhdGVfbWFwcGluZyA8LSBjKA0KICBgMWAgPSAiQWxhYmFtYSIsIGAyYCA9ICJBbGFza2EiLCBgNGAgPSAiQXJpem9uYSIsIGA1YCA9ICJBcmthbnNhcyIsIA0KICBgNmAgPSAiQ2FsaWZvcm5pYSIsIGA4YCA9ICJDb2xvcmFkbyIsIGA5YCA9ICJDb25uZWN0aWN1dCIsIGAxMGAgPSAiRGVsYXdhcmUiLCANCiAgYDExYCA9ICJEaXN0cmljdCBvZiBDb2x1bWJpYSIsIGAxMmAgPSAiRmxvcmlkYSIsIGAxM2AgPSAiR2VvcmdpYSIsIGAxNWAgPSAiSGF3YWlpIiwgDQogIGAxNmAgPSAiSWRhaG8iLCBgMTdgID0gIklsbGlub2lzIiwgYDE4YCA9ICJJbmRpYW5hIiwgYDE5YCA9ICJJb3dhIiwgDQogIGAyMGAgPSAiS2Fuc2FzIiwgYDIxYCA9ICJLZW50dWNreSIsIGAyMmAgPSAiTG91aXNpYW5hIiwgYDIzYCA9ICJNYWluZSIsIA0KICBgMjRgID0gIk1hcnlsYW5kIiwgYDI1YCA9ICJNYXNzYWNodXNldHRzIiwgYDI2YCA9ICJNaWNoaWdhbiIsIGAyN2AgPSAiTWlubmVzb3RhIiwgDQogIGAyOGAgPSAiTWlzc2lzc2lwcGkiLCBgMjlgID0gIk1pc3NvdXJpIiwgYDMwYCA9ICJNb250YW5hIiwgYDMxYCA9ICJOZWJyYXNrYSIsIA0KICBgMzJgID0gIk5ldmFkYSIsIGAzM2AgPSAiTmV3IEhhbXBzaGlyZSIsIGAzNGAgPSAiTmV3IEplcnNleSIsIGAzNWAgPSAiTmV3IE1leGljbyIsIA0KICBgMzZgID0gIk5ldyBZb3JrIiwgYDM3YCA9ICJOb3J0aCBDYXJvbGluYSIsIGAzOGAgPSAiTm9ydGggRGFrb3RhIiwgYDM5YCA9ICJPaGlvIiwgDQogIGA0MGAgPSAiT2tsYWhvbWEiLCBgNDFgID0gIk9yZWdvbiIsIGA0MmAgPSAiUGVubnN5bHZhbmlhIiwgYDQ0YCA9ICJSaG9kZSBJc2xhbmQiLCANCiAgYDQ1YCA9ICJTb3V0aCBDYXJvbGluYSIsIGA0NmAgPSAiU291dGggRGFrb3RhIiwgYDQ3YCA9ICJUZW5uZXNzZWUiLCBgNDhgID0gIlRleGFzIiwgDQogIGA0OWAgPSAiVXRhaCIsIGA1MGAgPSAiVmVybW9udCIsIGA1MWAgPSAiVmlyZ2luaWEiLCBgNTNgID0gIldhc2hpbmd0b24iLCANCiAgYDU0YCA9ICJXZXN0IFZpcmdpbmlhIiwgYDU1YCA9ICJXaXNjb25zaW4iLCBgNTZgID0gIld5b21pbmciLCBgNjZgID0gIkd1YW0iLCANCiAgYDcyYCA9ICJQdWVydG8gUmljbyIsIGA3OGAgPSAiVmlyZ2luIElzbGFuZHMiDQopDQoNCmVkdV9tYXBwaW5nIDwtIGMoDQogICJOZXZlciBhdHRlbmRlZCBzY2hvb2wgb3Igb25seSBraW5kZXJnYXJ0ZW4iID0gIkVsZW1lbnRhcnkgb3IgbGVzcyIsDQogICJHcmFkZXMgMSB0aHJvdWdoIDggKEVsZW1lbnRhcnkpIiA9ICJFbGVtZW50YXJ5IG9yIGxlc3MiLA0KICAiR3JhZGVzIDkgdGhyb3VnaCAxMSAoU29tZSBoaWdoIHNjaG9vbCkiID0gIkhpZ2ggc2Nob29sIG9yIGVxdWl2YWxlbnQiLA0KICAiR3JhZGUgMTIgb3IgR0VEIChIaWdoIHNjaG9vbCBncmFkdWF0ZSkiID0gIkhpZ2ggc2Nob29sIG9yIGVxdWl2YWxlbnQiLA0KICAiQ29sbGVnZSAxIHllYXIgdG8gMyB5ZWFycyAoU29tZSBjb2xsZWdlIG9yIHRlY2huaWNhbCBzY2hvb2wpIiA9ICJDb2xsZWdlIG9yIG1vcmUiLA0KICAiQ29sbGVnZSA0IHllYXJzIG9yIG1vcmUgKENvbGxlZ2UgZ3JhZHVhdGUpIiA9ICJDb2xsZWdlIG9yIG1vcmUiDQopDQoNCiMgQ29udmVydCB0aGUgZWR1IGNvbHVtbiBmcm9tIG51bWVyaWMgY29kZXMgdG8gZGVzY3JpcHRpdmUgbmFtZXMgdXNpbmcgdGhlIG1hcHBpbmcNCmVkdV9sZXZlbHMgPC0gYygNCiAgIk5ldmVyIGF0dGVuZGVkIHNjaG9vbCBvciBvbmx5IGtpbmRlcmdhcnRlbiIsDQogICJHcmFkZXMgMSB0aHJvdWdoIDggKEVsZW1lbnRhcnkpIiwNCiAgIkdyYWRlcyA5IHRocm91Z2ggMTEgKFNvbWUgaGlnaCBzY2hvb2wpIiwNCiAgIkdyYWRlIDEyIG9yIEdFRCAoSGlnaCBzY2hvb2wgZ3JhZHVhdGUpIiwNCiAgIkNvbGxlZ2UgMSB5ZWFyIHRvIDMgeWVhcnMgKFNvbWUgY29sbGVnZSBvciB0ZWNobmljYWwgc2Nob29sKSIsDQogICJDb2xsZWdlIDQgeWVhcnMgb3IgbW9yZSAoQ29sbGVnZSBncmFkdWF0ZSkiDQopDQoNCiMgRW5zdXJlIGFnZTVjYXQgaXMgY29ycmVjdGx5IGxhYmVsZWQNCmFnZTVjYXRfbGFiZWxzIDwtIGMoDQogICJBZ2UgMTggdG8gMjQiLCAiQWdlIDI1IHRvIDI5IiwgIkFnZSAzMCB0byAzNCIsICJBZ2UgMzUgdG8gMzkiLCANCiAgIkFnZSA0MCB0byA0NCIsICJBZ2UgNDUgdG8gNDkiLCAiQWdlIDUwIHRvIDU0IiwgIkFnZSA1NSB0byA1OSIsIA0KICAiQWdlIDYwIHRvIDY0IiwgIkFnZSA2NSB0byA2OSIsICJBZ2UgNzAgdG8gNzQiLCAiQWdlIDc1IHRvIDc5IiwgDQogICJBZ2UgODAgb3Igb2xkZXIiDQopDQoNCiMgQ29udmVydCBhZ2U1Y2F0IHRvIGEgZmFjdG9yIHdpdGggdGhlIGNvcnJlY3QgbGV2ZWxzIGFuZCBsYWJlbHMNCmZpbHRlcmVkX2RhdGEkYWdlNWNhdCA8LSBmYWN0b3IoZmlsdGVyZWRfZGF0YSRhZ2U1Y2F0LCBsZXZlbHMgPSAxOjEzLCBsYWJlbHMgPSBhZ2U1Y2F0X2xhYmVscykNCg0KIyBDcmVhdGUgdGhlIGFnZTVjYXQgbWFwcGluZyB0byBmZXdlciBjYXRlZ29yaWVzDQphZ2U1Y2F0X21hcHBpbmcgPC0gYygNCiAgIkFnZSAxOCB0byAyNCIgPSAiQWdlIDE4IHRvIDI5IiwNCiAgIkFnZSAyNSB0byAyOSIgPSAiQWdlIDE4IHRvIDI5IiwNCiAgIkFnZSAzMCB0byAzNCIgPSAiQWdlIDMwIHRvIDM5IiwNCiAgIkFnZSAzNSB0byAzOSIgPSAiQWdlIDMwIHRvIDM5IiwNCiAgIkFnZSA0MCB0byA0NCIgPSAiQWdlIDQwIHRvIDU0IiwNCiAgIkFnZSA0NSB0byA0OSIgPSAiQWdlIDQwIHRvIDU0IiwNCiAgIkFnZSA1MCB0byA1NCIgPSAiQWdlIDQwIHRvIDU0IiwNCiAgIkFnZSA1NSB0byA1OSIgPSAiQWdlIDU1IHRvIDY5IiwNCiAgIkFnZSA2MCB0byA2NCIgPSAiQWdlIDU1IHRvIDY5IiwNCiAgIkFnZSA2NSB0byA2OSIgPSAiQWdlIDU1IHRvIDY5IiwNCiAgIkFnZSA3MCB0byA3NCIgPSAiQWdlIDcwIGFuZCBvbGRlciIsDQogICJBZ2UgNzUgdG8gNzkiID0gIkFnZSA3MCBhbmQgb2xkZXIiLA0KICAiQWdlIDgwIG9yIG9sZGVyIiA9ICJBZ2UgNzAgYW5kIG9sZGVyIg0KKQ0KDQpmaWx0ZXJlZF9kYXRhIDwtIGZpbHRlcmVkX2RhdGEgJT4lIG11dGF0ZShvYmVzaXR5X3N0YXR1cyA9IGZhY3RvcihpZmVsc2UoYm1pID09IDQsIDEsIDApLCBsZXZlbHMgPSBjKDAsIDEpLCBsYWJlbHMgPSBjKCJOb3QgT2Jlc2UiLCAiT2Jlc2UiKSkpDQoNCg0KIyBBc3N1bWluZyBmaWx0ZXJlZF9kYXRhIGlzIHlvdXIgZGF0YWZyYW1lIGFuZCBpbmNvbWUgaXMgdGhlIGN1cnJlbnQgaW5jb21lIGZhY3Rvcg0KIyBEZWZpbmUgbmV3IGluY29tZSBjYXRlZ29yaWVzDQpmaWx0ZXJlZF9kYXRhJGluY29tZV9ncm91cCA8LSBjdXQoYXMubnVtZXJpYyhmaWx0ZXJlZF9kYXRhJGluY29tZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwgNCwgNywgMTEpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJMb3cgSW5jb21lIiwgIk1pZGRsZSBJbmNvbWUiLCAiSGlnaCBJbmNvbWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpDQoNCiMgQ3JlYXRlIGEgbmV3IHZhcmlhYmxlICdkaWFiZXRpY19zdGF0dXMnIGJhc2VkIG9uIHRoZSBleGlzdGluZyAnZGlhYmV0aWMnIHZhcmlhYmxlDQpmaWx0ZXJlZF9kYXRhJGRpYWJldGljX3N0YXR1cyA8LSBpZmVsc2UoZmlsdGVyZWRfZGF0YSRkaWFiZXRpYyAlaW4lIGMoMSwgMiksIDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShmaWx0ZXJlZF9kYXRhJGRpYWJldGljICVpbiUgYygzLCA0KSwgMiwgTkEpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMgTWFwIHRoZSBhZ2U1Y2F0IGNvbHVtbiB0byBmZXdlciBjYXRlZ29yaWVzDQpmaWx0ZXJlZF9kYXRhJGFnZTVjYXQgPC0gYXMuZmFjdG9yKGFnZTVjYXRfbWFwcGluZ1thcy5jaGFyYWN0ZXIoZmlsdGVyZWRfZGF0YSRhZ2U1Y2F0KV0pDQoNCiMgRW5zdXJlIGVkdSBpcyBhIGZhY3RvciB3aXRoIHRoZSBjb3JyZWN0IGxldmVscyBhbmQgbGFiZWxzDQpmaWx0ZXJlZF9kYXRhJGVkdSA8LSBmYWN0b3IoZmlsdGVyZWRfZGF0YSRlZHUsIGxldmVscyA9IDE6NiwgbGFiZWxzID0gZWR1X2xldmVscykNCg0KIyBNYXAgdGhlIGVkdSBjb2x1bW4gdG8gZmV3ZXIgY2F0ZWdvcmllcw0KZmlsdGVyZWRfZGF0YSRlZHUgPC0gYXMuZmFjdG9yKGVkdV9tYXBwaW5nW2FzLmNoYXJhY3RlcihmaWx0ZXJlZF9kYXRhJGVkdSldKQ0KDQojIE1hcCBzdGF0ZSBjb2RlcyB0byBzdGF0ZSBuYW1lcw0KZmlsdGVyZWRfZGF0YSRzdGF0ZSA8LSBhcy5mYWN0b3Ioc3RhdGVfbWFwcGluZ1thcy5jaGFyYWN0ZXIoZmlsdGVyZWRfZGF0YSRzdGF0ZSldKQ0KDQpmaWx0ZXJlZF9kYXRhJHNleCA8LSBmYWN0b3IoZmlsdGVyZWRfZGF0YSRzZXgsIGxldmVscyA9IGMoMSwgMiksIGxhYmVscyA9IGMoIk1hbGUiLCAiRmVtYWxlIikpDQoNCiMgRGVmaW5lIHRoZSBtYXJpdGFsIHN0YXR1cyBsYWJlbHMgYXMgYSBmYWN0b3Igd2l0aCBhcHByb3ByaWF0ZSBsZXZlbHMNCmZpbHRlcmVkX2RhdGEkbWFyaXRhbCA8LSBmYWN0b3IoZmlsdGVyZWRfZGF0YSRtYXJpdGFsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gMTo2LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTWFycmllZCIsICJEaXZvcmNlZCIsICJXaWRvd2VkIiwgIlNlcGFyYXRlZCIsICJOZXZlciBtYXJyaWVkIiwgIk1lbWJlciBvZiBhbiB1bm1hcnJpZWQgY291cGxlIikpDQoNCiMgRGVmaW5lIHRoZSBlbXBsb3ltZW50IHN0YXR1cyBsYWJlbHMgYXMgYSBmYWN0b3Igd2l0aCBhcHByb3ByaWF0ZSBsZXZlbHMNCmZpbHRlcmVkX2RhdGEkZW1wbG95c3RhdHVzIDwtIGZhY3RvcihmaWx0ZXJlZF9kYXRhJGVtcGxveXN0YXR1cywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gMTo4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJFbXBsb3llZCBmb3Igd2FnZXMiLCAiU2VsZi1lbXBsb3llZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk91dCBvZiB3b3JrIGZvciAxIHllYXIgb3IgbW9yZSIsICJPdXQgb2Ygd29yayBmb3IgbGVzcyB0aGFuIDEgeWVhciIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkEgaG9tZW1ha2VyIiwgIkEgc3R1ZGVudCIsICJSZXRpcmVkIiwgIlVuYWJsZSB0byB3b3JrIikpDQoNCiMgRGVmaW5lIHRoZSBoZWFydCBkaXNlYXNlIHN0YXR1cyBsYWJlbHMNCmZpbHRlcmVkX2RhdGEkaHJ0ZGlzZWFzZSA8LSBmYWN0b3IoZmlsdGVyZWRfZGF0YSRocnRkaXNlYXNlLCBsZXZlbHMgPSBjKDEsIDIpLCBsYWJlbHMgPSBjKCJZZXMiLCAiTm8iKSkNCg0KDQojIERpc3BsYXkgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZmlsdGVyZWRfZGF0YSBkYXRhIGZyYW1lIHRvIGNvbmZpcm0gY2hhbmdlcw0Kc3RyKGZpbHRlcmVkX2RhdGEpDQpgYGANCg0KV2Ugd2lsbCBjb252ZXJ0IHRoZSBjb2x1bW4gdG8gZmFjdG9ycyAobWFpbmx5IHRoZXkgYXJlIG51bWVyaWMpLg0KYGBge3J9DQojIENvbnZlcnQgc3BlY2lmaWVkIGNvbHVtbnMgdG8gZmFjdG9ycyBpbiB0aGUgZmlsdGVyZWRfZGF0YSBkYXRhIGZyYW1lDQpmaWx0ZXJlZF9kYXRhJGdlbmhsdGggPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkZ2VuaGx0aCkNCmZpbHRlcmVkX2RhdGEkcG9vcmhsdGggPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkcG9vcmhsdGgpDQpmaWx0ZXJlZF9kYXRhJGhydGRpc2Vhc2UgPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkaHJ0ZGlzZWFzZSkNCmZpbHRlcmVkX2RhdGEkZGlhYmV0aWMgPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkZGlhYmV0aWMpDQpmaWx0ZXJlZF9kYXRhJG1hcml0YWwgPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkbWFyaXRhbCkNCmZpbHRlcmVkX2RhdGEkZW1wbG95c3RhdHVzIDwtIGFzLmZhY3RvcihmaWx0ZXJlZF9kYXRhJGVtcGxveXN0YXR1cykNCmZpbHRlcmVkX2RhdGEkaW5jb21lIDwtIGFzLmZhY3RvcihmaWx0ZXJlZF9kYXRhJGluY29tZSkNCmZpbHRlcmVkX2RhdGEkc21va2VkYXkgPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkc21va2VkYXkpDQpmaWx0ZXJlZF9kYXRhJHRvdGluZGEgPC0gYXMuZmFjdG9yKGZpbHRlcmVkX2RhdGEkdG90aW5kYSkNCmZpbHRlcmVkX2RhdGEkc2V4IDwtIGFzLmZhY3RvcihmaWx0ZXJlZF9kYXRhJHNleCkNCmZpbHRlcmVkX2RhdGEkYm1pNSA8LSBhcy5mYWN0b3IoZmlsdGVyZWRfZGF0YSRibWk1KQ0KZmlsdGVyZWRfZGF0YSRvYmVzaXR5X3N0YXR1cyA8LSBmYWN0b3IoZmlsdGVyZWRfZGF0YSRvYmVzaXR5X3N0YXR1cykNCmZpbHRlcmVkX2RhdGEkZGlhYmV0aWNfc3RhdHVzIDwtIGFzLmZhY3RvcihmaWx0ZXJlZF9kYXRhJGRpYWJldGljX3N0YXR1cykNCg0KIyBEaXNwbGF5IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGZpbHRlcmVkX2RhdGEgZGF0YSBmcmFtZSB0byBjb25maXJtIGNoYW5nZXMNCnN0cihmaWx0ZXJlZF9kYXRhKQ0KYGBgDQoNCg0KDQojIEVEQQ0KDQpGaXJzdCB3ZSB3YW50ZWQgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyIHZhcmlhYmxlcyANCg0KYGBge3J9DQojIFBsb3QgYmFyIGNoYXJ0IGZvciBnZW5kZXIgZGlzdHJpYnV0aW9uDQpnZ3Bsb3QoZmlsdGVyZWRfZGF0YSwgYWVzKHggPSBzZXgsIGZpbGwgPSBzZXgpKSArDQogIGdlb21fYmFyKCkgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBzaXplID0gNSwgY29sb3IgPSAiYmxhY2siLCBmb250ZmFjZSA9ICJib2xkIikgKyAgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk1hbGUiID0gImJsdWU0IiwgIkZlbWFsZSIgPSAicmVkNCIpKSArICANCiAgbGFicyh0aXRsZSA9ICJHZW5kZXIgRGlzdHJpYnV0aW9uIGluIE91ciBEYXRhc2V0IiwgeCA9ICJHZW5kZXIiLCB5ID0gIkNvdW50IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksICANCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICAjIFJlbW92ZSB0aGUgbGVnZW5kDQogICkgKw0KICB5bGltKDAsIG1heCh0YWJsZShmaWx0ZXJlZF9kYXRhJHNleCkpICogMS4xKQ0KDQpgYGANCg0KVGhlIGJhciBjaGFydCBzaG93cyB0aGF0IHRoZSBkYXRhc2V0IGNvbnNpc3RzIG9mIDE4LDQ3OSBtYWxlcyAoNDYuNyUpIGFuZCAyMSwwNzIgZmVtYWxlcyAoNTMuMyUpLCBpbmRpY2F0aW5nIGEgaGlnaGVyIHJlcHJlc2VudGF0aW9uIG9mIGZlbWFsZXMuDQoNCg0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgYmFyIGNoYXJ0DQpnZ3Bsb3QoZmlsdGVyZWRfZGF0YSwgYWVzKHggPSBtYXJpdGFsKSkgKw0KICBnZW9tX2JhcihmaWxsID0gImJsdWU0IikgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDUsIGNvbG9yID0gImJsYWNrIiwgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgTWFyaXRhbCBTdGF0dXMiLCB4ID0gIk1hcml0YWwgU3RhdHVzIiwgeSA9ICJDb3VudCIpICsNCiAgeWxpbSgwLCBtYXgodGFibGUoZmlsdGVyZWRfZGF0YSRtYXJpdGFsKSkgKiAxLjEpICsgIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIsIG1hcmdpbiA9IG1hcmdpbihiID0gMTApKSwgIA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwgIA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDEpLCAgDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgIA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNSwgbGluZXR5cGUgPSAnc29saWQnLCBjb2xvdXIgPSAiZ3JleTgwIiksICANCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICANCiAgKQ0KDQpgYGANCjxicj4gVGhlIGJhciBjaGFydCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIG1hcml0YWwgc3RhdHVzIGluIG91ciBkYXRhc2V0LCB3ZSBoYXZlIDE2LDIzOCBpbmRpdmlkdWFscyBhcmUgbWFycmllZCAoMzguNCUpLCA4LDM2OCBhcmUgZGl2b3JjZWQgKDE5LjglKSwgNCwxNDIgYXJlIHdpZG93ZWQgKDkuOCUpLCAxLDM5OSBhcmUgc2VwYXJhdGVkICgzLjMlKSwgNywxNTIgaGF2ZSBuZXZlciBtYXJyaWVkICgxNi45JSksIGFuZCAyLDI1MiBhcmUgbWVtYmVycyBvZiBhbiB1bm1hcnJpZWQgY291cGxlICg1LjMlKS4gVGhlIG1ham9yaXR5IG9mIGluZGl2aWR1YWxzIGluIHRoZSBkYXRhc2V0IGFyZSBtYXJyaWVkLCBmb2xsb3dlZCBieSB0aG9zZSB3aG8gYXJlIGRpdm9yY2VkIGFuZCBuZXZlciBtYXJyaWVkLg0KDQoNCg0KYGBge3J9DQojIFBsb3QgdGhlIGJhciBjaGFydA0KZ2dwbG90KGZpbHRlcmVkX2RhdGEsIGFlcyh4ID0gZWR1KSkgKw0KICBnZW9tX2JhcihmaWxsID0gImJsdWU0IikgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBzaXplID0gNSwgY29sb3IgPSAiYmxhY2siLCBmb250ZmFjZSA9ICJib2xkIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBFZHVjYXRpb24gU3RhdHVzIiwgeCA9ICJFZHVjYXRpb24gTGV2ZWwiLCB5ID0gIkNvdW50IikgKw0KICB5bGltKDAsIG1heCh0YWJsZShmaWx0ZXJlZF9kYXRhJGVkdSkpICogMS4xKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiwgbWFyZ2luID0gbWFyZ2luKGIgPSAxMCkpLCAgDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAgDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksICANCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShzaXplID0gMC41LCBsaW5ldHlwZSA9ICdzb2xpZCcsIGNvbG91ciA9ICJncmV5ODAiKSwgIA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgIA0KICApDQpgYGANCjxicj4gVGhlIGJhciBjaGFydCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIGVkdWNhdGlvbiBzdGF0dXMgaW4gdGhlIGRhdGFzZXQgd2UgaGF2ZSAyNSwzODYgaW5kaXZpZHVhbHMgaGF2ZSBhIGNvbGxlZ2UgZWR1Y2F0aW9uIG9yIG1vcmUgKDY0LjglKSwgNjkzIGhhdmUgYW4gZWxlbWVudGFyeSBlZHVjYXRpb24gb3IgbGVzcyAoMS44JSksIGFuZCAxMyw0NzIgaGF2ZSBhIGhpZ2ggc2Nob29sIGVkdWNhdGlvbiBvciBlcXVpdmFsZW50ICgzMy40JSkuIFRoZSBtYWpvcml0eSBvZiBpbmRpdmlkdWFscyBpbiB0aGUgZGF0YXNldCBoYXZlIGEgY29sbGVnZSBlZHVjYXRpb24gb3IgaGlnaGVyLg0KDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBiYXIgY2hhcnQNCmdncGxvdChmaWx0ZXJlZF9kYXRhLCBhZXMoeCA9IGVtcGxveXN0YXR1cykpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJibHVlNCIpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDQsIGNvbG9yID0gImJsYWNrIiwgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgRW1wbG95bWVudCBTdGF0dXMiLCB4ID0gIkVtcGxveW1lbnQgU3RhdHVzIiwgeSA9ICJDb3VudCIpICsNCiAgeWxpbSgwLCBtYXgodGFibGUoZmlsdGVyZWRfZGF0YSRlbXBsb3lzdGF0dXMpKSAqIDEuMSkgKyAgDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiwgbWFyZ2luID0gbWFyZ2luKGIgPSAxMCkpLCAgDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAgDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksICANCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShzaXplID0gMC41LCBsaW5ldHlwZSA9ICdzb2xpZCcsIGNvbG91ciA9ICJncmV5ODAiKSwgIA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgIA0KICApDQpgYGANCjxicj4gVGhlIGJhciBjaGFydCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIGVtcGxveW1lbnQgc3RhdHVzIGluIHRoZSBkYXRhc2V0IGFzIGhlcmUgd2UgaGF2ZSAxMyw3MzQgaW5kaXZpZHVhbHMgYXJlIGVtcGxveWVkIGZvciB3YWdlcyAoMzQuOSUpLCAyLDg1NSBhcmUgc2VsZi1lbXBsb3llZCAoNy4zJSksIDEsNTQyIGhhdmUgYmVlbiBvdXQgb2Ygd29yayBmb3IgMSB5ZWFyIG9yIG1vcmUgKDMuOSUpLCAxLDIzNiBoYXZlIGJlZW4gb3V0IG9mIHdvcmsgZm9yIGxlc3MgdGhhbiAxIHllYXIgKDMuMSUpLCAxLDMzMCBhcmUgaG9tZW1ha2VycyAoMy40JSksIDQ1MSBhcmUgc3R1ZGVudHMgKDEuMSUpLCAxMCw4ODggYXJlIHJldGlyZWQgKDI3LjYlKSwgYW5kIDcsNTE1IGFyZSB1bmFibGUgdG8gd29yayAoMTkuMSUpLiBUaGUgbWFqb3JpdHkgb2YgaW5kaXZpZHVhbHMgaW4gdGhlIGRhdGFzZXQgYXJlIGVtcGxveWVkIGZvciB3YWdlcywgZm9sbG93ZWQgYnkgdGhvc2Ugd2hvIGFyZSByZXRpcmVkLg0KDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBiYXIgY2hhcnQNCmdncGxvdChmaWx0ZXJlZF9kYXRhLCBhZXMoeCA9IGluY29tZV9ncm91cCkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJibHVlNCIpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDQsIGNvbG9yID0gImJsYWNrIiwgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgSW5jb21lIFN0YXR1cyIsIHggPSAiSW5jb21lIFN0YXR1cyIsIHkgPSAiRnJlcXVlbmN5IikgKw0KICB5bGltKDAsIG1heCh0YWJsZShmaWx0ZXJlZF9kYXRhJGluY29tZV9ncm91cCkpICogMS4xKSArICANCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiLCBtYXJnaW4gPSBtYXJnaW4oYiA9IDEwKSksICANCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksICANCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSwgIA0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICANCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjUsIGxpbmV0eXBlID0gJ3NvbGlkJywgY29sb3VyID0gImdyZXk4MCIpLCAgDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgIA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAgDQogICkNCmBgYA0KPGJyPiBUaGUgYmFyIGNoYXJ0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgaW5jb21lIHN0YXR1cyBpbiB0aGUgZGF0YXNldDogMTEsMzQ5IGluZGl2aWR1YWxzIGhhdmUgbG93IGluY29tZSAoMjkuMCUpLCAxNyw1MjYgaGF2ZSBtaWRkbGUgaW5jb21lICg0NC43JSksIGFuZCAxMCw2NzYgaGF2ZSBoaWdoIGluY29tZSAoMjcuMiUpLiBUaGUgbWFqb3JpdHkgb2YgaW5kaXZpZHVhbHMgaW4gdGhlIGRhdGFzZXQgaGF2ZSBtaWRkbGUgaW5jb21lLCBmb2xsb3dlZCBieSBsb3cgaW5jb21lIGFuZCBoaWdoIGluY29tZS4NCg0KYGBge3J9DQojIFBsb3QgdGhlIHBpZSBjaGFydCB3aXRoIGltcHJvdmVkIGFlc3RoZXRpY3MNCmdncGxvdChmaWx0ZXJlZF9kYXRhLCBhZXMoeCA9ICIiLCBmaWxsID0gaHJ0ZGlzZWFzZSkpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImNvdW50IiwgY29sb3IgPSAiYmxhY2siKSArDQogIGNvb3JkX3BvbGFyKCJ5IikgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IHBhc3RlMChyb3VuZCguLmNvdW50Li4gLyBzdW0oLi5jb3VudC4uKSAqIDEwMCwgMSksICIlIikpLCANCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJZZXMiID0gInJlZDQiLCAiTm8iID0gImJsdWU0IikpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQ29yb25hcnkgSGVhcnQgRGlzZWFzZSAoQ0hEKSBvciBNeW9jYXJkaWFsIEluZmFyY3Rpb24gKE1JKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiLCBtYXJnaW4gPSBtYXJnaW4odCA9IDIwLCBiID0gMTApKSwgIA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIg0KICApDQpgYGANCjxicj4gVGhlIHBpZSBjaGFydCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UgKENIRCkgb3IgbXlvY2FyZGlhbCBpbmZhcmN0aW9uIChNSSkgc3RhdHVzIGluIHRoZSBkYXRhc2V0OiAxMS41JSBvZiBpbmRpdmlkdWFscyBoYXZlIENIRCBvciBNSSwgd2hpbGUgODguNSUgZG8gbm90IGhhdmUgdGhlc2UgY29uZGl0aW9ucy4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgdmFzdCBtYWpvcml0eSBvZiBpbmRpdmlkdWFscyBpbiB0aGUgZGF0YXNldCBkbyBub3QgaGF2ZSBDSEQgb3IgTUkuDQoNCg0KYGBge3J9DQojIFBsb3QgdGhlIHBpZSBjaGFydCB3aXRoIGltcHJvdmVkIGFlc3RoZXRpY3MNCmdncGxvdChmaWx0ZXJlZF9kYXRhLCBhZXMoeCA9ICIiLCBmaWxsID0gZmFjdG9yKHRvdGluZGEpKSkgKw0KICBnZW9tX2Jhcih3aWR0aCA9IDEsIHN0YXQgPSAiY291bnQiLCBjb2xvciA9ICJibGFjayIpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKC4uY291bnQuLiAvIHN1bSguLmNvdW50Li4pICogMTAwLCAxKSwgIiUiKSksIA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIHNpemUgPSA1LCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlID0gImJvbGQiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIjEiID0gImJsdWU0IiwgIjIiID0gInJlZDQiKSwgDQogICAgbGFiZWxzID0gYygiMSIgPSAiSGFkIHBoeXNpY2FsIGFjdGl2aXR5IG9yIGV4ZXJjaXNlIiwgDQogICAgICAgICAgICAgICAiMiIgPSAiTm8gcGh5c2ljYWwgYWN0aXZpdHkgb3IgZXhlcmNpc2UiKQ0KICApICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUGh5c2ljYWwgQWN0aXZpdHkgb3IgRXhlcmNpc2UgRHVyaW5nIHRoZSBQYXN0IDMwIERheXMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiwgbWFyZ2luID0gbWFyZ2luKHQgPSAyMCwgYiA9IDEwKSksICANCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCAgDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCAgDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSINCiAgKQ0KYGBgDQo8YnI+IFRoZSBwaWUgY2hhcnQgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwaHlzaWNhbCBhY3Rpdml0eSBvciBleGVyY2lzZSBkdXJpbmcgdGhlIHBhc3QgMzAgZGF5czogNjMuOSUgb2YgaW5kaXZpZHVhbHMgaGFkIHBoeXNpY2FsIGFjdGl2aXR5IG9yIGV4ZXJjaXNlLCB3aGlsZSAzNi4xJSBkaWQgbm90IGhhdmUgYW55IHBoeXNpY2FsIGFjdGl2aXR5IG9yIGV4ZXJjaXNlLiBUaGlzIGluZGljYXRlcyB0aGF0IGEgbWFqb3JpdHkgb2YgaW5kaXZpZHVhbHMgaW4gdGhlIGRhdGFzZXQgZW5nYWdlZCBpbiBwaHlzaWNhbCBhY3Rpdml0eSBvciBleGVyY2lzZSBkdXJpbmcgdGhlIHBhc3QgbW9udGguDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBwaWUgY2hhcnQgd2l0aCBpbXByb3ZlZCBhZXN0aGV0aWNzDQpnZ3Bsb3QoZmlsdGVyZWRfZGF0YSwgYWVzKHggPSAiIiwgZmlsbCA9IGZhY3RvcihkaWFiZXRpY19zdGF0dXMpKSkgKw0KICBnZW9tX2Jhcih3aWR0aCA9IDEsIHN0YXQgPSAiY291bnQiLCBjb2xvciA9ICJibGFjayIpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKC4uY291bnQuLiAvIHN1bSguLmNvdW50Li4pICogMTAwLCAxKSwgIiUiKSksIA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIHNpemUgPSA1LCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlID0gImJvbGQiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIjIiID0gImJsdWU0IiwgIjEiID0gInJlZDQiKSwgDQogICAgbGFiZWxzID0gYygiMSIgPSAiWWVzIiwgIjIiID0gIk5vIikNCiAgKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIERpYWJldGVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIG1hcmdpbiA9IG1hcmdpbih0ID0gMjAsIGIgPSAxMCkpLCAgDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgIA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgIA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgIA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgIA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iDQogICkNCmBgYA0KPGJyPiBUaGUgcGllIGNoYXJ0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgZGlhYmV0ZXMgc3RhdHVzIGluIHRoZSBkYXRhc2V0OiAyMC4xJSBvZiBpbmRpdmlkdWFscyBoYXZlIGRpYWJldGVzLCB3aGlsZSA3OS45JSBkbyBub3QgaGF2ZSBkaWFiZXRlcy4gVGhpcyBpbmRpY2F0ZXMgdGhhdCBhIHNpZ25pZmljYW50IG1ham9yaXR5IG9mIGluZGl2aWR1YWxzIGluIHRoZSBkYXRhc2V0IGRvIG5vdCBoYXZlIGRpYWJldGVzLg0KDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBiYXIgY2hhcnQNCmdncGxvdChmaWx0ZXJlZF9kYXRhLCBhZXMoeCA9IGZhY3RvcihnZW5obHRoLCBsZXZlbHMgPSAxOjUsIGxhYmVscyA9IGMoIkV4Y2VsbGVudCIsICJWZXJ5IGdvb2QiLCAiR29vZCIsICJGYWlyIiwgIlBvb3IiKSkpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiYmx1ZTQiKSArDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgdmp1c3QgPSAtMC41LCBzaXplID0gNCwgY29sb3IgPSAiYmxhY2siLCBmb250ZmFjZSA9ICJib2xkIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBHZW5lcmFsIEhlYWx0aCBTdGF0dXMiLCB4ID0gIkhlYWx0aCBTdGF0dXMiLCB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgeWxpbSgwLCBtYXgodGFibGUoZmlsdGVyZWRfZGF0YSRnZW5obHRoKSkgKiAxLjEpICsgIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIsIG1hcmdpbiA9IG1hcmdpbihiID0gMTApKSwgIA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwgIA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDEpLCAgDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgIA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNSwgbGluZXR5cGUgPSAnc29saWQnLCBjb2xvdXIgPSAiZ3JleTgwIiksICANCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICANCiAgKQ0KYGBgDQo8YnI+IFRoZSBiYXIgY2hhcnQgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBnZW5lcmFsIGhlYWx0aCBzdGF0dXMgaW4gdGhlIGRhdGFzZXQ6IDEsOTI3IGluZGl2aWR1YWxzIHJlcG9ydGVkIHRoZWlyIGhlYWx0aCBhcyBleGNlbGxlbnQgKDQuOSUpLCA3LDkyNCBhcyB2ZXJ5IGdvb2QgKDIwLjElKSwgMTIsODk5IGFzIGdvb2QgKDMyLjglKSwgMTAsODcyIGFzIGZhaXIgKDI3LjYlKSwgYW5kIDUsOTI5IGFzIHBvb3IgKDE1LjElKS4gVGhlIG1vc3QgY29tbW9uIGhlYWx0aCBzdGF0dXMgcmVwb3J0ZWQgaXMgZ29vZCwgZm9sbG93ZWQgYnkgZmFpci4NCg0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgYmFyIGNoYXJ0DQpnZ3Bsb3QoZmlsdGVyZWRfZGF0YSwgYWVzKHggPSBmYWN0b3Ioc21va2VkYXksIGxldmVscyA9IDE6MywgbGFiZWxzID0gYygiRXZlcnkgZGF5IiwgIlNvbWUgZGF5cyIsICJOb3QgYXQgYWxsIikpKSkgKw0KICBnZW9tX2JhcihmaWxsID0gImJsdWU0IikgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDQsIGNvbG9yID0gImJsYWNrIiwgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU21va2luZyBTdGF0dXMiLCB4ID0gIlNtb2tpbmcgU3RhdHVzIiwgeSA9ICJGcmVxdWVuY3kiKSArDQogIHlsaW0oMCwgbWF4KHRhYmxlKGZpbHRlcmVkX2RhdGEkc21va2VkYXkpKSAqIDEuMSkgKyAgDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiwgbWFyZ2luID0gbWFyZ2luKGIgPSAxMCkpLCAgDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAgDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksICANCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShzaXplID0gMC41LCBsaW5ldHlwZSA9ICdzb2xpZCcsIGNvbG91ciA9ICJncmV5ODAiKSwgIA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgIA0KICApDQpgYGANCjxicj4gVGhlIGJhciBjaGFydCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIHNtb2tpbmcgc3RhdHVzIGluIHRoZSBkYXRhc2V0OiAxMCw0MDggaW5kaXZpZHVhbHMgc21va2UgZXZlcnkgZGF5ICgyMS40JSksIDQsMDYyIHNtb2tlIG9uIHNvbWUgZGF5cyAoOC4zJSksIGFuZCAyNSwwODEgZG8gbm90IHNtb2tlIGF0IGFsbCAoNzAuMyUpLiBUaGUgbWFqb3JpdHkgb2YgaW5kaXZpZHVhbHMgaW4gdGhlIGRhdGFzZXQgZG8gbm90IHNtb2tlLg0KDQoNCg0KIyMgT2Jlc2l0eSByYXRlIG92ZXIgdGhlIHN0YXRlcw0KDQpJbiB0aGlzIHBhcnQgb2YgdGhlIHByb2plY3QsIHdlIHdhbnRlZCB0byBpbmNsdWRlIGEgbWFwIHdpdGggdGhlIG9iZXNpdHkgcmF0ZXMgZm9yIGVhY2ggcmVzcGVjdGl2ZSBzdGF0ZSwgc28gd2UgdW5kZXJzdGFuZCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBvYmVzaXR5IGluIHRoZSBVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EgZm9yIGNyZWF0aW5nIGEgZ2VuZXJhbCB2aWV3Lg0KDQpgYGB7cn0NCiMgU3VtbWFyaXplIHRoZSBvYmVzaXR5IHJhdGUgYnkgc3RhdGUNCm9iZXNpdHlfYnlfc3RhdGUgPC0gZmlsdGVyZWRfZGF0YSAlPiUNCiAgZ3JvdXBfYnkoc3RhdGUpICU+JQ0KICBzdW1tYXJpemUoDQogICAgdG90YWxfaW5kaXZpZHVhbHMgPSBuKCksDQogICAgb2Jlc2VfaW5kaXZpZHVhbHMgPSBzdW0ob2Jlc2l0eV9zdGF0dXMgPT0gIk9iZXNlIiksDQogICAgb2Jlc2l0eV9yYXRlID0gbWVhbihvYmVzaXR5X3N0YXR1cyA9PSAiT2Jlc2UiKQ0KICApICU+JQ0KICBhcnJhbmdlKGRlc2Mob2Jlc2l0eV9yYXRlKSkNCg0KIyBQcmludCB0aGUgc3VtbWFyaXplZCBvYmVzaXR5IHJhdGVzIHBlciBzdGF0ZQ0KcHJpbnQob2Jlc2l0eV9ieV9zdGF0ZSkNCmBgYA0KDQpgYGB7cn0NCiMgTG9hZCBVUyBtYXAgZGF0YQ0KdXNfbWFwIDwtIG1hcF9kYXRhKCJzdGF0ZSIpDQoNCiMgTWVyZ2UgbWFwIGRhdGEgd2l0aCBvYmVzaXR5IGRhdGENCnVzX21hcCRyZWdpb24gPC0gdG9sb3dlcih1c19tYXAkcmVnaW9uKQ0Kb2Jlc2l0eV9ieV9zdGF0ZSRzdGF0ZSA8LSB0b2xvd2VyKG9iZXNpdHlfYnlfc3RhdGUkc3RhdGUpDQptYXBfZGF0YSA8LSBtZXJnZSh1c19tYXAsIG9iZXNpdHlfYnlfc3RhdGUsIGJ5LnggPSAicmVnaW9uIiwgYnkueSA9ICJzdGF0ZSIsIGFsbC54ID0gVFJVRSkNCg0KIyBQbG90IHRoZSBtYXANCmdncGxvdChtYXBfZGF0YSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gb2Jlc2l0eV9yYXRlKSkgKw0KICBnZW9tX3BvbHlnb24oY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4yKSArDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lID0gIk9iZXNpdHkgUmF0ZSIsIGxvdyA9ICJsaWdodGJsdWUyIiwgaGlnaCA9ICJkYXJrcmVkIikgKw0KICBjb29yZF9maXhlZCgxLjMpICsgIyBNYWludGFpbiBhc3BlY3QgcmF0aW8NCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJPYmVzaXR5IFJhdGVzIGJ5IFN0YXRlIiwNCiAgICB4ID0gTlVMTCwNCiAgICB5ID0gTlVMTA0KICApICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpDQogICkgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2NvbG9yYmFyKGJhcndpZHRoID0gMTAsIGJhcmhlaWdodCA9IDEpKQ0KYGBgDQpUaGUgbWFwIHByb3ZpZGVkIGFib3ZlIHNob3dzIHRoZSBvYmVzaXR5IHJhdGVzIGJ5IHN0YXRlIGluIHRoZSBVbml0ZWQgU3RhdGVzIGFuZCBzb21lIG90aGVyIHBhcnRzIG9mDQpjb3VudHJpZXMgYm9yZGVyZWQuIEl0IGlzIHJlcHJlc2VudGVkIHdpdGggdmFyeWluZyBzaGFkZXMgb2YgcmVkIHJlcHJlc2VudGluZyBkaWZmZXJlbnQgbGV2ZWxzIG9mIG9iZXNpdHkgcmF0ZXMuDQoNCkdFTkVSQUwgVFJFTkQ6IE1vc3Qgc3RhdGVzIGhhdmUgaGlnaCBvYmVzaXR5IHJhdGVzLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIHByZWRvbWluYW50IGRhcmsgcmVkIHNoYWRpbmcgDQphY3Jvc3MgdGhlIG1hcC4NCg0KU3RhdGVzIHdpdGggdGhlIGRhcmtlc3QgcmVkIGNvbG9yIGhhdmUgdGhlIGhpZ2hlc3Qgb2Jlc2l0eSByYXRlcywgY2xvc2UgdG8gMC40NS4NClRoZXNlIHN0YXRlcyBhcmUgbWFpbmx5IGxvY2F0ZWQgaW4gdGhlIFNvdXRoIGFuZCBwYXJ0cyBvZiB0aGUgTWlkd2VzdC4gU3BlY2lmaWNhbGx5LCBOb3J0aCBEYWtvdGEsDQpXaXNjb25zaW4gYW5kIElvd2EgYXJlIHRoZSBzdGF0ZXMgd2l0aCB0aGUgaGlnaGVzdCByYXRlIG9mIG9iZXNpdHkuIA0KDQoNCiMgQ29udGluZ2VuY3kgVGFibGVzDQpOb3csIHdlIHdpbGwgbWFrZSBzb21lIGNvbnRpbmdlbmN5IHRhYmxlcy4gVGhpcyB0eXBlIG9mIHRhYmxlcyBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgaW5kaXZpZHVhbHMgb2JzZXJ2ZWQgZm9yIHRoZSBjb21iaW5hdGlvbnMgb2YgdGhlIGNhdGVnb3JpZXMgb2YgdGhlIDIgdmFyaWFibGVzIG9yIG1vcmUuDQoNCiMjIEpvaW50IFByb2JhYmlsaXR5DQpMZXQgdXMgc3RhcnQgd2l0aCBUaGUgam9pbnQgcHJvYmFiaWxpdHkgDQpgYGB7cn0NCiMgSm9pbnQgUHJvYmFiaWxpdHkgdGFibGUgZm9yIG9iZXNpdHlfc3RhdHVzIGFuZCBzZXgNCmpvaW50X3Byb2Jfc2V4IDwtIHByb3AudGFibGUodGFibGUoZmlsdGVyZWRfZGF0YSRvYmVzaXR5X3N0YXR1cywgZmlsdGVyZWRfZGF0YSRzZXgpKQ0Ka2FibGUoam9pbnRfcHJvYl9zZXgsIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiSm9pbnQgUHJvYmFiaWxpdHkgb2YgT2Jlc2l0eSBTdGF0dXMgYW5kIFNleCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpKQ0KDQoNCiMgSm9pbnQgUHJvYmFiaWxpdHkgdGFibGUgZm9yIG9iZXNpdHlfc3RhdHVzIGFuZCBhZ2U1Y2F0DQpqb2ludF9wcm9iX2FnZSA8LSBwcm9wLnRhYmxlKHRhYmxlKGZpbHRlcmVkX2RhdGEkb2Jlc2l0eV9zdGF0dXMsIGZpbHRlcmVkX2RhdGEkYWdlNWNhdCkpDQprYWJsZShqb2ludF9wcm9iX2FnZSwgZGlnaXRzID0gMywgY2FwdGlvbiA9ICJKb2ludCBQcm9iYWJpbGl0eSBvZiBPYmVzaXR5IFN0YXR1cyBhbmQgQWdlIENhdGVnb3J5IikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIikpDQoNCiMgSm9pbnQgUHJvYmFiaWxpdHkgdGFibGUgZm9yIG9iZXNpdHlfc3RhdHVzIGFuZCBpbmNvbWVfZ3JvdXANCmpvaW50X3Byb2JfaW5jb21lIDwtIHByb3AudGFibGUodGFibGUoZmlsdGVyZWRfZGF0YSRvYmVzaXR5X3N0YXR1cywgZmlsdGVyZWRfZGF0YSRpbmNvbWVfZ3JvdXApKQ0Ka2FibGUoam9pbnRfcHJvYl9pbmNvbWUsIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiSm9pbnQgUHJvYmFiaWxpdHkgb2YgT2Jlc2l0eSBTdGF0dXMgYW5kIEluY29tZSBHcm91cCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpKQ0KDQpgYGANCg0KSW4gdGhlICoqSm9pbnQgUHJvYmFiaWxpdHkqKiwgdGhlIHByb2JhYmlsaXR5IGlzIGNhbGN1bGF0ZWQgb2YgdHdvIGV2ZW50cyBvY2N1cnJpbmcgc2ltdWx0YW5lb3VzbHkuIEl0IGlzIGNhbGN1bGF0ZWQgYnkgZGl2aWRpbmcgdGhlIGNvdW50IG9mIG9jY3VycmVuY2VzIG9mIGJvdGggZXZlbnRzIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLg0KDQoqKlNleCBEaWZmZXJlbmNlcyBpbiBPYmVzaXR5OioqDQpUaGVyZSBzZWVtcyB0byBiZSBhIGRpZmZlcmVuY2UgaW4gb2Jlc2l0eSByYXRlcyBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzLCB3aXRoIGZlbWFsZXMgaGF2aW5nIGEgaGlnaGVyIHByb2JhYmlsaXR5IG9mIGJlaW5nIG9iZXNlLiAoMC4yMjUgPiAwLjE3NSkuIFRoZXNlIHByb2JhYmlsaXRpZXMgc2hvdyBhc3NvY2lhdGlvbnMsIGJ1dCB0aGV5IGRvbid0IGltcGx5IGNhdXNhdGlvbi4gRm9yIGV4YW1wbGUsIGJlaW5nIGZlbWFsZSBkb2Vzbid0IGNhdXNlIG9iZXNpdHksIG5vciBkb2VzIGJlaW5nIG1hbGUgcHJldmVudCBpdC4gT3RoZXIgZmFjdG9ycyBub3QgaW5jbHVkZWQgaW4gdGhpcyB0YWJsZSBjb3VsZCBjb250cmlidXRlIHRvIG9iZXNpdHkgcmF0ZXMuIDxicj4NCg0KKipXRSBIQVZFIDUgY2F0ZWdvcmllcyBvZiB0aGUgYWdlOioqDQpHZW5lcmFsbHksIHRoZSBwcm9iYWJpbGl0eSBvZiBiZWluZyBvYmVzZSB0ZW5kcyB0byBpbmNyZWFzZSB3aXRoIGFnZSwgd2l0aCB0aGUgaGlnaGVzdCBwcm9iYWJpbGl0eSBhbW9uZyBpbmRpdmlkdWFscyBhZ2VkIDU1IHRvIDY5ICgwLjE0MikuIEFnZSA3MCBhbmQgb2xkZXIgaXMgbG93IHJhdGUuIDxicj4NCg0KKipNSURETEUgSU5DT01FIGhhcyB0aGUgaGlnaGVzdCBvYmVzaXR5OioqDQpGb3IgdGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIG9iZXNlLCB0aGUgbWlkZGxlLWluY29tZSBncm91cCBoYXMgdGhlIGhpZ2hlc3QgcHJvYmFiaWxpdHkgKDAuMTc5KSwgZm9sbG93ZWQgYnkgdGhlIGxvdy1pbmNvbWUgZ3JvdXAgKDAuMTIwKSBhbmQgdGhlIGhpZ2gtaW5jb21lIGdyb3VwICgwLjEwMikuIDxicj4NCg0KDQojIyBDb25kaXRpb25hbCBQcm9iYWJpbGl0eQ0KYGBge3J9DQojIENvbmRpdGlvbmFsIFByb2JhYmlsaXR5IG9mIG9iZXNpdHlfc3RhdHVzIGdpdmVuIHNleA0KY29uZGl0aW9uYWxfcHJvYl9vYmVzaXR5X3NleCA8LSBwcm9wLnRhYmxlKHRhYmxlKGZpbHRlcmVkX2RhdGEkb2Jlc2l0eV9zdGF0dXMsIGZpbHRlcmVkX2RhdGEkc2V4KSwgMikNCmthYmxlKGNvbmRpdGlvbmFsX3Byb2Jfb2Jlc2l0eV9zZXgsIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiQ29uZGl0aW9uYWwgUHJvYmFiaWxpdHkgb2YgT2Jlc2l0eSBTdGF0dXMgZ2l2ZW4gU2V4IikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIikpDQoNCiMgQ29uZGl0aW9uYWwgUHJvYmFiaWxpdHkgb2Ygb2Jlc2l0eV9zdGF0dXMgZ2l2ZW4gYWdlNWNhdA0KY29uZGl0aW9uYWxfcHJvYl9vYmVzaXR5X2FnZSA8LSBwcm9wLnRhYmxlKHRhYmxlKGZpbHRlcmVkX2RhdGEkb2Jlc2l0eV9zdGF0dXMsIGZpbHRlcmVkX2RhdGEkYWdlNWNhdCksIDIpDQprYWJsZShjb25kaXRpb25hbF9wcm9iX29iZXNpdHlfYWdlLCBkaWdpdHMgPSAzLCBjYXB0aW9uID0gIkNvbmRpdGlvbmFsIFByb2JhYmlsaXR5IG9mIE9iZXNpdHkgU3RhdHVzIGdpdmVuIEFnZSBDYXRlZ29yeSIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpKQ0KDQojIENvbmRpdGlvbmFsIFByb2JhYmlsaXR5IG9mIG9iZXNpdHlfc3RhdHVzIGdpdmVuIGluY29tZV9ncm91cA0KY29uZGl0aW9uYWxfcHJvYl9vYmVzaXR5X2luY29tZSA8LSBwcm9wLnRhYmxlKHRhYmxlKGZpbHRlcmVkX2RhdGEkb2Jlc2l0eV9zdGF0dXMsIGZpbHRlcmVkX2RhdGEkaW5jb21lX2dyb3VwKSwgMikNCmthYmxlKGNvbmRpdGlvbmFsX3Byb2Jfb2Jlc2l0eV9pbmNvbWUsIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiQ29uZGl0aW9uYWwgUHJvYmFiaWxpdHkgb2YgT2Jlc2l0eSBTdGF0dXMgZ2l2ZW4gSW5jb21lIEdyb3VwIikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIikpDQoNCmBgYA0KDQpJbiBoZXJlIHdlIHdhbnRlZCB0byBzZWUgdGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIG9iZXNlIG9jY3VycmluZyBnaXZlbiB0aGF0IGFub3RoZXIgZXZlbnQgaGFzIGFscmVhZHkgb2NjdXJyZWQgd2hpY2ggaXMgKipDb25kaXRpb25hbCBQcm9iYWJpbGl0eSoqLiBJdCBpcyBjYWxjdWxhdGVkIGJ5IGRpdmlkaW5nIHRoZSBqb2ludCBwcm9iYWJpbGl0eSBvZiB0aGUgdHdvIGV2ZW50cyBieSB0aGUgcHJvYmFiaWxpdHkgb2YgdGhlIGNvbmRpdGlvbmluZyBldmVudC4gPGJyPg0KDQoqKlNleCBEaWZmZXJlbmNlcyBpbiBPYmVzaXR5OioqDQpUaGUgam9pbnQgcHJvYmFiaWxpdGllcyBpbmRpY2F0ZWQgdGhhdCBmZW1hbGVzIGhhdmUgYSBoaWdoZXIgcHJvYmFiaWxpdHkgb2YgYmVpbmcgb2Jlc2UgKDQyLjMlKSBjb21wYXJlZCB0byBtYWxlcyAoMzcuNCUpLiA8YnI+DQoNCioqQWdlIERpZmZlcmVuY2VzIGluIE9iZXNpdHk6KioNClRoZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0aWVzIHNob3cgYW4gaW5jcmVhc2luZyB0cmVuZCBvZiBvYmVzaXR5IHdpdGggYWdlLCBwZWFraW5nIGF0IDQ0LjklIGZvciB0aGUgNDAgdG8gNTQgYWdlIGdyb3VwLCBhbmQgdGhlbiBkZWNyZWFzaW5nIHRvIDMzLjUlIGZvciB0aG9zZSBhZ2VkIDcwIGFuZCBvbGRlci4gPGJyPg0KDQoqKkluY29tZSBHcm91cCBEaWZmZXJlbmNlcyBpbiBPYmVzaXR5OioqDQpUaGUgY29uZGl0aW9uYWwgcHJvYmFiaWxpdGllcyByZXZlYWwgdGhhdCB0aGUgbG93LWluY29tZSBncm91cCBoYXMgdGhlIGhpZ2hlc3QgcHJvYmFiaWxpdHkgb2YgYmVpbmcgb2Jlc2UgKDQxLjglKSwgZm9sbG93ZWQgYnkgdGhlIG1pZGRsZS1pbmNvbWUgZ3JvdXAgKDQwLjQlKSwgYW5kIHRoZSBoaWdoLWluY29tZSBncm91cCAoMzcuNiUpLiA8YnI+DQoNCg0KIyBUaGUgTW9kZWxzDQoNCg0KIyMgTXVsdGlub21pYWwgTG9naXN0aWMgUmVncmVzc2lvbg0KYGBge3J9DQpsaWJyYXJ5KFZHQU0pDQoNCiMgRml0IGEgbXVsdGlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbA0KbXVsdGlub21fbW9kZWwgPC0gdmdsbShibWkgfiBocnRkaXNlYXNlICsgZGlhYmV0aWNfc3RhdHVzICsgYWdlNWNhdCArIHNleCArIGVkdSArIGluY29tZV9ncm91cCArIHRvdGluZGEgKyBzbW9rZWRheSArIGdlbmhsdGgsIGZhbWlseSA9IG11bHRpbm9taWFsKHJlZkxldmVsID0gNCksIGRhdGEgPSBmaWx0ZXJlZF9kYXRhKQ0KDQojIFN1bW1hcnkgb2YgdGhlIG11bHRpbm9taWFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCnN1bW1hcnkobXVsdGlub21fbW9kZWwpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJ5DQpsaWJyYXJ5KFZHQU0pDQoNCiMgRXh0cmFjdCBjb2VmZmljaWVudHMNCmNvZWZfbXVsdGlub20gPC0gY29lZihtdWx0aW5vbV9tb2RlbCkNCg0KIyBFeHRyYWN0IHN0YW5kYXJkIGVycm9ycw0Kc3RkX2VyciA8LSBzcXJ0KGRpYWcodmNvdihtdWx0aW5vbV9tb2RlbCkpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMgQ29udmVydCBjb2VmZmljaWVudHMgdG8gYSBtYXRyaXggd2l0aCAzIGNvbHVtbnMgKGZvciAzIGNhdGVnb3JpZXM6IFVuZGVyd2VpZ2h0LCBOb3JtYWwgV2VpZ2h0LCBPdmVyd2VpZ2h0KQ0KY29lZl9tYXRyaXggPC0gbWF0cml4KGNvZWZfbXVsdGlub20sIG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpDQpzdGRfZXJyX21hdHJpeCA8LSBtYXRyaXgoc3RkX2VyciwgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSkNCg0KIyBOYW1lIHRoZSBjb2x1bW5zIGFwcHJvcHJpYXRlbHkNCmNvbG5hbWVzKGNvZWZfbWF0cml4KSA8LSBjKCJVbmRlcndlaWdodCIsICJOb3JtYWwgV2VpZ2h0IiwgIk92ZXJ3ZWlnaHQiKQ0KY29sbmFtZXMoc3RkX2Vycl9tYXRyaXgpIDwtIGMoIlVuZGVyd2VpZ2h0IiwgIk5vcm1hbCBXZWlnaHQiLCAiT3ZlcndlaWdodCIpDQoNCiMgQ29udmVydCB0byBkYXRhIGZyYW1lcyBmb3IgZWFzaWVyIG1hbmlwdWxhdGlvbg0KY29lZl9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvZWZfbWF0cml4KQ0Kc3RkX2Vycl9kZiA8LSBhcy5kYXRhLmZyYW1lKHN0ZF9lcnJfbWF0cml4KQ0KDQojIEV4dHJhY3QgcHJlZGljdG9yIG5hbWVzIGZyb20gdGhlIHJvdyBuYW1lcyBvZiB0aGUgY29lZmZpY2llbnRzIG1hdHJpeA0KcHJlZGljdG9yX25hbWVzIDwtIHJvd25hbWVzKGNvZWYobXVsdGlub21fbW9kZWwpKQ0KDQojIEFzc2lnbiBwcmVkaWN0b3IgbmFtZXMgdG8gdGhlIGRhdGEgZnJhbWVzDQpyb3duYW1lcyhjb2VmX2RmKSA8LSBwcmVkaWN0b3JfbmFtZXMNCnJvd25hbWVzKHN0ZF9lcnJfZGYpIDwtIHByZWRpY3Rvcl9uYW1lcw0KDQpgYGANCg0KDQpgYGB7cn0NCg0KIyBDYWxjdWxhdGUgb2RkcyByYXRpb3MgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQpvZGRzX3JhdGlvcyA8LSBleHAoY29lZl9kZikNCmNvbmZfaW50X2xvd2VyIDwtIGV4cChjb2VmX2RmIC0gMS45NiAqIHN0ZF9lcnJfZGYpDQpjb25mX2ludF91cHBlciA8LSBleHAoY29lZl9kZiArIDEuOTYgKiBzdGRfZXJyX2RmKQ0KDQpgYGANCg0KYGBge3J9DQojIENvbnZlcnQgdG8gbG9uZyBmb3JtYXQgZm9yIHBsb3R0aW5nDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KDQojIEFkZGluZyBhIHByZWRpY3RvciBjb2x1bW4gZm9yIG1lbHRpbmcNCm9kZHNfcmF0aW9zJFByZWRpY3RvciA8LSByb3duYW1lcyhvZGRzX3JhdGlvcykNCmNvbmZfaW50X2xvd2VyJFByZWRpY3RvciA8LSByb3duYW1lcyhjb25mX2ludF9sb3dlcikNCmNvbmZfaW50X3VwcGVyJFByZWRpY3RvciA8LSByb3duYW1lcyhjb25mX2ludF91cHBlcikNCg0KIyBNZWx0aW5nIHRoZSBkYXRhIGZyYW1lcw0Kb2Rkc19yYXRpb3NfbG9uZyA8LSBtZWx0KG9kZHNfcmF0aW9zLCBpZC52YXJzID0gIlByZWRpY3RvciIsIHZhcmlhYmxlLm5hbWUgPSAiQk1JX0NhdGVnb3J5IiwgdmFsdWUubmFtZSA9ICJPZGRzX1JhdGlvIikNCmNvbmZfaW50X2xvd2VyX2xvbmcgPC0gbWVsdChjb25mX2ludF9sb3dlciwgaWQudmFycyA9ICJQcmVkaWN0b3IiLCB2YXJpYWJsZS5uYW1lID0gIkJNSV9DYXRlZ29yeSIsIHZhbHVlLm5hbWUgPSAiQ0lfTG93ZXIiKQ0KY29uZl9pbnRfdXBwZXJfbG9uZyA8LSBtZWx0KGNvbmZfaW50X3VwcGVyLCBpZC52YXJzID0gIlByZWRpY3RvciIsIHZhcmlhYmxlLm5hbWUgPSAiQk1JX0NhdGVnb3J5IiwgdmFsdWUubmFtZSA9ICJDSV9VcHBlciIpDQoNCmBgYA0KDQpgYGB7cn0NCiMgTWVyZ2UgdGhlIGRhdGEgZnJhbWVzDQpjb25mX2ludF9kZiA8LSBtZXJnZShvZGRzX3JhdGlvc19sb25nLCBjb25mX2ludF9sb3dlcl9sb25nLCBieSA9IGMoIlByZWRpY3RvciIsICJCTUlfQ2F0ZWdvcnkiKSkNCmNvbmZfaW50X2RmIDwtIG1lcmdlKGNvbmZfaW50X2RmLCBjb25mX2ludF91cHBlcl9sb25nLCBieSA9IGMoIlByZWRpY3RvciIsICJCTUlfQ2F0ZWdvcnkiKSkNCg0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgUGxvdA0KZ2dwbG90KGNvbmZfaW50X2RmLCBhZXMoeCA9IE9kZHNfUmF0aW8sIHkgPSBQcmVkaWN0b3IsIGNvbG9yID0gQk1JX0NhdGVnb3J5KSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGdlb21fZXJyb3JiYXJoKGFlcyh4bWluID0gQ0lfTG93ZXIsIHhtYXggPSBDSV9VcHBlciksIGhlaWdodCA9IDAuMikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIkZvcmVzdCBQbG90IG9mIE9kZHMgUmF0aW9zIHdpdGggQ29uZmlkZW5jZSBJbnRlcnZhbHMiLCB4ID0gIk9kZHMgUmF0aW8iLCB5ID0gIlByZWRpY3RvciIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gJ2xvZzEwJykgKw0KICBmYWNldF93cmFwKH4gQk1JX0NhdGVnb3J5LCBzY2FsZXMgPSAiZnJlZV95IikNCg0KYGBgDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkoVkdBTSkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgYW5kIHN0YW5kYXJkIGVycm9ycw0KY29lZl9tdWx0aW5vbSA8LSBjb2VmKG11bHRpbm9tX21vZGVsKQ0Kc3RkX2VyciA8LSBzcXJ0KGRpYWcodmNvdihtdWx0aW5vbV9tb2RlbCkpKQ0KDQojIFJlc2hhcGUgY29lZmZpY2llbnRzIGFuZCBzdGFuZGFyZCBlcnJvcnMgaW50byBtYXRyaWNlcw0KY29lZl9tYXRyaXggPC0gbWF0cml4KGNvZWZfbXVsdGlub20sIG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpDQpzdGRfZXJyX21hdHJpeCA8LSBtYXRyaXgoc3RkX2VyciwgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSkNCg0KIyBOYW1lIHRoZSBjb2x1bW5zIGFwcHJvcHJpYXRlbHkNCmNvbG5hbWVzKGNvZWZfbWF0cml4KSA8LSBjKCJVbmRlcndlaWdodCIsICJOb3JtYWwgV2VpZ2h0IiwgIk92ZXJ3ZWlnaHQiKQ0KY29sbmFtZXMoc3RkX2Vycl9tYXRyaXgpIDwtIGMoIlVuZGVyd2VpZ2h0IiwgIk5vcm1hbCBXZWlnaHQiLCAiT3ZlcndlaWdodCIpDQoNCiMgQ29udmVydCB0byBkYXRhIGZyYW1lcyBmb3IgZWFzaWVyIG1hbmlwdWxhdGlvbg0KY29lZl9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvZWZfbWF0cml4KQ0Kc3RkX2Vycl9kZiA8LSBhcy5kYXRhLmZyYW1lKHN0ZF9lcnJfbWF0cml4KQ0KDQojIERlZmluZSBwcmVkaWN0b3IgbmFtZXMNCnByZWRpY3Rvcl9uYW1lcyA8LSBjKCIoSW50ZXJjZXB0KSIsICJIZWFydCBEaXNlYXNlIiwgIkRpYWJldGVzIFN0YXR1cyIsICJBZ2UgMzAgdG8gMzkiLCAiQWdlIDQwIHRvIDU0IiwgDQogICAgICAgICAgICAgICAgICAgICAiQWdlIDU1IHRvIDY5IiwgIkFnZSA3MCBhbmQgb2xkZXIiLCAiU2V4IiwgIkVsZW1lbnRhcnkgRWR1Y2F0aW9uIiwgIkhpZ2ggU2Nob29sIEVkdWNhdGlvbiIsIA0KICAgICAgICAgICAgICAgICAgICAgIk1pZGRsZSBJbmNvbWUiLCAiSGlnaCBJbmNvbWUiLCAiUGh5c2ljYWwgQWN0aXZpdHkiLCAiU21va2VkYXkyIiwgIlNtb2tlZGF5MyIsIA0KICAgICAgICAgICAgICAgICAgICAgIkdlbmVyYWwgSGVhbHRoIDIiLCAiR2VuZXJhbCBIZWFsdGggMyIsICJHZW5lcmFsIEhlYWx0aCA0IiwgIkdlbmVyYWwgSGVhbHRoIDUiKQ0KDQojIEFzc2lnbiBwcmVkaWN0b3IgbmFtZXMgdG8gdGhlIGRhdGEgZnJhbWVzDQpyb3duYW1lcyhjb2VmX2RmKSA8LSBwcmVkaWN0b3JfbmFtZXMNCnJvd25hbWVzKHN0ZF9lcnJfZGYpIDwtIHByZWRpY3Rvcl9uYW1lcw0KDQojIENhbGN1bGF0ZSBvZGRzIHJhdGlvcyBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMNCm9kZHNfcmF0aW9zIDwtIGV4cChjb2VmX2RmKQ0KY29uZl9pbnRfbG93ZXIgPC0gZXhwKGNvZWZfZGYgLSAxLjk2ICogc3RkX2Vycl9kZikNCmNvbmZfaW50X3VwcGVyIDwtIGV4cChjb2VmX2RmICsgMS45NiAqIHN0ZF9lcnJfZGYpDQoNCiMgQWRkaW5nIGEgcHJlZGljdG9yIGNvbHVtbiBmb3IgbWVsdGluZw0Kb2Rkc19yYXRpb3MkUHJlZGljdG9yIDwtIHJvd25hbWVzKG9kZHNfcmF0aW9zKQ0KY29uZl9pbnRfbG93ZXIkUHJlZGljdG9yIDwtIHJvd25hbWVzKGNvbmZfaW50X2xvd2VyKQ0KY29uZl9pbnRfdXBwZXIkUHJlZGljdG9yIDwtIHJvd25hbWVzKGNvbmZfaW50X3VwcGVyKQ0KDQojIE1lbHRpbmcgdGhlIGRhdGEgZnJhbWVzDQpvZGRzX3JhdGlvc19sb25nIDwtIG1lbHQob2Rkc19yYXRpb3MsIGlkLnZhcnMgPSAiUHJlZGljdG9yIiwgdmFyaWFibGUubmFtZSA9ICJCTUlfQ2F0ZWdvcnkiLCB2YWx1ZS5uYW1lID0gIk9kZHNfUmF0aW8iKQ0KY29uZl9pbnRfbG93ZXJfbG9uZyA8LSBtZWx0KGNvbmZfaW50X2xvd2VyLCBpZC52YXJzID0gIlByZWRpY3RvciIsIHZhcmlhYmxlLm5hbWUgPSAiQk1JX0NhdGVnb3J5IiwgdmFsdWUubmFtZSA9ICJDSV9Mb3dlciIpDQpjb25mX2ludF91cHBlcl9sb25nIDwtIG1lbHQoY29uZl9pbnRfdXBwZXIsIGlkLnZhcnMgPSAiUHJlZGljdG9yIiwgdmFyaWFibGUubmFtZSA9ICJCTUlfQ2F0ZWdvcnkiLCB2YWx1ZS5uYW1lID0gIkNJX1VwcGVyIikNCg0KIyBNZXJnZSB0aGUgZGF0YSBmcmFtZXMNCmNvbmZfaW50X2RmIDwtIG1lcmdlKG9kZHNfcmF0aW9zX2xvbmcsIGNvbmZfaW50X2xvd2VyX2xvbmcsIGJ5ID0gYygiUHJlZGljdG9yIiwgIkJNSV9DYXRlZ29yeSIpKQ0KY29uZl9pbnRfZGYgPC0gbWVyZ2UoY29uZl9pbnRfZGYsIGNvbmZfaW50X3VwcGVyX2xvbmcsIGJ5ID0gYygiUHJlZGljdG9yIiwgIkJNSV9DYXRlZ29yeSIpKQ0KDQojIEZ1bmN0aW9uIHRvIGNyZWF0ZSBpbmRpdmlkdWFsIHBsb3RzIGZvciBlYWNoIEJNSSBjYXRlZ29yeSB3aXRoIGRpZmZlcmVudCBjb2xvcnMNCmNyZWF0ZV9mb3Jlc3RfcGxvdCA8LSBmdW5jdGlvbihkZiwgY2F0ZWdvcnksIGNvbG9yKSB7DQogIGdncGxvdChkZltkZiRCTUlfQ2F0ZWdvcnkgPT0gY2F0ZWdvcnksIF0sIGFlcyh4ID0gT2Rkc19SYXRpbywgeSA9IFByZWRpY3RvcikpICsNCiAgICBnZW9tX3BvaW50KHNpemUgPSAyLCBjb2xvciA9IGNvbG9yKSArDQogICAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtaW4gPSBDSV9Mb3dlciwgeG1heCA9IENJX1VwcGVyKSwgaGVpZ2h0ID0gMC4yLCBjb2xvciA9IGNvbG9yKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkZvcmVzdCBQbG90IG9mIE9kZHMgUmF0aW9zIHdpdGggQ29uZmlkZW5jZSBJbnRlcnZhbHNcbiIsIGNhdGVnb3J5KSwgDQogICAgICAgICB4ID0gIk9kZHMgUmF0aW8iLCB5ID0gIlByZWRpY3RvciIpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAnbG9nMTAnKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBhbmdsZSA9IDAsIGhqdXN0ID0gMSkpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQp9DQoNCiMgQ3JlYXRlIGluZGl2aWR1YWwgcGxvdHMgd2l0aCBkaWZmZXJlbnQgY29sb3JzDQpwbG90X3VuZGVyd2VpZ2h0IDwtIGNyZWF0ZV9mb3Jlc3RfcGxvdChjb25mX2ludF9kZiwgIlVuZGVyd2VpZ2h0IiwgInJlZCIpDQpwbG90X25vcm1hbF93ZWlnaHQgPC0gY3JlYXRlX2ZvcmVzdF9wbG90KGNvbmZfaW50X2RmLCAiTm9ybWFsIFdlaWdodCIsICJkYXJrZ3JlZW4iKQ0KcGxvdF9vdmVyd2VpZ2h0IDwtIGNyZWF0ZV9mb3Jlc3RfcGxvdChjb25mX2ludF9kZiwgIk92ZXJ3ZWlnaHQiLCAiYmx1ZTIiKQ0KDQojIERpc3BsYXkgaW5kaXZpZHVhbCBwbG90cw0KcHJpbnQocGxvdF91bmRlcndlaWdodCkNCnByaW50KHBsb3Rfbm9ybWFsX3dlaWdodCkNCnByaW50KHBsb3Rfb3ZlcndlaWdodCkNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KIyMgT3JkaW5hbCBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCmBgYHtyfQ0KIyBGaXQgTW9kZWwgMSAoQWdlLCBHZW5kZXIsIE1hcml0YWwsIEVkdWNhdGlvbikNCmZpdDEgPC0gdmdsbShibWkgfiBhZ2U1Y2F0ICsgc2V4ICsgbWFyaXRhbCArIGVkdSwgZmFtaWx5ID0gY3VtdWxhdGl2ZShwYXJhbGxlbCA9IFRSVUUpLCBkYXRhID0gZmlsdGVyZWRfZGF0YSkNCg0KIyBGaXQgTW9kZWwgMiAoQWRkIGhlYWx0aCB2YXJpYWJsZXMpDQpmaXQyIDwtIHZnbG0oYm1pIH4gYWdlNWNhdCArIHNleCArIG1hcml0YWwgKyBlZHUgKyBnZW5obHRoICsgaHJ0ZGlzZWFzZSArIGRpYWJldGljX3N0YXR1cyArIHNtb2tlZGF5LCBmYW1pbHkgPSBjdW11bGF0aXZlKHBhcmFsbGVsID0gVFJVRSksIGRhdGEgPSBmaWx0ZXJlZF9kYXRhKQ0KDQojIEZpdCBNb2RlbCAzIChBZGQgRW1wbG95bWVudCBTdGF0dXMgYW5kIGluY29tZSkNCmZpdDMgPC0gdmdsbShibWkgfiBhZ2U1Y2F0ICsgc2V4ICsgbWFyaXRhbCArIGVkdSArIGdlbmhsdGggKyBocnRkaXNlYXNlICsgZGlhYmV0aWNfc3RhdHVzICsgc21va2VkYXkgKyBpbmNvbWVfZ3JvdXAgKyBlbXBsb3lzdGF0dXMsIGZhbWlseSA9IGN1bXVsYXRpdmUocGFyYWxsZWwgPSBUUlVFKSwgZGF0YSA9IGZpbHRlcmVkX2RhdGEpDQoNCiMgQ2FsY3VsYXRlIEFJQyBhbmQgQklDIGZvciBhbGwgdGhyZWUgbW9kZWxzDQphaWNfZml0MSA8LSBBSUMoZml0MSkNCmFpY19maXQyIDwtIEFJQyhmaXQyKQ0KYWljX2ZpdDMgPC0gQUlDKGZpdDMpDQoNCmJpY19maXQxIDwtIEJJQyhmaXQxKQ0KYmljX2ZpdDIgPC0gQklDKGZpdDIpDQpiaWNfZml0MyA8LSBCSUMoZml0MykNCg0KIyBQcmludCBBSUMgYW5kIEJJQyB2YWx1ZXMgZm9yIGNvbXBhcmlzb24NCmNhdCgiTW9kZWwgMSBBSUM6IiwgYWljX2ZpdDEsICJcbiIpDQpjYXQoIk1vZGVsIDIgQUlDOiIsIGFpY19maXQyLCAiXG4iKQ0KY2F0KCJNb2RlbCAzIEFJQzoiLCBhaWNfZml0MywgIlxuIikNCg0KY2F0KCJNb2RlbCAxIEJJQzoiLCBiaWNfZml0MSwgIlxuIikNCmNhdCgiTW9kZWwgMiBCSUM6IiwgYmljX2ZpdDIsICJcbiIpDQpjYXQoIk1vZGVsIDMgQklDOiIsIGJpY19maXQzLCAiXG4iKQ0KDQojIERldGVybWluZSB0aGUgYmVzdCBtb2RlbCBiYXNlZCBvbiBBSUMgYW5kIEJJQw0KaWYgKGFpY19maXQxIDwgYWljX2ZpdDIgJiYgYWljX2ZpdDEgPCBhaWNfZml0MyAmJiBiaWNfZml0MSA8IGJpY19maXQyICYmIGJpY19maXQxIDwgYmljX2ZpdDMpIHsNCiAgICBjYXQoIk1vZGVsIDEgaXMgYmV0dGVyIGJhc2VkIG9uIEFJQyBhbmQgQklDLlxuIikNCiAgICBzdW1tYXJ5KGZpdDEpDQp9IGVsc2UgaWYgKGFpY19maXQyIDwgYWljX2ZpdDEgJiYgYWljX2ZpdDIgPCBhaWNfZml0MyAmJiBiaWNfZml0MiA8IGJpY19maXQxICYmIGJpY19maXQyIDwgYmljX2ZpdDMpIHsNCiAgICBjYXQoIk1vZGVsIDIgaXMgYmV0dGVyIGJhc2VkIG9uIEFJQyBhbmQgQklDLlxuIikNCiAgICBzdW1tYXJ5KGZpdDIpDQp9IGVsc2Ugew0KICAgIGNhdCgiTW9kZWwgMyBpcyBiZXR0ZXIgYmFzZWQgb24gQUlDIGFuZCBCSUMuXG4iKQ0KICAgIHN1bW1hcnkoZml0MykNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IGludGVyY2VwdHMgZnJvbSB0aGUgYmVzdCBtb2RlbCAoZml0MykNCmludGVyY2VwdHMgPC0gY29lZihmaXQzKVtncmVwbCgiSW50ZXJjZXB0IiwgbmFtZXMoY29lZihmaXQzKSkpXQ0KaW50ZXJjZXB0X25hbWVzIDwtIG5hbWVzKGludGVyY2VwdHMpDQoNCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcNCmludGVyY2VwdF9kYXRhIDwtIGRhdGEuZnJhbWUoDQogIENhdGVnb3J5ID0gZmFjdG9yKGludGVyY2VwdF9uYW1lcywgbGV2ZWxzID0gaW50ZXJjZXB0X25hbWVzKSwNCiAgRXN0aW1hdGUgPSBpbnRlcmNlcHRzDQopDQoNCiMgUGxvdCB0aGUgaW50ZXJjZXB0cw0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoaW50ZXJjZXB0X2RhdGEsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBFc3RpbWF0ZSkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDMpICsNCiAgZ2VvbV9saW5lKGdyb3VwID0gMSwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJJbnRlcmNlcHRzIGZvciBCTUkgQ2F0ZWdvcmllcyIsDQogICAgICAgeCA9ICJCTUkgQ2F0ZWdvcnkgVGhyZXNob2xkcyIsDQogICAgICAgeSA9ICJJbnRlcmNlcHQgRXN0aW1hdGUiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQoNCg0KYGBgDQpgYGB7cn0NCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQpsaWJyYXJ5KFZHQU0pDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIEV4dHJhY3QgY29lZmZpY2llbnRzDQpjb2VmX3ZhbHVlcyA8LSBjb2VmKGZpdDMpDQppbnRlcmNlcHRzIDwtIGNvZWZfdmFsdWVzW2dyZXBsKCJJbnRlcmNlcHQiLCBuYW1lcyhjb2VmX3ZhbHVlcykpXQ0KYmV0YXMgPC0gY29lZl92YWx1ZXNbIWdyZXBsKCJJbnRlcmNlcHQiLCBuYW1lcyhjb2VmX3ZhbHVlcykpXQ0KYmV0YV9uYW1lcyA8LSBuYW1lcyhiZXRhcykNCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciBwbG90dGluZyBiZXRhcyB3aXRoIGRpZmZlcmVudCBpbnRlcmNlcHRzDQpwbG90X2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgQ29lZmZpY2llbnQgPSByZXAoYmV0YV9uYW1lcywgMyksDQogIEVzdGltYXRlID0gcmVwKGJldGFzLCAzKSwNCiAgQ2F0ZWdvcnkgPSByZXAoYygiVW5kZXJ3ZWlnaHQiLCAiTm9ybWFsX3dlaWdodCIsICJPdmVyd2VpZ2h0IiksIGVhY2ggPSBsZW5ndGgoYmV0YXMpKSwNCiAgSW50ZXJjZXB0ID0gcmVwKGludGVyY2VwdHMsIGVhY2ggPSBsZW5ndGgoYmV0YXMpKQ0KKQ0KDQojIENhbGN1bGF0ZSB0aGUgeS12YWx1ZXMgZm9yIHRoZSBsaW5lcyB3aXRoIGRpZmZlcmVudCBpbnRlcmNlcHRzDQpwbG90X2RhdGEkRml0dGVkX1ZhbHVlIDwtIHBsb3RfZGF0YSRFc3RpbWF0ZSArIHBsb3RfZGF0YSRJbnRlcmNlcHQNCg0KIyBQbG90DQpnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IENvZWZmaWNpZW50LCB5ID0gRml0dGVkX1ZhbHVlLCBjb2xvciA9IENhdGVnb3J5LCBncm91cCA9IENhdGVnb3J5KSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJCZXRhcyBmb3IgQk1JIENhdGVnb3JpZXMgd2l0aCBEaWZmZXJlbnQgSW50ZXJjZXB0cyIsDQogICAgICAgeCA9ICJQcmVkaWN0b3IiLA0KICAgICAgIHkgPSAiRml0dGVkIFZhbHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=