Required packages

Following packages have been used in this report:

library(readr) # Used for importing csv files
library(tidyr) # Used for tidying data
library(dplyr) # Used for data manipulation
library(knitr) # Used for creating nice tables
library(kableExtra) # Used for table formatting
library(magrittr) # Used for pipe operation
library(stringi) # Used for string manipulation
library(editrules) # Used to deal with inconsistencies in data

Executive Summary

The aim of this report is to carry out data pre-processing. This involves - importing, understanding, tidying, manipulating, scanning and transforming data.

First, we identify two open datasets (country wise malnutrition and life expectancy) having at least one common attribute (country) and import them into the R workspace. Next we subset the data by dropping unwanted columns and rename the columns suitably. The untidy dataset is reshaped into tidy format, according to the tidy data principles. To avoid any inconsistencies while merging the datasets, we perform upper case conversion on the common column ‘Country’. We use left join to merge the datasets. It ensures any data loss as a result of merging does not affect the research goal.

Next, we understand the data by checking its volume, attributes and structure. We validate if all the different data types of variables, are classified correctly and apply type conversions for incorrect classifications.

We demonstrate data manipulation through creation of a new column from the existing columns.

Next, we scan for missing values and choose to omit the NA values. Replacing it with a constant/mean/median value wouldn’t make sense in our case, as each of the values are country specific and cannot be approximated. Dataset is scanned for special values and obvious errors. It is validated against pre-defined rules and no issues are observed in the summary of errors.

Tukey’s method of outlier detection is used to plot the box plot for numerical variables. Observed outliers for population column are valid and not handled. For example, countries like India, China etc have very large population compared to other countries. On a similar reasoning, since the outliers observed in all the box plots are actual valid values, we retain the outliers. The distribution of all numerical variables are explored through histograms. To decrease the right skewness of one of the variable and convert it to a normal distribution, we apply the square root transformation.

All the above steps ensure that our data is error free, consistent and ready for further statistical analysis.

Data

Data is sourced from Kaggle.

The first open data set used in this assignment, consists of country wise life expectancy data segregated by gender (male/female) and also the overall life expectancy.
Dataset 1 URL: https://www.kaggle.com/amansaxena/lifeexpectancy?select=Life_expectancy_dataset.csv

The second data set comprises of malnutrition data of countries across the globe from 1983 to 2019.
Dataset 2 URL: https://www.kaggle.com/ruchi798/malnutrition-across-the-globe?select=country-wise-average.csv

Variables description:
Data set 1 - Life_expectancy_dataset.csv
Rank: Numerical variable, life expectancy rank
Country: Character variable, name of the country
Overall Life: Numerical variable, overall life expectancy of male and female population
Male Life: Numerical variable, life expectancy of males
Female Life: Numerical variable, life expectancy of females
Continent: Categorical variable with 6 levels, continent to which a country belongs to - Africa, Europe, Asia, Oceania, North America, South America

Data set 2 - country-wise-average.csv
Country: Character variable, name of the country
Income Classification: Categorical variable with 4 levels, Low Income: 0, Lower Middle Income: 1, Upper Middle Income: 2, High Income: 3
Severe Wasting: Numerical variable, average severe wasting percentage. It indicates the percentage of children aged under 5 years who are below minus three standard deviations from median weight-for-height
Wasting: Numerical variable, average wasting percentage. It indicates the percentage of children aged under 5 years who are below minus two standard deviations from median weight-for-height
Overweight: Numerical variable, average overweight percentage. It indicates the percentage of children aged under 5 years who are above two standard deviations from median weight-for-height
Stunting: Numerical variable, average stunting percentage. It indicates the percentage of children aged under 5 years who are below minus two standard deviations from median height-for-age
Underweight: Numerical variable, average underweight percentage. It indicates the percentage of children aged under 5 years who are below minus two standard deviations from median weight-for-age
U5 Population (`000s): Numerical variable, population of children below the age of five, expressed in thousands

Read in the data sets using read_csv() function. Rename the columns appropriately and drop any columns that are not required.

#Research goal - To compare and correlate malnutrition to average life expectancy.
df1 <- read_csv("Life_expectancy_dataset.csv") # Read first data set

── Column specification ─────────────────────────────────────────────────────────────────────────────
cols(
  Rank = col_double(),
  Country = col_character(),
  `Overall Life` = col_double(),
  `Male Life` = col_double(),
  `Female Life` = col_double(),
  Continent = col_character()
)
colnames(df1)[1] <- "Global Rank" # Rename column name "Rank" to "Global Rank"
colnames(df1)[4] <- "Male" # Rename column name "Male Life" to "Male"
colnames(df1)[5] <- "Female" # Rename column name "Female Life" to "Female"
df1 <- df1[ -c(3) ] # Drop the overall "Overall Life" column, as it is not required

df2 <- read_csv("country-wise-average.csv") # Read second data set

── Column specification ─────────────────────────────────────────────────────────────────────────────
cols(
  Country = col_character(),
  `Income Classification` = col_double(),
  `Severe Wasting` = col_double(),
  Wasting = col_double(),
  Overweight = col_double(),
  Stunting = col_double(),
  Underweight = col_double(),
  `U5 Population ('000s)` = col_double()
)


Let us take a look at the top 6 rows of our datasets.

Data set 1

knitr::kable(head(df1), format = "html", align = 'l')%>%
kable_styling(bootstrap_options = c("striped")) # Head of data set 1 - untidy format
Global Rank Country Male Female Continent
1 Monaco 85.6 93.5 Europe
2 Japan 81.7 88.5 Asia
3 Singapore 82.3 87.8 Asia
4 Macau; China 81.6 87.6 Asia
5 San Marino 80.7 86.1 Europe
6 Iceland 80.9 85.3 Europe


Our first data set is in untidy format as column names (Male and Female) are values (of life expectancy) instead of variables and doesn’t satisfy the tidy data principles. Let us convert it to tidy format using pivot_longer() function from the tidyr package and check the top 6 rows.

df1_tidy <- pivot_longer(df1, names_to = "Gender", values_to = "Life Expectancy", cols = 3:4) # Convert to tidy format

knitr::kable(head(df1_tidy), format = "html", align = 'l')%>%
kable_styling(bootstrap_options = c("striped")) # Display top 6 rows of the tidy dataset 1
Global Rank Country Continent Gender Life Expectancy
1 Monaco Europe Male 85.6
1 Monaco Europe Female 93.5
2 Japan Asia Male 81.7
2 Japan Asia Female 88.5
3 Singapore Asia Male 82.3
3 Singapore Asia Female 87.8


Now, lets take a peek into the Data set 2

knitr::kable(head(df2), format = "html", align = 'l')%>%
kable_styling(bootstrap_options = c("striped")) # Head of data set 2 
Country Income Classification Severe Wasting Wasting Overweight Stunting Underweight U5 Population ('000s)
AFGHANISTAN 0 3.033333 10.350000 5.12500 47.77500 30.375000 4918.5615
ALBANIA 2 4.075000 7.760000 20.80000 24.16000 7.700000 232.8598
ALGERIA 2 2.733333 5.942857 12.83333 19.57143 7.342857 3565.2131
ANGOLA 1 2.400000 6.933333 2.55000 42.63333 23.600000 3980.0540
ARGENTINA 2 0.200000 2.150000 11.12500 10.02500 2.600000 3613.6517
ARMENIA 2 1.600000 3.940000 13.62000 16.12000 3.480000 204.1452


The second data set is already in tidy format and does not require to be reshaped.

Next we proceed to merge both the data sets, for further analysis.
Since we are trying to compare and correlate malnutrition to average life expectancy, we merge the datasets using a left_join() as per below. Prior to merging, we convert the values of the Country column in df1_tidy to upper case, to match the casing of the common column between the data sets.

df1_tidy$Country <-  stringi::stri_trans_toupper(df1_tidy$Country) #Convert to upper case
#Source - https://stackoverflow.com/questions/9637278/r-tm-package-invalid-input-in-utf8towcs

df_combined <- left_join(df2,df1_tidy,by="Country") # We do a left join for the data sets as we intend to find correlation between malnutrition and life expectancy across the countries.


Lets check the top 6 rows of our combined data set.

knitr::kable(head(df_combined), format = "html", align = 'l')%>%
kable_styling(bootstrap_options = c("striped"))
Country Income Classification Severe Wasting Wasting Overweight Stunting Underweight U5 Population ('000s) Global Rank Continent Gender Life Expectancy
AFGHANISTAN 0 3.033333 10.350000 5.12500 47.77500 30.375000 4918.5615 221 Asia Male 49.9
AFGHANISTAN 0 3.033333 10.350000 5.12500 47.77500 30.375000 4918.5615 221 Asia Female 52.7
ALBANIA 2 4.075000 7.760000 20.80000 24.16000 7.700000 232.8598 59 Europe Male 75.7
ALBANIA 2 4.075000 7.760000 20.80000 24.16000 7.700000 232.8598 59 Europe Female 81.2
ALGERIA 2 2.733333 5.942857 12.83333 19.57143 7.342857 3565.2131 79 Africa Male 75.5
ALGERIA 2 2.733333 5.942857 12.83333 19.57143 7.342857 3565.2131 79 Africa Female 78.2


This meets the minimum requirement 1 of the specification.

Understand

In this section, let us inspect and understand the data by checking its volume, attributes and structure.

The dimensions of the data frame can be checked as below. It consists of 273 rows and 12 columns.

dim(df_combined)
[1] 273  12


str() function gives the structure of the data frame.

str(df_combined)
spec_tbl_df [273 × 12] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Country              : chr [1:273] "AFGHANISTAN" "AFGHANISTAN" "ALBANIA" "ALBANIA" ...
 $ Income Classification: num [1:273] 0 0 2 2 2 2 1 1 2 2 ...
 $ Severe Wasting       : num [1:273] 3.03 3.03 4.08 4.08 2.73 ...
 $ Wasting              : num [1:273] 10.35 10.35 7.76 7.76 5.94 ...
 $ Overweight           : num [1:273] 5.12 5.12 20.8 20.8 12.83 ...
 $ Stunting             : num [1:273] 47.8 47.8 24.2 24.2 19.6 ...
 $ Underweight          : num [1:273] 30.38 30.38 7.7 7.7 7.34 ...
 $ U5 Population ('000s): num [1:273] 4919 4919 233 233 3565 ...
 $ Global Rank          : num [1:273] 221 221 59 59 79 79 207 207 75 75 ...
 $ Continent            : chr [1:273] "Asia" "Asia" "Europe" "Europe" ...
 $ Gender               : chr [1:273] "Male" "Female" "Male" "Female" ...
 $ Life Expectancy      : num [1:273] 49.9 52.7 75.7 81.2 75.5 78.2 54.8 57.2 74 80.4 ...
 - attr(*, "spec")=
  .. cols(
  ..   Country = col_character(),
  ..   `Income Classification` = col_double(),
  ..   `Severe Wasting` = col_double(),
  ..   Wasting = col_double(),
  ..   Overweight = col_double(),
  ..   Stunting = col_double(),
  ..   Underweight = col_double(),
  ..   `U5 Population ('000s)` = col_double()
  .. )


We can see that by default, the read_csv() function has allocated the following data types for each of the columns.

Country - Character variable
Income Classification - Double numeric variable
Severe Wasting - Double numeric variable
Wasting - Double numeric variable
Overweight - Double numeric variable
Stunting - Double numeric variable
Underweight - Double numeric variable
U5 Population (’000s) - Double numeric variable
Global Rank - Double numeric variable
Continent - Character variable
Gender - Character variable
Life Expectancy - Double numeric variable

However, columns - Income Classification, Continent and Gender are incorrectly classified. We can go ahead and perform type conversions on the column names as below -

df_combined$`Income Classification` <- factor(df_combined$`Income Classification`, 
                                              levels = c(0,1,2,3),
                                              labels = c('Low Income', 'Lower Middle Income', 'Upper Middle Income', 'High Income'),
                                              ordered = TRUE) #Convert from numeric to an ordered factor

df_combined$Continent <- factor(df_combined$Continent,
                                levels = c('Africa', 'Europe', 'Asia', 'Oceania', 'North America', 'South America')) # Convert from character to factor
df_combined$Gender <- factor(df_combined$Gender,
                                levels = c('Male', 'Female'))#Convert from character to factor


Lets check the structure of the dataframe again

str(df_combined)
spec_tbl_df [273 × 12] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Country              : chr [1:273] "AFGHANISTAN" "AFGHANISTAN" "ALBANIA" "ALBANIA" ...
 $ Income Classification: Ord.factor w/ 4 levels "Low Income"<"Lower Middle Income"<..: 1 1 3 3 3 3 2 2 3 3 ...
 $ Severe Wasting       : num [1:273] 3.03 3.03 4.08 4.08 2.73 ...
 $ Wasting              : num [1:273] 10.35 10.35 7.76 7.76 5.94 ...
 $ Overweight           : num [1:273] 5.12 5.12 20.8 20.8 12.83 ...
 $ Stunting             : num [1:273] 47.8 47.8 24.2 24.2 19.6 ...
 $ Underweight          : num [1:273] 30.38 30.38 7.7 7.7 7.34 ...
 $ U5 Population ('000s): num [1:273] 4919 4919 233 233 3565 ...
 $ Global Rank          : num [1:273] 221 221 59 59 79 79 207 207 75 75 ...
 $ Continent            : Factor w/ 6 levels "Africa","Europe",..: 3 3 2 2 1 1 1 1 6 6 ...
 $ Gender               : Factor w/ 2 levels "Male","Female": 1 2 1 2 1 2 1 2 1 2 ...
 $ Life Expectancy      : num [1:273] 49.9 52.7 75.7 81.2 75.5 78.2 54.8 57.2 74 80.4 ...
 - attr(*, "spec")=
  .. cols(
  ..   Country = col_character(),
  ..   `Income Classification` = col_double(),
  ..   `Severe Wasting` = col_double(),
  ..   Wasting = col_double(),
  ..   Overweight = col_double(),
  ..   Stunting = col_double(),
  ..   Underweight = col_double(),
  ..   `U5 Population ('000s)` = col_double()
  .. )

We can see that our data set includes multiple data types - character, numeric and factors. Also we’ve applied data type conversions from numeric to factor, and character to factor. We have at least one factor variable which is ordered and labeled. Thus it meets the minimum requirements 2, 3 and 4.

The details of column names, row names, column specifications and class of the variable can be obtained using the attributes() function.

attributes(df_combined) #Describe the attributes of a variable
$names
 [1] "Country"               "Income Classification" "Severe Wasting"        "Wasting"              
 [5] "Overweight"            "Stunting"              "Underweight"           "U5 Population ('000s)"
 [9] "Global Rank"           "Continent"             "Gender"                "Life Expectancy"      

$row.names
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24
 [25]  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48
 [49]  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72
 [73]  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96
 [97]  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
[121] 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
[145] 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
[169] 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
[193] 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
[217] 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
[241] 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
[265] 265 266 267 268 269 270 271 272 273

$spec
cols(
  Country = col_character(),
  `Income Classification` = col_double(),
  `Severe Wasting` = col_double(),
  Wasting = col_double(),
  Overweight = col_double(),
  Stunting = col_double(),
  Underweight = col_double(),
  `U5 Population ('000s)` = col_double()
)

$class
[1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame" 


Tidy & Manipulate Data I

According to the Tidy data principles, if one of the below conditions is violated, then its is categorized as untidy data:
1) Each variable must have its own column.
2) Each observation must have its own row.
3) Each value must have its own cell.

In our case, the first data set “df1” is untidy.
This is because the column names “Male Life” and “Female Life”, represent the values (life expectancy) for the gender variable and each row represents 2 observations and not 1. Since the column headers are values instead of variables, it violates the first condition mentioned above.

We use pivot_longer() function from tidyr package to reshape the data into tidy format. It converts data from wide format to long format.
Required steps to tidy the data have already been applied prior to merging it with data set 2. Kindly refer the section “Data”.

Tidy & Manipulate Data II

Let us create a new variable by using the mutate() function from the dplyr package.

We create a new variable ‘U5 over/under-weight population(’000s)’ to compute the population (in thousands) of children aged below 5 years, who are either underweight or overweight, using the existing columns.

# This is the R chunk for the Tidy & Manipulate Data II 
df_combined_mutated <- mutate(df_combined, 'U5 over/under-weight population(\'000s)'=(df_combined$Overweight+df_combined$Underweight)*df_combined$`U5 Population ('000s)`/100) #Use of mutate function
kable(head(df_combined_mutated[c(1,2,5,7,8,11:13)]), format = "html", align = 'l')%>%
kable_styling(bootstrap_options = c("striped")) # Display the top 6 rows of the mutated and subsetted data frame.
Country Income Classification Overweight Underweight U5 Population ('000s) Gender Life Expectancy U5 over/under-weight population('000s)
AFGHANISTAN Low Income 5.12500 30.375000 4918.5615 Male 49.9 1746.08933
AFGHANISTAN Low Income 5.12500 30.375000 4918.5615 Female 52.7 1746.08933
ALBANIA Upper Middle Income 20.80000 7.700000 232.8598 Male 75.7 66.36504
ALBANIA Upper Middle Income 20.80000 7.700000 232.8598 Female 81.2 66.36504
ALGERIA Upper Middle Income 12.83333 7.342857 3565.2131 Male 75.5 719.32419
ALGERIA Upper Middle Income 12.83333 7.342857 3565.2131 Female 78.2 719.32419


This meets the minimum requirement 6.

Scan I

Let us check for missing values in our data set using is.na() function. Further using the sum() function we can obtain the total count.
We use sapply() function to apply it to the entire data frame.

# This is the R chunk for the Scan I
sapply(df_combined, function(x) sum( is.na(x) )) #Check for missing values
              Country Income Classification        Severe Wasting               Wasting 
                    0                     0                    23                     4 
           Overweight              Stunting           Underweight U5 Population ('000s) 
                    6                     2                     4                     0 
          Global Rank             Continent                Gender       Life Expectancy 
                   31                    31                    31                    31 


As we can see, we have many NA values in our data set. We choose to omit the NA values.Replacing it with a constant/mean/median/mode value doesn’t make much sense in our case as each of the values differ from country to country and cannot be approximated.

The missing values are removed using complete.cases() function.

df_combined <- df_combined[complete.cases(df_combined), ]
sapply(df_combined, function(x) sum( is.na(x) )) #Check for missing values again
              Country Income Classification        Severe Wasting               Wasting 
                    0                     0                     0                     0 
           Overweight              Stunting           Underweight U5 Population ('000s) 
                    0                     0                     0                     0 
          Global Rank             Continent                Gender       Life Expectancy 
                    0                     0                     0                     0 


All missing values are now omitted.


Next, let us scan the data set for Special values.
We create a function called is.special() that returns TRUE if the numerical variable has Inf, -Inf or NaN values.
This is applied to the data set using sapply() function

is.special <- function(x){
if (is.numeric(x)) (is.infinite(x) | is.nan(x))
} #Function to scan for Inf, -Inf and NaN values.

sapply(df_combined, function(x) sum( is.special(x) ))
              Country Income Classification        Severe Wasting               Wasting 
                    0                     0                     0                     0 
           Overweight              Stunting           Underweight U5 Population ('000s) 
                    0                     0                     0                     0 
          Global Rank             Continent                Gender       Life Expectancy 
                    0                     0                     0                     0 


The output indicates that there are no special values.

Further, let us scan the dataset for obvious errors.
We define custom rules and place it in a file called editrules.txt

  1. The categorical variables (factors- Income Classification, Continent, Gender) can hold only predefined values as mentioned below.
  2. Life Expectancy mandatorily needs to be a value between 0 to 120.
Rules <- editfile("editrules.txt", type = "all")#Populate constraints defined under editrules.txt file to Rules variable
Rules #Display the rules

Data model:
dat1 : `Income Classification` %in% c('High Income', 'Low Income', 'Lower Middle Income', 'Upper Middle Income')
dat2 : Continent %in% c('Africa', 'Asia', 'Europe', 'North America', 'Oceania', 'South America')
dat3 : Gender %in% c('Female', 'Male') 

Edit set:
num1 : 0 <= `Life Expectancy`
num2 : `Life Expectancy` <= 100 


Our dataframe is validated against the pre-defined rules using the violatedEdits() function and we obtain the summary of errors.

violated <- violatedEdits(Rules,df_combined)
summary(violated)
No violations detected, 0 checks evaluated to NA
NULL

We do not see any errors, meaning all the values in our dataset satisfy the conditions defined under editfiles.txt
If required, we could use deducorrect package and correctionRules() function to correct the obvious inconsistencies and errors.

Scan II

Using Tukey’s method of outlier detection, we plot the box plot for each of the numerical variables.

df_combined$`U5 Population ('000s)` %>% boxplot(main="Boxplot of under 5 years Population in thousands", ylab="Population", col = "grey") #Boxplot of U5 Population ('000s)


Observation: Eventhough we see outliers, we choose to retaim those values. For example countries like India, China etc have a population over a billion and are identified as valid outliers.

#Box plot of numerical variables
par(mfrow=c(2,2))
df_combined$`Severe Wasting` %>% boxplot(main="Boxplot of severe wasting percentage", ylab="Percentage", col = "grey")
df_combined$`Wasting` %>% boxplot(main="Boxplot of wasting percentage", ylab="Percentage", col = "grey")
df_combined$`Overweight` %>% boxplot(main="Boxplot of over-weight percentage", ylab="Percentage", col = "grey")
df_combined$`Underweight` %>% boxplot(main="Boxplot of under-weight percentage", ylab="Percentage", col = "grey")
par(mfrow=c(1,1))


On a similar reasoning, since the outliers observed in the above box plots (> 1.5 * IQR) are not errors, but actual values which are valid, we do not remove the outliers.

No outliers are detected in the stunting and Life Expectancy columns.

#Boxplot of stunting percentage and life expectancy
par(mfrow=c(1,2))
df_combined$Stunting %>% boxplot(main="Boxplot of stunting percentage", ylab="Percentage", col = "grey")
df_combined$`Life Expectancy` %>% boxplot(main="Boxplot of life expectancy", ylab="age in years", col = "grey")
par(mfrow=c(1,1))

Transform

Let us explore the histograms of all the numerical variables.

# Histogram plots of all numerical variables
par(mfrow =c(3,3))

U5_population_in_thousands <-  df_combined$`U5 Population ('000s)`
hist(U5_population_in_thousands)
severe_wasting <- df_combined$`Severe Wasting`
hist(severe_wasting)
wasting <- df_combined$`Wasting`
hist(wasting)
overweight<- df_combined$`Overweight`
hist(overweight)
stunting <- df_combined$Stunting
hist(stunting)
underweight <- df_combined$`Underweight`
hist(underweight)
life_expectancy <- df_combined$`Life Expectancy`
hist(life_expectancy)

par(mfrow=c(1,1))

To apply a suitable transformation, let us choose the histogram plot of the variable wasting.

hist(wasting)

We observe that this distribution is right skewed. To decrease the right skewness and convert the distribution into a normal distribution we apply the square root transformation

sqrt_wasting <- sqrt(wasting)
hist(sqrt_wasting)

Observation - The square root transformation has reduced the right skewness and has improved the symmetry of the distribution

We can also apply log transformations to reduce the right skewness.

log_wasting <- log10(wasting)
hist(log_wasting)

ln_wasting <- log(wasting)
hist(ln_wasting)

Compared to the log transformations, we can see that square root transformation gives a better result.

References:


1) Create Awesome HTML Table with knitr::kable and kableExtra, Hao Zhu, viewed 13 May 2021: https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html
2) Data set 1: https://www.kaggle.com/amansaxena/lifeexpectancy?select=Life_expectancy_dataset.csv
3) Data set 2: https://www.kaggle.com/ruchi798/malnutrition-across-the-globe?select=country-wise-average.csv
4) Conversion to upper case, viewed 11 May 2021: https://stackoverflow.com/questions/9637278/r-tm-package-invalid-input-in-utf8towcs
5) Concepts discussed under module notes 1 to 7:
https://rare-phoenix-161610.appspot.com/secured/index.html

LS0tCnRpdGxlOiAiRGF0YSBXcmFuZ2xpbmcgKERhdGEgUHJlcHJvY2Vzc2luZykiCmF1dGhvcjogIlllc2hhc3dpIEFyYWxhZ3VwcGUgTXVyYWxpZGhhciAtIFMzODI1ODU2IgpzdWJ0aXRsZTogUHJhY3RpY2FsIGFzc2Vzc21lbnQgMgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgojIyBSZXF1aXJlZCBwYWNrYWdlcyAKCkZvbGxvd2luZyBwYWNrYWdlcyBoYXZlIGJlZW4gdXNlZCBpbiB0aGlzIHJlcG9ydDoKYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQpsaWJyYXJ5KHJlYWRyKSAjIFVzZWQgZm9yIGltcG9ydGluZyBjc3YgZmlsZXMKbGlicmFyeSh0aWR5cikgIyBVc2VkIGZvciB0aWR5aW5nIGRhdGEKbGlicmFyeShkcGx5cikgIyBVc2VkIGZvciBkYXRhIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KGtuaXRyKSAjIFVzZWQgZm9yIGNyZWF0aW5nIG5pY2UgdGFibGVzCmxpYnJhcnkoa2FibGVFeHRyYSkgIyBVc2VkIGZvciB0YWJsZSBmb3JtYXR0aW5nCmxpYnJhcnkobWFncml0dHIpICMgVXNlZCBmb3IgcGlwZSBvcGVyYXRpb24KbGlicmFyeShzdHJpbmdpKSAjIFVzZWQgZm9yIHN0cmluZyBtYW5pcHVsYXRpb24KbGlicmFyeShlZGl0cnVsZXMpICMgVXNlZCB0byBkZWFsIHdpdGggaW5jb25zaXN0ZW5jaWVzIGluIGRhdGEKYGBgCgojIyBFeGVjdXRpdmUgU3VtbWFyeSAKClRoZSBhaW0gb2YgdGhpcyByZXBvcnQgaXMgdG8gY2Fycnkgb3V0IGRhdGEgcHJlLXByb2Nlc3NpbmcuIFRoaXMgaW52b2x2ZXMgLSBpbXBvcnRpbmcsIHVuZGVyc3RhbmRpbmcsIHRpZHlpbmcsIG1hbmlwdWxhdGluZywgc2Nhbm5pbmcgYW5kIHRyYW5zZm9ybWluZyBkYXRhLjxicj4KCkZpcnN0LCB3ZSBpZGVudGlmeSB0d28gb3BlbiBkYXRhc2V0cyAoY291bnRyeSB3aXNlIG1hbG51dHJpdGlvbiBhbmQgbGlmZSBleHBlY3RhbmN5KSBoYXZpbmcgYXQgbGVhc3Qgb25lIGNvbW1vbiBhdHRyaWJ1dGUgKGNvdW50cnkpIGFuZCBpbXBvcnQgdGhlbSBpbnRvIHRoZSBSIHdvcmtzcGFjZS4gTmV4dCB3ZSBzdWJzZXQgdGhlIGRhdGEgYnkgZHJvcHBpbmcgdW53YW50ZWQgY29sdW1ucyBhbmQgcmVuYW1lIHRoZSBjb2x1bW5zIHN1aXRhYmx5LiBUaGUgdW50aWR5IGRhdGFzZXQgaXMgcmVzaGFwZWQgaW50byB0aWR5IGZvcm1hdCwgYWNjb3JkaW5nIHRvIHRoZSB0aWR5IGRhdGEgcHJpbmNpcGxlcy4gVG8gYXZvaWQgYW55IGluY29uc2lzdGVuY2llcyB3aGlsZSBtZXJnaW5nIHRoZSBkYXRhc2V0cywgd2UgcGVyZm9ybSB1cHBlciBjYXNlIGNvbnZlcnNpb24gb24gdGhlIGNvbW1vbiBjb2x1bW4g4oCYQ291bnRyeeKAmS4gV2UgdXNlIGxlZnQgam9pbiB0byBtZXJnZSB0aGUgZGF0YXNldHMuIEl0IGVuc3VyZXMgYW55IGRhdGEgbG9zcyBhcyBhIHJlc3VsdCBvZiBtZXJnaW5nIGRvZXMgbm90IGFmZmVjdCB0aGUgcmVzZWFyY2ggZ29hbC48YnI+CgpOZXh0LCB3ZSB1bmRlcnN0YW5kIHRoZSBkYXRhIGJ5IGNoZWNraW5nIGl0cyB2b2x1bWUsIGF0dHJpYnV0ZXMgYW5kIHN0cnVjdHVyZS4gV2UgdmFsaWRhdGUgaWYgYWxsIHRoZSBkaWZmZXJlbnQgZGF0YSB0eXBlcyBvZiB2YXJpYWJsZXMsIGFyZSBjbGFzc2lmaWVkIGNvcnJlY3RseSBhbmQgYXBwbHkgdHlwZSBjb252ZXJzaW9ucyBmb3IgaW5jb3JyZWN0IGNsYXNzaWZpY2F0aW9ucy48YnI+CgpXZSBkZW1vbnN0cmF0ZSBkYXRhIG1hbmlwdWxhdGlvbiB0aHJvdWdoIGNyZWF0aW9uIG9mIGEgbmV3IGNvbHVtbiBmcm9tIHRoZSBleGlzdGluZyBjb2x1bW5zLjxicj4KCk5leHQsIHdlIHNjYW4gZm9yIG1pc3NpbmcgdmFsdWVzIGFuZCBjaG9vc2UgdG8gb21pdCB0aGUgTkEgdmFsdWVzLiBSZXBsYWNpbmcgaXQgd2l0aCBhIGNvbnN0YW50L21lYW4vbWVkaWFuIHZhbHVlIHdvdWxkbuKAmXQgbWFrZSBzZW5zZSBpbiBvdXIgY2FzZSwgYXMgZWFjaCBvZiB0aGUgdmFsdWVzIGFyZSBjb3VudHJ5IHNwZWNpZmljIGFuZCBjYW5ub3QgYmUgYXBwcm94aW1hdGVkLgpEYXRhc2V0IGlzIHNjYW5uZWQgZm9yIHNwZWNpYWwgdmFsdWVzIGFuZCBvYnZpb3VzIGVycm9ycy4gSXQgaXMgdmFsaWRhdGVkIGFnYWluc3QgcHJlLWRlZmluZWQgcnVsZXMgYW5kIG5vIGlzc3VlcyBhcmUgb2JzZXJ2ZWQgaW4gdGhlIHN1bW1hcnkgb2YgZXJyb3JzLjxicj4KClR1a2V54oCZcyBtZXRob2Qgb2Ygb3V0bGllciBkZXRlY3Rpb24gaXMgdXNlZCB0byBwbG90IHRoZSBib3ggcGxvdCBmb3IgbnVtZXJpY2FsIHZhcmlhYmxlcy4gT2JzZXJ2ZWQgb3V0bGllcnMgZm9yIHBvcHVsYXRpb24gY29sdW1uIGFyZSB2YWxpZCBhbmQgbm90IGhhbmRsZWQuIEZvciBleGFtcGxlLCBjb3VudHJpZXMgbGlrZSBJbmRpYSwgQ2hpbmEgZXRjIGhhdmUgdmVyeSBsYXJnZSBwb3B1bGF0aW9uIGNvbXBhcmVkIHRvIG90aGVyIGNvdW50cmllcy4gT24gYSBzaW1pbGFyIHJlYXNvbmluZywgc2luY2UgdGhlIG91dGxpZXJzIG9ic2VydmVkIGluIGFsbCB0aGUgYm94IHBsb3RzIGFyZSBhY3R1YWwgdmFsaWQgdmFsdWVzLCB3ZSByZXRhaW4gdGhlIG91dGxpZXJzLgpUaGUgZGlzdHJpYnV0aW9uIG9mIGFsbCBudW1lcmljYWwgdmFyaWFibGVzIGFyZSBleHBsb3JlZCB0aHJvdWdoIGhpc3RvZ3JhbXMuIFRvIGRlY3JlYXNlIHRoZSByaWdodCBza2V3bmVzcyBvZiBvbmUgb2YgdGhlIHZhcmlhYmxlIGFuZCBjb252ZXJ0IGl0IHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgd2UgYXBwbHkgdGhlIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uLjxicj4KCkFsbCB0aGUgYWJvdmUgc3RlcHMgZW5zdXJlIHRoYXQgb3VyIGRhdGEgaXMgZXJyb3IgZnJlZSwgY29uc2lzdGVudCBhbmQgcmVhZHkgZm9yIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzaXMuPGJyPgoKCiMjIERhdGEgCgpEYXRhIGlzIHNvdXJjZWQgZnJvbSBLYWdnbGUuIDxicj4KClRoZSBmaXJzdCBvcGVuIGRhdGEgc2V0IHVzZWQgaW4gdGhpcyBhc3NpZ25tZW50LCBjb25zaXN0cyBvZiBjb3VudHJ5IHdpc2UgbGlmZSBleHBlY3RhbmN5IGRhdGEgc2VncmVnYXRlZCBieSBnZW5kZXIgKG1hbGUvZmVtYWxlKSBhbmQgYWxzbyB0aGUgb3ZlcmFsbCBsaWZlIGV4cGVjdGFuY3kuPGJyPgoqKkRhdGFzZXQgMSBVUkw6KiogPGEgaHJlZj1odHRwczovL3d3dy5rYWdnbGUuY29tL2FtYW5zYXhlbmEvbGlmZWV4cGVjdGFuY3k/c2VsZWN0PUxpZmVfZXhwZWN0YW5jeV9kYXRhc2V0LmNzdj5odHRwczovL3d3dy5rYWdnbGUuY29tL2FtYW5zYXhlbmEvbGlmZWV4cGVjdGFuY3k/c2VsZWN0PUxpZmVfZXhwZWN0YW5jeV9kYXRhc2V0LmNzdjxhLz4KClRoZSBzZWNvbmQgZGF0YSBzZXQgY29tcHJpc2VzIG9mIG1hbG51dHJpdGlvbiBkYXRhIG9mIGNvdW50cmllcyBhY3Jvc3MgdGhlIGdsb2JlIGZyb20gMTk4MyB0byAyMDE5Ljxicj4KKipEYXRhc2V0IDIgVVJMOioqIDxhIGhyZWY9aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9ydWNoaTc5OC9tYWxudXRyaXRpb24tYWNyb3NzLXRoZS1nbG9iZT9zZWxlY3Q9Y291bnRyeS13aXNlLWF2ZXJhZ2UuY3N2Pmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vcnVjaGk3OTgvbWFsbnV0cml0aW9uLWFjcm9zcy10aGUtZ2xvYmU/c2VsZWN0PWNvdW50cnktd2lzZS1hdmVyYWdlLmNzdjxhLz4gPGJyPgoKKipWYXJpYWJsZXMgZGVzY3JpcHRpb24qKjo8YnI+CioqRGF0YSBzZXQgMSAtIExpZmVfZXhwZWN0YW5jeV9kYXRhc2V0LmNzdioqIDxicj4KKipSYW5rKio6IE51bWVyaWNhbCB2YXJpYWJsZSwgbGlmZSBleHBlY3RhbmN5IHJhbmsgPGJyPgoqKkNvdW50cnkqKjogQ2hhcmFjdGVyIHZhcmlhYmxlLCBuYW1lIG9mIHRoZSBjb3VudHJ5PGJyPgoqKk92ZXJhbGwgTGlmZSoqOiBOdW1lcmljYWwgdmFyaWFibGUsIG92ZXJhbGwgbGlmZSBleHBlY3RhbmN5IG9mIG1hbGUgYW5kIGZlbWFsZSBwb3B1bGF0aW9uIDxicj4KKipNYWxlIExpZmUqKjogTnVtZXJpY2FsIHZhcmlhYmxlLCBsaWZlIGV4cGVjdGFuY3kgb2YgbWFsZXMgPGJyPgoqKkZlbWFsZSBMaWZlKio6IE51bWVyaWNhbCB2YXJpYWJsZSwgbGlmZSBleHBlY3RhbmN5IG9mIGZlbWFsZXMgPGJyPgoqKkNvbnRpbmVudCoqOiBDYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIDYgbGV2ZWxzLCBjb250aW5lbnQgdG8gd2hpY2ggYSBjb3VudHJ5IGJlbG9uZ3MgdG8gLSBBZnJpY2EsIEV1cm9wZSwgQXNpYSwgT2NlYW5pYSwgTm9ydGggQW1lcmljYSwgU291dGggQW1lcmljYSA8YnI+PGJyPgoKKipEYXRhIHNldCAyIC0gY291bnRyeS13aXNlLWF2ZXJhZ2UuY3N2KiogPGJyPgoqKkNvdW50cnkqKjogQ2hhcmFjdGVyIHZhcmlhYmxlLCBuYW1lIG9mIHRoZSBjb3VudHJ5IDxicj4KKipJbmNvbWUgQ2xhc3NpZmljYXRpb24qKjogQ2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCA0IGxldmVscywgTG93IEluY29tZTogMCwgTG93ZXIgTWlkZGxlIEluY29tZTogMSwgVXBwZXIgTWlkZGxlIEluY29tZTogMiwgSGlnaCBJbmNvbWU6IDM8YnI+CioqU2V2ZXJlIFdhc3RpbmcqKjogTnVtZXJpY2FsIHZhcmlhYmxlLCBhdmVyYWdlIHNldmVyZSB3YXN0aW5nIHBlcmNlbnRhZ2UuIEl0IGluZGljYXRlcyB0aGUgcGVyY2VudGFnZSBvZiBjaGlsZHJlbiBhZ2VkIHVuZGVyIDUgeWVhcnMgd2hvIGFyZSBiZWxvdyBtaW51cyB0aHJlZSBzdGFuZGFyZCBkZXZpYXRpb25zIGZyb20gbWVkaWFuIHdlaWdodC1mb3ItaGVpZ2h0IDxicj4KKipXYXN0aW5nKio6IE51bWVyaWNhbCB2YXJpYWJsZSwgYXZlcmFnZSB3YXN0aW5nIHBlcmNlbnRhZ2UuIEl0IGluZGljYXRlcyB0aGUgcGVyY2VudGFnZSBvZiBjaGlsZHJlbiBhZ2VkIHVuZGVyIDUgeWVhcnMgd2hvIGFyZSBiZWxvdyBtaW51cyB0d28gc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIG1lZGlhbiB3ZWlnaHQtZm9yLWhlaWdodCAgPGJyPgoqKk92ZXJ3ZWlnaHQqKjogTnVtZXJpY2FsIHZhcmlhYmxlLCBhdmVyYWdlIG92ZXJ3ZWlnaHQgcGVyY2VudGFnZS4gSXQgaW5kaWNhdGVzIHRoZSBwZXJjZW50YWdlIG9mIGNoaWxkcmVuIGFnZWQgdW5kZXIgNSB5ZWFycyB3aG8gYXJlIGFib3ZlIHR3byBzdGFuZGFyZCBkZXZpYXRpb25zIGZyb20gbWVkaWFuIHdlaWdodC1mb3ItaGVpZ2h0IDxicj4KKipTdHVudGluZyoqOiBOdW1lcmljYWwgdmFyaWFibGUsIGF2ZXJhZ2Ugc3R1bnRpbmcgcGVyY2VudGFnZS4gSXQgaW5kaWNhdGVzIHRoZSBwZXJjZW50YWdlIG9mIGNoaWxkcmVuIGFnZWQgdW5kZXIgNSB5ZWFycyB3aG8gYXJlIGJlbG93IG1pbnVzIHR3byBzdGFuZGFyZCBkZXZpYXRpb25zIGZyb20gbWVkaWFuIGhlaWdodC1mb3ItYWdlPGJyPgoqKlVuZGVyd2VpZ2h0Kio6IE51bWVyaWNhbCB2YXJpYWJsZSwgYXZlcmFnZSB1bmRlcndlaWdodCBwZXJjZW50YWdlLiBJdCBpbmRpY2F0ZXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY2hpbGRyZW4gYWdlZCB1bmRlciA1IHllYXJzIHdobyBhcmUgYmVsb3cgbWludXMgdHdvIHN0YW5kYXJkIGRldmlhdGlvbnMgZnJvbSBtZWRpYW4gd2VpZ2h0LWZvci1hZ2U8YnI+CioqVTUgUG9wdWxhdGlvbiAoYDAwMHMpKio6IE51bWVyaWNhbCB2YXJpYWJsZSwgcG9wdWxhdGlvbiBvZiBjaGlsZHJlbiBiZWxvdyB0aGUgYWdlIG9mIGZpdmUsIGV4cHJlc3NlZCBpbiB0aG91c2FuZHM8YnI+CgpSZWFkIGluIHRoZSBkYXRhIHNldHMgdXNpbmcgcmVhZF9jc3YoKSBmdW5jdGlvbi4gUmVuYW1lIHRoZSBjb2x1bW5zIGFwcHJvcHJpYXRlbHkgYW5kIGRyb3AgYW55IGNvbHVtbnMgdGhhdCBhcmUgbm90IHJlcXVpcmVkLgpgYGB7cn0KI1Jlc2VhcmNoIGdvYWwgLSBUbyBjb21wYXJlIGFuZCBjb3JyZWxhdGUgbWFsbnV0cml0aW9uIHRvIGF2ZXJhZ2UgbGlmZSBleHBlY3RhbmN5LgpkZjEgPC0gcmVhZF9jc3YoIkxpZmVfZXhwZWN0YW5jeV9kYXRhc2V0LmNzdiIpICMgUmVhZCBmaXJzdCBkYXRhIHNldAoKY29sbmFtZXMoZGYxKVsxXSA8LSAiR2xvYmFsIFJhbmsiICMgUmVuYW1lIGNvbHVtbiBuYW1lICJSYW5rIiB0byAiR2xvYmFsIFJhbmsiCmNvbG5hbWVzKGRmMSlbNF0gPC0gIk1hbGUiICMgUmVuYW1lIGNvbHVtbiBuYW1lICJNYWxlIExpZmUiIHRvICJNYWxlIgpjb2xuYW1lcyhkZjEpWzVdIDwtICJGZW1hbGUiICMgUmVuYW1lIGNvbHVtbiBuYW1lICJGZW1hbGUgTGlmZSIgdG8gIkZlbWFsZSIKZGYxIDwtIGRmMVsgLWMoMykgXSAjIERyb3AgdGhlIG92ZXJhbGwgIk92ZXJhbGwgTGlmZSIgY29sdW1uLCBhcyBpdCBpcyBub3QgcmVxdWlyZWQKCmRmMiA8LSByZWFkX2NzdigiY291bnRyeS13aXNlLWF2ZXJhZ2UuY3N2IikgIyBSZWFkIHNlY29uZCBkYXRhIHNldApgYGAKPGJyPgpMZXQgdXMgdGFrZSBhIGxvb2sgYXQgdGhlIHRvcCA2IHJvd3Mgb2Ygb3VyIGRhdGFzZXRzLiA8YnI+Cjxicj4KKipEYXRhIHNldCAxKioKYGBge3J9CmtuaXRyOjprYWJsZShoZWFkKGRmMSksIGZvcm1hdCA9ICJodG1sIiwgYWxpZ24gPSAnbCcpJT4lCmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIikpICMgSGVhZCBvZiBkYXRhIHNldCAxIC0gdW50aWR5IGZvcm1hdApgYGAKPGJyPgpPdXIgZmlyc3QgZGF0YSBzZXQgaXMgaW4gdW50aWR5IGZvcm1hdCBhcyBjb2x1bW4gbmFtZXMgKE1hbGUgYW5kIEZlbWFsZSkgYXJlIHZhbHVlcyAob2YgbGlmZSBleHBlY3RhbmN5KSBpbnN0ZWFkIG9mIHZhcmlhYmxlcyBhbmQgZG9lc24ndCBzYXRpc2Z5IHRoZSB0aWR5IGRhdGEgcHJpbmNpcGxlcy4gTGV0IHVzIGNvbnZlcnQgaXQgdG8gdGlkeSBmb3JtYXQgdXNpbmcgcGl2b3RfbG9uZ2VyKCkgZnVuY3Rpb24gZnJvbSB0aGUgdGlkeXIgcGFja2FnZSBhbmQgY2hlY2sgdGhlIHRvcCA2IHJvd3MuCmBgYHtyfQpkZjFfdGlkeSA8LSBwaXZvdF9sb25nZXIoZGYxLCBuYW1lc190byA9ICJHZW5kZXIiLCB2YWx1ZXNfdG8gPSAiTGlmZSBFeHBlY3RhbmN5IiwgY29scyA9IDM6NCkgIyBDb252ZXJ0IHRvIHRpZHkgZm9ybWF0Cgprbml0cjo6a2FibGUoaGVhZChkZjFfdGlkeSksIGZvcm1hdCA9ICJodG1sIiwgYWxpZ24gPSAnbCcpJT4lCmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIikpICMgRGlzcGxheSB0b3AgNiByb3dzIG9mIHRoZSB0aWR5IGRhdGFzZXQgMQpgYGAKPGJyPgpOb3csIGxldHMgdGFrZSBhIHBlZWsgaW50byB0aGUgKipEYXRhIHNldCAyKioKYGBge3J9CmtuaXRyOjprYWJsZShoZWFkKGRmMiksIGZvcm1hdCA9ICJodG1sIiwgYWxpZ24gPSAnbCcpJT4lCmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIikpICMgSGVhZCBvZiBkYXRhIHNldCAyIApgYGAKPGJyPgpUaGUgc2Vjb25kIGRhdGEgc2V0IGlzIGFscmVhZHkgaW4gdGlkeSBmb3JtYXQgYW5kIGRvZXMgbm90IHJlcXVpcmUgdG8gYmUgcmVzaGFwZWQuPGJyPjxicj4KCk5leHQgd2UgcHJvY2VlZCB0byBtZXJnZSBib3RoIHRoZSBkYXRhIHNldHMsIGZvciBmdXJ0aGVyIGFuYWx5c2lzLjxicj4KU2luY2Ugd2UgYXJlIHRyeWluZyB0byBjb21wYXJlIGFuZCBjb3JyZWxhdGUgbWFsbnV0cml0aW9uIHRvIGF2ZXJhZ2UgbGlmZSBleHBlY3RhbmN5LCB3ZSBtZXJnZSB0aGUgZGF0YXNldHMgdXNpbmcgYSBsZWZ0X2pvaW4oKSBhcyBwZXIgYmVsb3cuIFByaW9yIHRvIG1lcmdpbmcsIHdlIGNvbnZlcnQgdGhlIHZhbHVlcyBvZiB0aGUgQ291bnRyeSBjb2x1bW4gaW4gZGYxX3RpZHkgdG8gdXBwZXIgY2FzZSwgdG8gbWF0Y2ggdGhlIGNhc2luZyBvZiB0aGUgY29tbW9uIGNvbHVtbiBiZXR3ZWVuIHRoZSBkYXRhIHNldHMuIAoKYGBge3J9CmRmMV90aWR5JENvdW50cnkgPC0gIHN0cmluZ2k6OnN0cmlfdHJhbnNfdG91cHBlcihkZjFfdGlkeSRDb3VudHJ5KSAjQ29udmVydCB0byB1cHBlciBjYXNlCiNTb3VyY2UgLSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy85NjM3Mjc4L3ItdG0tcGFja2FnZS1pbnZhbGlkLWlucHV0LWluLXV0Zjh0b3djcwoKZGZfY29tYmluZWQgPC0gbGVmdF9qb2luKGRmMixkZjFfdGlkeSxieT0iQ291bnRyeSIpICMgV2UgZG8gYSBsZWZ0IGpvaW4gZm9yIHRoZSBkYXRhIHNldHMgYXMgd2UgaW50ZW5kIHRvIGZpbmQgY29ycmVsYXRpb24gYmV0d2VlbiBtYWxudXRyaXRpb24gYW5kIGxpZmUgZXhwZWN0YW5jeSBhY3Jvc3MgdGhlIGNvdW50cmllcy4KYGBgCjxicj4KTGV0cyBjaGVjayB0aGUgdG9wIDYgcm93cyBvZiBvdXIgY29tYmluZWQgZGF0YSBzZXQuCgpgYGB7cn0Ka25pdHI6OmthYmxlKGhlYWQoZGZfY29tYmluZWQpLCBmb3JtYXQgPSAiaHRtbCIsIGFsaWduID0gJ2wnKSU+JQprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIpKQpgYGAKPGJyPgpUaGlzIG1lZXRzIHRoZSBtaW5pbXVtIHJlcXVpcmVtZW50IDEgb2YgdGhlIHNwZWNpZmljYXRpb24uCjxicj4KCiMjIFVuZGVyc3RhbmQgCgpJbiB0aGlzIHNlY3Rpb24sIGxldCB1cyBpbnNwZWN0IGFuZCB1bmRlcnN0YW5kIHRoZSBkYXRhIGJ5IGNoZWNraW5nIGl0cyB2b2x1bWUsIGF0dHJpYnV0ZXMgYW5kIHN0cnVjdHVyZS48YnI+Cjxicj4KVGhlIGRpbWVuc2lvbnMgb2YgdGhlIGRhdGEgZnJhbWUgY2FuIGJlIGNoZWNrZWQgYXMgYmVsb3cuIEl0IGNvbnNpc3RzIG9mIDI3MyByb3dzIGFuZCAxMiBjb2x1bW5zLgpgYGB7cn0KZGltKGRmX2NvbWJpbmVkKQpgYGAKPGJyPgoqKnN0cigpKiogZnVuY3Rpb24gZ2l2ZXMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBmcmFtZS4KYGBge3J9CnN0cihkZl9jb21iaW5lZCkKYGBgCjxicj4KV2UgY2FuIHNlZSB0aGF0IGJ5IGRlZmF1bHQsIHRoZSByZWFkX2NzdigpIGZ1bmN0aW9uIGhhcyBhbGxvY2F0ZWQgdGhlIGZvbGxvd2luZyBkYXRhIHR5cGVzIGZvciBlYWNoIG9mIHRoZSBjb2x1bW5zLjxicj4gPGJyPgoqKkNvdW50cnkqKiAtICpDaGFyYWN0ZXIgdmFyaWFibGUqIDxicj4KKipJbmNvbWUgQ2xhc3NpZmljYXRpb24qKiAtICpEb3VibGUgbnVtZXJpYyB2YXJpYWJsZSogPGJyPgoqKlNldmVyZSBXYXN0aW5nKiogLSAqRG91YmxlIG51bWVyaWMgdmFyaWFibGUqIDxicj4KKipXYXN0aW5nKiogLSAqRG91YmxlIG51bWVyaWMgdmFyaWFibGUqIDxicj4KKipPdmVyd2VpZ2h0KiogLSAqRG91YmxlIG51bWVyaWMgdmFyaWFibGUqIDxicj4KKipTdHVudGluZyoqIC0gKkRvdWJsZSBudW1lcmljIHZhcmlhYmxlKiA8YnI+CioqVW5kZXJ3ZWlnaHQqKiAtICpEb3VibGUgbnVtZXJpYyB2YXJpYWJsZSogPGJyPgoqKlU1IFBvcHVsYXRpb24gKCcwMDBzKSoqIC0gKkRvdWJsZSBudW1lcmljIHZhcmlhYmxlKiA8YnI+CioqR2xvYmFsIFJhbmsqKiAtICpEb3VibGUgbnVtZXJpYyB2YXJpYWJsZSogPGJyPgoqKkNvbnRpbmVudCoqIC0gKkNoYXJhY3RlciB2YXJpYWJsZSogPGJyPgoqKkdlbmRlcioqIC0gKkNoYXJhY3RlciB2YXJpYWJsZSogPGJyPgoqKkxpZmUgRXhwZWN0YW5jeSoqIC0gKkRvdWJsZSBudW1lcmljIHZhcmlhYmxlKiA8YnI+CgpIb3dldmVyLCBjb2x1bW5zIC0gKipJbmNvbWUgQ2xhc3NpZmljYXRpb24qKiwgKipDb250aW5lbnQqKiBhbmQgKipHZW5kZXIqKiBhcmUgaW5jb3JyZWN0bHkgY2xhc3NpZmllZC4KV2UgY2FuIGdvIGFoZWFkIGFuZCBwZXJmb3JtIHR5cGUgY29udmVyc2lvbnMgb24gdGhlIGNvbHVtbiBuYW1lcyBhcyBiZWxvdyAtCmBgYHtyfQpkZl9jb21iaW5lZCRgSW5jb21lIENsYXNzaWZpY2F0aW9uYCA8LSBmYWN0b3IoZGZfY29tYmluZWQkYEluY29tZSBDbGFzc2lmaWNhdGlvbmAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygwLDEsMiwzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ0xvdyBJbmNvbWUnLCAnTG93ZXIgTWlkZGxlIEluY29tZScsICdVcHBlciBNaWRkbGUgSW5jb21lJywgJ0hpZ2ggSW5jb21lJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRSkgI0NvbnZlcnQgZnJvbSBudW1lcmljIHRvIGFuIG9yZGVyZWQgZmFjdG9yCgpkZl9jb21iaW5lZCRDb250aW5lbnQgPC0gZmFjdG9yKGRmX2NvbWJpbmVkJENvbnRpbmVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdBZnJpY2EnLCAnRXVyb3BlJywgJ0FzaWEnLCAnT2NlYW5pYScsICdOb3J0aCBBbWVyaWNhJywgJ1NvdXRoIEFtZXJpY2EnKSkgIyBDb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIGZhY3RvcgpkZl9jb21iaW5lZCRHZW5kZXIgPC0gZmFjdG9yKGRmX2NvbWJpbmVkJEdlbmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdNYWxlJywgJ0ZlbWFsZScpKSNDb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIGZhY3RvcgpgYGAKPGJyPgpMZXRzIGNoZWNrIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFmcmFtZSBhZ2FpbgpgYGB7cn0Kc3RyKGRmX2NvbWJpbmVkKQoKYGBgCgpXZSBjYW4gc2VlIHRoYXQgb3VyIGRhdGEgc2V0IGluY2x1ZGVzIG11bHRpcGxlIGRhdGEgdHlwZXMgLSBjaGFyYWN0ZXIsIG51bWVyaWMgYW5kIGZhY3RvcnMuIEFsc28gd2UndmUgYXBwbGllZCBkYXRhIHR5cGUgY29udmVyc2lvbnMgZnJvbSBudW1lcmljIHRvIGZhY3RvciwgYW5kIGNoYXJhY3RlciB0byBmYWN0b3IuIFdlIGhhdmUgYXQgbGVhc3Qgb25lIGZhY3RvciB2YXJpYWJsZSB3aGljaCBpcyBvcmRlcmVkIGFuZCBsYWJlbGVkLiBUaHVzIGl0IG1lZXRzIHRoZSBtaW5pbXVtIHJlcXVpcmVtZW50cyAyLCAzIGFuZCA0LiA8YnI+CgpUaGUgZGV0YWlscyBvZiBjb2x1bW4gbmFtZXMsIHJvdyBuYW1lcywgY29sdW1uIHNwZWNpZmljYXRpb25zIGFuZCBjbGFzcyBvZiB0aGUgdmFyaWFibGUgY2FuIGJlIG9idGFpbmVkIHVzaW5nIHRoZSBhdHRyaWJ1dGVzKCkgZnVuY3Rpb24uCmBgYHtyfQphdHRyaWJ1dGVzKGRmX2NvbWJpbmVkKSAjRGVzY3JpYmUgdGhlIGF0dHJpYnV0ZXMgb2YgYSB2YXJpYWJsZQpgYGAKPGJyPgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoKQWNjb3JkaW5nIHRvIHRoZSBUaWR5IGRhdGEgcHJpbmNpcGxlcywgaWYgb25lIG9mIHRoZSBiZWxvdyBjb25kaXRpb25zIGlzIHZpb2xhdGVkLCB0aGVuIGl0cyBpcyBjYXRlZ29yaXplZCBhcyB1bnRpZHkgZGF0YTo8YnI+CjEpIEVhY2ggdmFyaWFibGUgbXVzdCBoYXZlIGl0cyBvd24gY29sdW1uLjxicj4KMikgRWFjaCBvYnNlcnZhdGlvbiBtdXN0IGhhdmUgaXRzIG93biByb3cuPGJyPgozKSBFYWNoIHZhbHVlIG11c3QgaGF2ZSBpdHMgb3duIGNlbGwuPGJyPgoKSW4gb3VyIGNhc2UsIHRoZSBmaXJzdCBkYXRhIHNldCAqKiJkZjEiKiogaXMgdW50aWR5LiA8YnI+ClRoaXMgaXMgYmVjYXVzZSB0aGUgY29sdW1uIG5hbWVzICoqIk1hbGUgTGlmZSIqKiBhbmQgKioiRmVtYWxlIExpZmUiKiosIHJlcHJlc2VudCB0aGUgKip2YWx1ZXMqKiAobGlmZSBleHBlY3RhbmN5KSBmb3IgdGhlIGdlbmRlciB2YXJpYWJsZSBhbmQgZWFjaCByb3cgcmVwcmVzZW50cyAyIG9ic2VydmF0aW9ucyBhbmQgbm90IDEuIFNpbmNlIHRoZSBjb2x1bW4gaGVhZGVycyBhcmUgdmFsdWVzIGluc3RlYWQgb2YgdmFyaWFibGVzLCBpdCB2aW9sYXRlcyB0aGUgZmlyc3QgY29uZGl0aW9uIG1lbnRpb25lZCBhYm92ZS48YnI+PGJyPgoKV2UgdXNlIHBpdm90X2xvbmdlcigpIGZ1bmN0aW9uIGZyb20gdGlkeXIgcGFja2FnZSB0byByZXNoYXBlIHRoZSBkYXRhIGludG8gdGlkeSBmb3JtYXQuCkl0IGNvbnZlcnRzIGRhdGEgZnJvbSB3aWRlIGZvcm1hdCB0byBsb25nIGZvcm1hdC48YnI+ClJlcXVpcmVkIHN0ZXBzIHRvIHRpZHkgdGhlIGRhdGEgaGF2ZSBhbHJlYWR5IGJlZW4gYXBwbGllZCBwcmlvciB0byBtZXJnaW5nIGl0IHdpdGggZGF0YSBzZXQgMi4gS2luZGx5IHJlZmVyIHRoZSBzZWN0aW9uICJEYXRhIi48YnI+PGJyPgoKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCgpMZXQgdXMgY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGJ5IHVzaW5nIHRoZSBtdXRhdGUoKSBmdW5jdGlvbiBmcm9tIHRoZSBkcGx5ciBwYWNrYWdlLjxicj4KCldlIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSAnVTUgb3Zlci91bmRlci13ZWlnaHQgcG9wdWxhdGlvbignMDAwcyknICB0byBjb21wdXRlIHRoZSBwb3B1bGF0aW9uIChpbiB0aG91c2FuZHMpIG9mIGNoaWxkcmVuIGFnZWQgYmVsb3cgNSB5ZWFycywgd2hvIGFyZSBlaXRoZXIgdW5kZXJ3ZWlnaHQgb3Igb3ZlcndlaWdodCwgdXNpbmcgdGhlIGV4aXN0aW5nIGNvbHVtbnMuCgpgYGB7cn0KIyBUaGlzIGlzIHRoZSBSIGNodW5rIGZvciB0aGUgVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJSSAKZGZfY29tYmluZWRfbXV0YXRlZCA8LSBtdXRhdGUoZGZfY29tYmluZWQsICdVNSBvdmVyL3VuZGVyLXdlaWdodCBwb3B1bGF0aW9uKFwnMDAwcyknPShkZl9jb21iaW5lZCRPdmVyd2VpZ2h0K2RmX2NvbWJpbmVkJFVuZGVyd2VpZ2h0KSpkZl9jb21iaW5lZCRgVTUgUG9wdWxhdGlvbiAoJzAwMHMpYC8xMDApICNVc2Ugb2YgbXV0YXRlIGZ1bmN0aW9uCmthYmxlKGhlYWQoZGZfY29tYmluZWRfbXV0YXRlZFtjKDEsMiw1LDcsOCwxMToxMyldKSwgZm9ybWF0ID0gImh0bWwiLCBhbGlnbiA9ICdsJyklPiUKa2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiKSkgIyBEaXNwbGF5IHRoZSB0b3AgNiByb3dzIG9mIHRoZSBtdXRhdGVkIGFuZCBzdWItc2V0dGVkIGRhdGEgZnJhbWUuCmBgYAo8YnI+VGhpcyBtZWV0cyB0aGUgbWluaW11bSByZXF1aXJlbWVudCA2Ljxicj48YnI+CgoKIyMJU2NhbiBJIAoKTGV0IHVzIGNoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyBpbiBvdXIgZGF0YSBzZXQgdXNpbmcgKippcy5uYSgpKiogZnVuY3Rpb24uIEZ1cnRoZXIgdXNpbmcgdGhlICoqc3VtKCkqKiBmdW5jdGlvbiB3ZSBjYW4gb2J0YWluIHRoZSB0b3RhbCBjb3VudC48YnI+CldlIHVzZSBzYXBwbHkoKSBmdW5jdGlvbiB0byBhcHBseSBpdCB0byB0aGUgZW50aXJlIGRhdGEgZnJhbWUuCgpgYGB7cn0KIyBUaGlzIGlzIHRoZSBSIGNodW5rIGZvciB0aGUgU2NhbiBJCnNhcHBseShkZl9jb21iaW5lZCwgZnVuY3Rpb24oeCkgc3VtKCBpcy5uYSh4KSApKSAjQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzCmBgYAo8YnI+CkFzIHdlIGNhbiBzZWUsIHdlIGhhdmUgbWFueSBOQSB2YWx1ZXMgaW4gb3VyIGRhdGEgc2V0LiBXZSBjaG9vc2UgdG8gb21pdCB0aGUgTkEgdmFsdWVzLlJlcGxhY2luZyBpdCB3aXRoIGEgY29uc3RhbnQvbWVhbi9tZWRpYW4vbW9kZSB2YWx1ZSBkb2Vzbid0IG1ha2UgbXVjaCBzZW5zZSBpbiBvdXIgY2FzZSBhcyBlYWNoIG9mIHRoZSB2YWx1ZXMgZGlmZmVyIGZyb20gY291bnRyeSB0byBjb3VudHJ5IGFuZCBjYW5ub3QgYmUgYXBwcm94aW1hdGVkLjxicj4KClRoZSBtaXNzaW5nIHZhbHVlcyBhcmUgcmVtb3ZlZCB1c2luZyBjb21wbGV0ZS5jYXNlcygpIGZ1bmN0aW9uLgpgYGB7cn0KZGZfY29tYmluZWQgPC0gZGZfY29tYmluZWRbY29tcGxldGUuY2FzZXMoZGZfY29tYmluZWQpLCBdCnNhcHBseShkZl9jb21iaW5lZCwgZnVuY3Rpb24oeCkgc3VtKCBpcy5uYSh4KSApKSAjQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzIGFnYWluCmBgYAo8YnI+IEFsbCBtaXNzaW5nIHZhbHVlcyBhcmUgbm93IG9taXR0ZWQuCgo8YnI+Ck5leHQsIGxldCB1cyBzY2FuIHRoZSBkYXRhIHNldCBmb3IgU3BlY2lhbCB2YWx1ZXMuPGJyPgpXZSBjcmVhdGUgYSBmdW5jdGlvbiBjYWxsZWQgaXMuc3BlY2lhbCgpIHRoYXQgcmV0dXJucyBUUlVFIGlmIHRoZSBudW1lcmljYWwgdmFyaWFibGUgaGFzIEluZiwgLUluZiBvciBOYU4gdmFsdWVzLjxicj4KVGhpcyBpcyBhcHBsaWVkIHRvIHRoZSBkYXRhIHNldCB1c2luZyBzYXBwbHkoKSBmdW5jdGlvbgpgYGB7cn0KaXMuc3BlY2lhbCA8LSBmdW5jdGlvbih4KXsKaWYgKGlzLm51bWVyaWMoeCkpIChpcy5pbmZpbml0ZSh4KSB8IGlzLm5hbih4KSkKfSAjRnVuY3Rpb24gdG8gc2NhbiBmb3IgSW5mLCAtSW5mIGFuZCBOYU4gdmFsdWVzLgoKc2FwcGx5KGRmX2NvbWJpbmVkLCBmdW5jdGlvbih4KSBzdW0oIGlzLnNwZWNpYWwoeCkgKSkKYGBgCjxicj4KVGhlIG91dHB1dCBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBhcmUgbm8gc3BlY2lhbCB2YWx1ZXMuPGJyPgoKRnVydGhlciwgbGV0IHVzIHNjYW4gdGhlIGRhdGFzZXQgZm9yIG9idmlvdXMgZXJyb3JzLjxicj4KV2UgZGVmaW5lIGN1c3RvbSBydWxlcyBhbmQgcGxhY2UgaXQgaW4gYSBmaWxlIGNhbGxlZCAqKmVkaXRydWxlcy50eHQqKjxicj4KCiAgMSkgIFRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKGZhY3RvcnMtIEluY29tZSBDbGFzc2lmaWNhdGlvbiwgQ29udGluZW50LCBHZW5kZXIpIGNhbiBob2xkIG9ubHkgcHJlZGVmaW5lZCB2YWx1ZXMgYXMgbWVudGlvbmVkIGJlbG93Ljxicj4KICAyKSAgTGlmZSBFeHBlY3RhbmN5IG1hbmRhdG9yaWx5IG5lZWRzIHRvIGJlIGEgdmFsdWUgYmV0d2VlbiAwIHRvIDEyMC48YnI+CgpgYGB7cn0KUnVsZXMgPC0gZWRpdGZpbGUoImVkaXRydWxlcy50eHQiLCB0eXBlID0gImFsbCIpI1BvcHVsYXRlIGNvbnN0cmFpbnRzIGRlZmluZWQgdW5kZXIgZWRpdHJ1bGVzLnR4dCBmaWxlIHRvIFJ1bGVzIHZhcmlhYmxlClJ1bGVzICNEaXNwbGF5IHRoZSBydWxlcwpgYGAKPGJyPgpPdXIgZGF0YWZyYW1lIGlzIHZhbGlkYXRlZCBhZ2FpbnN0IHRoZSBwcmUtZGVmaW5lZCBydWxlcyB1c2luZyB0aGUgdmlvbGF0ZWRFZGl0cygpIGZ1bmN0aW9uIGFuZCB3ZSBvYnRhaW4gdGhlIHN1bW1hcnkgb2YgZXJyb3JzLjxicj4KYGBge3J9CnZpb2xhdGVkIDwtIHZpb2xhdGVkRWRpdHMoUnVsZXMsZGZfY29tYmluZWQpCnN1bW1hcnkodmlvbGF0ZWQpCgpgYGAKV2UgZG8gbm90IHNlZSBhbnkgZXJyb3JzLCBtZWFuaW5nIGFsbCB0aGUgdmFsdWVzIGluIG91ciBkYXRhc2V0IHNhdGlzZnkgdGhlIGNvbmRpdGlvbnMgZGVmaW5lZCB1bmRlciBlZGl0ZmlsZXMudHh0IDxicj4gCklmIHJlcXVpcmVkLCB3ZSBjb3VsZCB1c2UgZGVkdWNvcnJlY3QgcGFja2FnZSBhbmQgY29ycmVjdGlvblJ1bGVzKCkgZnVuY3Rpb24gdG8gY29ycmVjdCB0aGUgb2J2aW91cyBpbmNvbnNpc3RlbmNpZXMgYW5kIGVycm9ycy4gPGJyPjxicj4KCiMjCVNjYW4gSUkKClVzaW5nIFR1a2V54oCZcyBtZXRob2Qgb2Ygb3V0bGllciBkZXRlY3Rpb24sIHdlIHBsb3QgdGhlIGJveCBwbG90IGZvciBlYWNoIG9mIHRoZSBudW1lcmljYWwgdmFyaWFibGVzLgpgYGB7cn0KZGZfY29tYmluZWQkYFU1IFBvcHVsYXRpb24gKCcwMDBzKWAgJT4lIGJveHBsb3QobWFpbj0iQm94cGxvdCBvZiB1bmRlciA1IHllYXJzIFBvcHVsYXRpb24gaW4gdGhvdXNhbmRzIiwgeWxhYj0iUG9wdWxhdGlvbiIsIGNvbCA9ICJncmV5IikgI0JveHBsb3Qgb2YgVTUgUG9wdWxhdGlvbiAoJzAwMHMpCmBgYAo8YnI+Ck9ic2VydmF0aW9uOiBFdmVudGhvdWdoIHdlIHNlZSBvdXRsaWVycywgd2UgY2hvb3NlIHRvIHJldGFpbSB0aG9zZSB2YWx1ZXMuIEZvciBleGFtcGxlIGNvdW50cmllcyBsaWtlIEluZGlhLCBDaGluYSBldGMgaGF2ZSBhIHBvcHVsYXRpb24gb3ZlciBhIGJpbGxpb24gYW5kIGFyZSBpZGVudGlmaWVkIGFzIHZhbGlkIG91dGxpZXJzLgoKYGBge3J9CiNCb3ggcGxvdCBvZiBudW1lcmljYWwgdmFyaWFibGVzCnBhcihtZnJvdz1jKDIsMikpCmRmX2NvbWJpbmVkJGBTZXZlcmUgV2FzdGluZ2AgJT4lIGJveHBsb3QobWFpbj0iQm94cGxvdCBvZiBzZXZlcmUgd2FzdGluZyBwZXJjZW50YWdlIiwgeWxhYj0iUGVyY2VudGFnZSIsIGNvbCA9ICJncmV5IikKZGZfY29tYmluZWQkYFdhc3RpbmdgICU+JSBib3hwbG90KG1haW49IkJveHBsb3Qgb2Ygd2FzdGluZyBwZXJjZW50YWdlIiwgeWxhYj0iUGVyY2VudGFnZSIsIGNvbCA9ICJncmV5IikKZGZfY29tYmluZWQkYE92ZXJ3ZWlnaHRgICU+JSBib3hwbG90KG1haW49IkJveHBsb3Qgb2Ygb3Zlci13ZWlnaHQgcGVyY2VudGFnZSIsIHlsYWI9IlBlcmNlbnRhZ2UiLCBjb2wgPSAiZ3JleSIpCmRmX2NvbWJpbmVkJGBVbmRlcndlaWdodGAgJT4lIGJveHBsb3QobWFpbj0iQm94cGxvdCBvZiB1bmRlci13ZWlnaHQgcGVyY2VudGFnZSIsIHlsYWI9IlBlcmNlbnRhZ2UiLCBjb2wgPSAiZ3JleSIpCnBhcihtZnJvdz1jKDEsMSkpCmBgYAo8YnI+Ck9uIGEgc2ltaWxhciByZWFzb25pbmcsIHNpbmNlIHRoZSBvdXRsaWVycyBvYnNlcnZlZCBpbiB0aGUgYWJvdmUgYm94IHBsb3RzICg+IDEuNSAqIElRUikgYXJlIG5vdCBlcnJvcnMsIGJ1dCBhY3R1YWwgdmFsdWVzIHdoaWNoIGFyZSB2YWxpZCwgd2UgZG8gbm90IHJlbW92ZSB0aGUgb3V0bGllcnMuPGJyPgoKTm8gb3V0bGllcnMgYXJlIGRldGVjdGVkIGluIHRoZSBzdHVudGluZyBhbmQgTGlmZSBFeHBlY3RhbmN5IGNvbHVtbnMuCmBgYHtyfQojQm94cGxvdCBvZiBzdHVudGluZyBwZXJjZW50YWdlIGFuZCBsaWZlIGV4cGVjdGFuY3kKcGFyKG1mcm93PWMoMSwyKSkKZGZfY29tYmluZWQkU3R1bnRpbmcgJT4lIGJveHBsb3QobWFpbj0iQm94cGxvdCBvZiBzdHVudGluZyBwZXJjZW50YWdlIiwgeWxhYj0iUGVyY2VudGFnZSIsIGNvbCA9ICJncmV5IikKZGZfY29tYmluZWQkYExpZmUgRXhwZWN0YW5jeWAgJT4lIGJveHBsb3QobWFpbj0iQm94cGxvdCBvZiBsaWZlIGV4cGVjdGFuY3kiLCB5bGFiPSJhZ2UgaW4geWVhcnMiLCBjb2wgPSAiZ3JleSIpCnBhcihtZnJvdz1jKDEsMSkpCmBgYAojIwlUcmFuc2Zvcm0gCgpMZXQgdXMgZXhwbG9yZSB0aGUgaGlzdG9ncmFtcyBvZiBhbGwgdGhlIG51bWVyaWNhbCB2YXJpYWJsZXMuCgpgYGB7cn0KIyBIaXN0b2dyYW0gcGxvdHMgb2YgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMKcGFyKG1mcm93ID1jKDMsMykpCgpVNV9wb3B1bGF0aW9uX2luX3Rob3VzYW5kcyA8LSAgZGZfY29tYmluZWQkYFU1IFBvcHVsYXRpb24gKCcwMDBzKWAKaGlzdChVNV9wb3B1bGF0aW9uX2luX3Rob3VzYW5kcykKc2V2ZXJlX3dhc3RpbmcgPC0gZGZfY29tYmluZWQkYFNldmVyZSBXYXN0aW5nYApoaXN0KHNldmVyZV93YXN0aW5nKQp3YXN0aW5nIDwtIGRmX2NvbWJpbmVkJGBXYXN0aW5nYApoaXN0KHdhc3RpbmcpCm92ZXJ3ZWlnaHQ8LSBkZl9jb21iaW5lZCRgT3ZlcndlaWdodGAKaGlzdChvdmVyd2VpZ2h0KQpzdHVudGluZyA8LSBkZl9jb21iaW5lZCRTdHVudGluZwpoaXN0KHN0dW50aW5nKQp1bmRlcndlaWdodCA8LSBkZl9jb21iaW5lZCRgVW5kZXJ3ZWlnaHRgCmhpc3QodW5kZXJ3ZWlnaHQpCmxpZmVfZXhwZWN0YW5jeSA8LSBkZl9jb21iaW5lZCRgTGlmZSBFeHBlY3RhbmN5YApoaXN0KGxpZmVfZXhwZWN0YW5jeSkKCnBhcihtZnJvdz1jKDEsMSkpCgpgYGAKVG8gYXBwbHkgYSBzdWl0YWJsZSB0cmFuc2Zvcm1hdGlvbiwgbGV0IHVzIGNob29zZSB0aGUgaGlzdG9ncmFtIHBsb3Qgb2YgdGhlIHZhcmlhYmxlIHdhc3RpbmcuPGJyPgoKYGBge3J9Cmhpc3Qod2FzdGluZykKYGBgCgpXZSBvYnNlcnZlIHRoYXQgdGhpcyBkaXN0cmlidXRpb24gaXMgcmlnaHQgc2tld2VkLiBUbyBkZWNyZWFzZSB0aGUgcmlnaHQgc2tld25lc3MgYW5kIGNvbnZlcnQgdGhlIGRpc3RyaWJ1dGlvbiBpbnRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3ZSBhcHBseSB0aGUgKipzcXVhcmUgcm9vdCB0cmFuc2Zvcm1hdGlvbioqCgpgYGB7cn0Kc3FydF93YXN0aW5nIDwtIHNxcnQod2FzdGluZykKaGlzdChzcXJ0X3dhc3RpbmcpCgpgYGAKT2JzZXJ2YXRpb24gLSBUaGUgc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24gaGFzIHJlZHVjZWQgdGhlIHJpZ2h0IHNrZXduZXNzIGFuZCBoYXMgaW1wcm92ZWQgdGhlIHN5bW1ldHJ5IG9mIHRoZSBkaXN0cmlidXRpb248YnI+CgpXZSBjYW4gYWxzbyBhcHBseSBsb2cgdHJhbnNmb3JtYXRpb25zIHRvIHJlZHVjZSB0aGUgcmlnaHQgc2tld25lc3MuCmBgYHtyfQpsb2dfd2FzdGluZyA8LSBsb2cxMCh3YXN0aW5nKQpoaXN0KGxvZ193YXN0aW5nKQpgYGAKCmBgYHtyfQpsbl93YXN0aW5nIDwtIGxvZyh3YXN0aW5nKQpoaXN0KGxuX3dhc3RpbmcpCmBgYApDb21wYXJlZCB0byB0aGUgbG9nIHRyYW5zZm9ybWF0aW9ucywgd2UgY2FuIHNlZSB0aGF0IHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uIGdpdmVzIGEgYmV0dGVyIHJlc3VsdC48YnI+CgojIyBSZWZlcmVuY2VzOiAKIDxicj4gCjEpIENyZWF0ZSBBd2Vzb21lIEhUTUwgVGFibGUgd2l0aCBrbml0cjo6a2FibGUgYW5kIGthYmxlRXh0cmEsIEhhbyBaaHUsIHZpZXdlZCAxMyBNYXkgMjAyMTogPGEgaHJlZj1odHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMva2FibGVFeHRyYS92aWduZXR0ZXMvYXdlc29tZV90YWJsZV9pbl9odG1sLmh0bWw+IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMva2FibGVFeHRyYS92aWduZXR0ZXMvYXdlc29tZV90YWJsZV9pbl9odG1sLmh0bWw+PGEvPjxicj4KMikgRGF0YSBzZXQgMTogPGEgaHJlZj1odHRwczovL3d3dy5rYWdnbGUuY29tL2FtYW5zYXhlbmEvbGlmZWV4cGVjdGFuY3k/c2VsZWN0PUxpZmVfZXhwZWN0YW5jeV9kYXRhc2V0LmNzdj4gPGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYW1hbnNheGVuYS9saWZlZXhwZWN0YW5jeT9zZWxlY3Q9TGlmZV9leHBlY3RhbmN5X2RhdGFzZXQuY3N2PjxhLz48YnI+CjMpIERhdGEgc2V0IDI6IDxhIGhyZWY9aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9ydWNoaTc5OC9tYWxudXRyaXRpb24tYWNyb3NzLXRoZS1nbG9iZT9zZWxlY3Q9Y291bnRyeS13aXNlLWF2ZXJhZ2UuY3N2PiA8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9ydWNoaTc5OC9tYWxudXRyaXRpb24tYWNyb3NzLXRoZS1nbG9iZT9zZWxlY3Q9Y291bnRyeS13aXNlLWF2ZXJhZ2UuY3N2PjxhLz48YnI+CjQpIENvbnZlcnNpb24gdG8gdXBwZXIgY2FzZSwgdmlld2VkIDExIE1heSAyMDIxOiA8YSBocmVmPWh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzk2MzcyNzgvci10bS1wYWNrYWdlLWludmFsaWQtaW5wdXQtaW4tdXRmOHRvd2NzPiA8aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvOTYzNzI3OC9yLXRtLXBhY2thZ2UtaW52YWxpZC1pbnB1dC1pbi11dGY4dG93Y3M+PGEvPjxicj4KNSkgQ29uY2VwdHMgZGlzY3Vzc2VkIHVuZGVyIG1vZHVsZSBub3RlcyAxIHRvIDc6IDxicj4gPGEgaHJlZj1odHRwczovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9pbmRleC5odG1sPiA8aHR0cHM6Ly9yYXJlLXBob2VuaXgtMTYxNjEwLmFwcHNwb3QuY29tL3NlY3VyZWQvaW5kZXguaHRtbD48YS8+Cjxicj4KPGJyPgo=