Required packages

The following packages were called:

require(rmarkdown)
require(readr)
require(readxl)
require(tidyr)
require(dplyr)
require(knitr)
require(stringr)
require(foreign)
require(ggplot2)
require(lubridate)
require(outliers)
require(forecast)

Executive Summary

Three data sets have been selected for this assignment:

Preprocessing steps include:

Data sets were then scanned for missing values, special characters and other obvious errors, and data visualisation and z-score tests were used to identify, evaluate and manage outliers.

Within the merged data set, two variables were transformed to:

Data Description

Data set 1:

Sourced from Heart Disease UCI on Kaggle, the data contains 303 observations of 14 variables. “heart.csv” was downloaded as a ZIP file, uncompressed, and the CSV file saved locally, prior to importing as heart1 using read_csv().

Variables in the data set:

  • age: Age (in years)
  • sex: Sex (1=Male, 0=Female)
  • cp: Classification of chest pain symptoms (4 categorical levels)
  • trestbps: Resting blood pressure (mm Hg)
  • chol: Total cholesterol level (mg/dL)
  • fbs: Fasting blood sugar (if >120 mg/dL, 1=true, 0=false)
  • restecg: Resting electrocardiographic results (3 categorical levels)
  • thalach: Maximum heart rate achieved (bpm)
  • exang: Presence of exercise induced angina in the person (1=yes, 0=no)
  • oldpeak: Indicates whether ST depression was induced by exercise, relative to rest
  • slope: Slope of the peak exercise ST segment (1=sloping up, 2=flat, 3=sloping down)
  • ca: Number of major vessels (from 0 to 3) coloured by fluorosopy
  • thal: Presence of thalassemia, a blood disorder (1=normal, 2=fixed defect, 3=reversable defect)
  • target: Indicates whether the person has been diagnosed with heart disease (0=no, 1=yes)
heart1 <- read_csv("~/RStudio Projects/RStudio Data/heart.csv")
Parsed with column specification:
cols(
  age = col_double(),
  sex = col_double(),
  cp = col_double(),
  trestbps = col_double(),
  chol = col_double(),
  fbs = col_double(),
  restecg = col_double(),
  thalach = col_double(),
  exang = col_double(),
  oldpeak = col_double(),
  slope = col_double(),
  ca = col_double(),
  thal = col_double(),
  target = col_double()
)

Data Description cont.

Data set 2:

Sourced from Cardiovascular Disease on Kaggle. The data contains 70,000 observations of 13 variables. “cardio_train.csv” was downloaded and saved locally, prior to importing as heart2 using read_delim(). Variable “id” is not needed and was removed using a subset command.

Variables in the data set:

  • id: Identification number of observation
  • age: Age (in days)
  • gender: Sex (1=women, 2=men)
  • height: Height (cm)
  • Weight: Weight (kg)
  • ap_hi: Systolic blood pressure
  • ap_lo: Diastolic blood pressure
  • cholesterol: Cholesterol group classification (1=normal, 2=above normal, 3=well above normal)
  • gluc: Glucose level (1=normal, 2=above normal, 3=well above normal)
  • smoke: Whether subject smokes (0=no, 1=yes)
  • alco: Whether subject consumes alcohol (0=no, 1=yes)
  • active: Whether subject engages in physical activity (0=no, 1=yes)
  • cardio: Presence or absence of cardiovascular disease (0=no, 1=yes)
heart2 <- read_delim("~/RStudio Projects/RStudio Data/cardio_train.csv", ";",
                     escape_double = FALSE, col_names = TRUE, trim_ws = TRUE)
Parsed with column specification:
cols(
  id = col_double(),
  age = col_double(),
  gender = col_double(),
  height = col_double(),
  weight = col_double(),
  ap_hi = col_double(),
  ap_lo = col_double(),
  cholesterol = col_double(),
  gluc = col_double(),
  smoke = col_double(),
  alco = col_double(),
  active = col_double(),
  cardio = col_double()
)
heart2 <- heart2 %>% subset(select = 2:13)

Data Description cont.

Data set 3:

Sourced from the ABS, the Australian Health Survey: Biomedical Results for Chronic Diseases, 2011–12 data file was downloaded and saved locally. Variables were imported individually and will be remerged as heart3 under Tidy and Manipulate below.

Selected variables for import from the data set:

  • Total cholesterol: Cholesterol group based on reading (mmol/L)
  • Males: Number of males per cholesterol group (’000)
  • Females: Number of females per cholesterol group (’000)
  • Persons: Number of persons per cholesterol group (’000)
abs_file <- "C:/Users/Wendy/Documents/RStudio Projects/Rstudio Data/43640do004_20112012.xls"

group <- as.data.frame(read_excel(path = abs_file, sheet = "Table_4_1",
                                  range = "A8:A15", col_names = "chol_group"))

males <- as.data.frame(read_excel(path = abs_file, sheet = "Table_4_1",
                                  range = "B8:B15", col_names = "count"))

females <- as.data.frame(read_excel(path = abs_file, sheet = "Table_4_1",
                                    range = "C8:C15", col_names = "count"))

persons <- as.data.frame(read_excel(path = abs_file, sheet = "Table_4_1",
                                    range = "D8:D15", col_names = "count"))

Understanding the Data

After importing, and before preprocessing the data, the dim() and str() functions are used to summarise the variables, understand the size, structures, and data types.

It is apparent that both data sets have imported with all variables classified as numeric. This is not surprising due to the use of numbers to represent ordinal variables.

heart1 %>% dim()
[1] 303  14
heart1 %>% str(give.attr = FALSE)
tibble [303 x 14] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ age     : num [1:303] 63 37 41 56 57 57 56 44 52 57 ...
 $ sex     : num [1:303] 1 1 0 1 0 1 0 1 1 1 ...
 $ cp      : num [1:303] 3 2 1 1 0 0 1 1 2 2 ...
 $ trestbps: num [1:303] 145 130 130 120 120 140 140 120 172 150 ...
 $ chol    : num [1:303] 233 250 204 236 354 192 294 263 199 168 ...
 $ fbs     : num [1:303] 1 0 0 0 0 0 0 0 1 0 ...
 $ restecg : num [1:303] 0 1 0 1 1 1 0 1 1 1 ...
 $ thalach : num [1:303] 150 187 172 178 163 148 153 173 162 174 ...
 $ exang   : num [1:303] 0 0 0 0 1 0 0 0 0 0 ...
 $ oldpeak : num [1:303] 2.3 3.5 1.4 0.8 0.6 0.4 1.3 0 0.5 1.6 ...
 $ slope   : num [1:303] 0 0 2 2 2 1 1 2 2 2 ...
 $ ca      : num [1:303] 0 0 0 0 0 0 0 0 0 0 ...
 $ thal    : num [1:303] 1 2 2 2 2 1 2 3 3 2 ...
 $ target  : num [1:303] 1 1 1 1 1 1 1 1 1 1 ...
heart1 %>% head()

heart2 %>% dim()
[1] 70000    12
heart2 %>% str()
tibble [70,000 x 12] (S3: tbl_df/tbl/data.frame)
 $ age        : num [1:70000] 18393 20228 18857 17623 17474 ...
 $ gender     : num [1:70000] 2 1 1 2 1 1 1 2 1 1 ...
 $ height     : num [1:70000] 168 156 165 169 156 151 157 178 158 164 ...
 $ weight     : num [1:70000] 62 85 64 82 56 67 93 95 71 68 ...
 $ ap_hi      : num [1:70000] 110 140 130 150 100 120 130 130 110 110 ...
 $ ap_lo      : num [1:70000] 80 90 70 100 60 80 80 90 70 60 ...
 $ cholesterol: num [1:70000] 1 3 3 1 1 2 3 3 1 1 ...
 $ gluc       : num [1:70000] 1 1 1 1 1 2 1 3 1 1 ...
 $ smoke      : num [1:70000] 0 0 0 0 0 0 0 0 0 0 ...
 $ alco       : num [1:70000] 0 0 0 0 0 0 0 0 0 0 ...
 $ active     : num [1:70000] 1 1 0 1 0 0 1 1 1 0 ...
 $ cardio     : num [1:70000] 0 1 1 1 0 0 0 1 0 0 ...
heart2 %>% head()

Understanding the Data cont.

Data conversions are needed before merging heart1 and heart2 in order to standardise data formats. The following actions have been taken:

  • Convert heart1 ‘sex’ and heart2 ‘gender’ to a common variable ‘sex’ and values converted from numeric to factor with labels ‘male’ and ‘female’
heart1$sex <- heart1$sex %>% factor(levels = c("0", "1"), labels = c("female", "male"))

heart2 <- heart2 %>% dplyr::rename(sex = gender)
heart2$sex <- heart2$sex %>% factor(levels = c("1", "2"), labels = c("female", "male"))
  • Convert variable heart2 ‘cholesterol’ from numeric to factor with ordered levels by changing cholesterol levels 1, 2, 3 to normal, above normal, and well above normal, aligned with Total Cholesterol levels = Desirable, Borderline High, and High (respectively) reproduced in the table below, based on a WebMD table [2]:
Total Cholesterol Category New values
Less than 200 Desirable Normal
200 - 239 Borderline High Above normal
240 and above High Well above normal
heart2$cholesterol <- heart2$cholesterol %>% factor(levels = c("1", "2", "3"),
         labels = c("normal", "above normal", "well above normal"), ordered = TRUE)
  • Rename variable heart1 ‘target’ to ‘hdisease’ and change values for easier interpretation
heart1 <- heart1 %>% dplyr::rename(hdisease = target)

heart1$hdisease <- heart1$hdisease %>% factor(levels = c("0", "1"), labels = c("no", "yes"))
  • Convert 5 additional variables from numeric to factors.
heart1$fbs <- heart1$fbs %>% as.factor()
heart1$cp <- heart1$cp %>% as.factor()
heart1$exang <- heart1$exang %>% as.factor()
heart1$slope <- heart1$slope %>% as.factor()
heart1$thal <- heart1$thal %>% as.factor()

Understanding the Data cont.

The data is reviewed to confirm the changes have been made:

heart1 %>% head()
heart2 %>% head()

Tidy and Manipulate Data

Finding an untidy data set was challenging. To meet the assignment criteria a third data set was selected and which is ‘untidy’. It was not scraped directly from the web. The data set does not comply with Hadley Wickham’s Tidy Data principle that “(e)ach variable forms a column”[1]. Specifically, it contains variables in both rows (cholesterol groups) and columns (males, females, persons). Steps taken to prepare this data set:

group$country <- "aus"
males$sex <- "male"
females$sex <- "female"
persons$sex <- "all"
males <- cbind(group, males)
females <- cbind(group, females)
persons <- cbind(group, persons)
heart3 <- rbind(males, females, persons)
heart3$chol_group <- heart3$chol_group %>% factor(ordered = TRUE)

Review the changes using the str() function:

heart3 %>% str()
'data.frame':   24 obs. of  4 variables:
 $ chol_group: Ord.factor w/ 8 levels "<4.0"<"≥4.0 to <4.5"<..: 1 2 3 4 5 6 7 8 1 2 ...
 $ country   : chr  "aus" "aus" "aus" "aus" ...
 $ count     : num  1193 1198 1585 1608 1189 ...
 $ sex       : chr  "male" "male" "male" "male" ...

Tidy and Manipulate Data cont.

To merge data from two countries using different cholesterol scores, it is necessary to create a new variable for chol_group to convert the score ranges for Australia (mmol/L) to the US score ranges (mg/dL). The mg/dL thresholds (200, 239, 240 mg/dL) are converted to mmol/L to determine where they fall within the ABS ranges. This is done with the following conversion formula [6]: \(chol (mmol/L) = \frac{chol (mg/dL)}{38.67}\)

norm_au <- 200 / 38.67  # Determine mmol/L value for max of 'normal'
abnorm_au <- 239 / 38.67  # Determine mmol/L value for max of 'above normal'
wanorm_au <- 240 /38.67  # Determine mmol/L value for min of 'well above normal'
norm_au
[1] 5.171968
abnorm_au
[1] 6.180502
wanorm_au
[1] 6.206362

It is clear that the ABS cholesterol groupings cannot be accurately mapped to cholesterol level groups in the WebMD table above as summarised below:

  • ABS <4.0 = normal
  • ABS =4.0 < 4.5 = normal
  • ABS =4.5 < 5.0 = normal
  • ABS =5.0 < 5.5 cannot be categorised, 200mg/dL = 5.17 mmol/L
  • ABS =5.5 < 6.0 = above normal
  • ABS =6.0 < 6.5 cannot be categorised, 240mg/dL = 6.21 mmol/L
  • ABS =6.5 < 7.0 = well above normal
  • ABS > 7.0 = well above normal

As a result, the decision is made not to approximate, therefore heart3 will not be preprocessed further.

Tidy and Manipulate Data cont.

Returning to the heart1 and heart2 data sets:

  • Create a new factor variable heart1 ‘chol_group’ with ordered levels using cut() function
heart1$chol_group <- heart1$chol %>% 
  cut(breaks = c(0, 200, 239, 600), labels = c("normal","above normal","well above normal"),
      ordered = TRUE)
heart1 %>% head()
  • Create a new variable heart2 ‘age_yr’ by calculating \({{age\space (days)} \over {365.25}} = {age\space (years)}\)
heart2$age_yr <- (heart2$age / 365.25) %>% round(0)
heart2 %>% head()

Tidy and Manipulate Data cont.

It is time to create subsets of heart1 and heart2, standardise column names. Verify the subsets are ready to be merged by reviewing the head(). The key variables are:

  • Age (in years)
  • Sex
  • Cholesterol (by group)
heart1_sub <- heart1 %>% select(c(age, sex, chol_group))

heart2_sub <- heart2 %>% select(c(age_yr, sex, cholesterol))
heart2_sub <- heart2_sub %>% dplyr::rename(age = age_yr)
heart2_sub <- heart2_sub %>% dplyr::rename(chol_group = cholesterol)

heart1_sub %>% head()
heart2_sub %>% head()

Tidy and Manipulate Data cont.

  • Merge the subsets using the rbind() function and retain the variable names of heart1
heart_merge <- rbind(heart1_sub, heart2_sub)
  • Confirm the changes using dim() and str() functions
heart_merge %>% dim()
[1] 70303     3
heart_merge %>% str()
tibble [70,303 x 3] (S3: tbl_df/tbl/data.frame)
 $ age       : num [1:70303] 63 37 41 56 57 57 56 44 52 57 ...
 $ sex       : Factor w/ 2 levels "female","male": 2 2 1 2 1 2 1 2 2 2 ...
 $ chol_group: Ord.factor w/ 3 levels "normal"<"above normal"<..: 2 3 2 2 3 1 3 3 1 1 ...

Scan the Data

The data is checked for issues:

colSums(is.na(heart_merge))
       age        sex chol_group 
         0          0          0 
heart_merge %>% summary()
      age            sex                    chol_group   
 Min.   :29.00   female:45626   normal           :52436  
 1st Qu.:48.00   male  :24677   above normal     : 9646  
 Median :54.00                  well above normal: 8221  
 Mean   :53.31                                           
 3rd Qu.:58.00                                           
 Max.   :77.00                                           

Scan the Data cont.

  • To check for outliers, Tukey’s method [7] is used via a boxplot to visualise the data.
heart_merge %>% ggplot(mapping = aes(x = chol_group, y = age)) + 
  geom_boxplot(col = "black", outlier.colour = "red") +
  labs(title = "Boxplot of Age Ranges by Cholesterol Groups (heart_merge)", x = "Cholesterol Group", y = "Age")

The boxplot indicates possible outliers (age per cholesterol group) which may be skewing the results.

Assess outliers by calculating observations with an absolute z-score greater than 3. This method is performed with reference to MATH2349 Module 6 Notes.[7]

z.scores <- heart_merge$age %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.5894 -0.7838  0.1022  0.0000  0.6929  3.4985 
which(abs(z.scores) > 3)
[1]    73   130   145   239  6523 22647 30970 56209

Scan the Data cont.

Eight observations (out of 70,303) were identified as outliers with a Z-score > 3, so these are removed and a new data frame heart_clean is created for further analysis.

heart_clean <- heart_merge[ - which( abs(z.scores) >3 ), ]
heart_clean %>% dim()
[1] 70295     3
heart_clean %>% head()

Rerun the boxplot, after removing outliers with a z-score > 3. It is apparent that the ‘well above normal’ group still has observations which are outside the expected range, and relates to people aged less than 40 with ‘well above normal’ cholesterol levels.

heart_clean %>% ggplot(mapping = aes(x = chol_group, y = age)) +
  geom_boxplot(col = "black", outlier.colour = "red") +
  labs(title = "Boxplot of Age Ranges by Cholesterol Groups (heart_clean)",
       x = "Cholesterol Group", y = "Age")

Transform the Data

The first data transformation occurred in Tidy and Manipulate to create a new variable age_yr from heart2 age (days) through a mathematical calculation. This needed to occur prior to merging the data sets to standardise the data (i.e. to convert a non-linear relation into a linear one).

Noting that heart_clean chol_group “well above normal” still has outliers, a subset heart_wan is created and visualised with a histogram. To make the histogram clearer, an outline/fill has been added to clearly see each column [3]. The histogram shows a left-skew.

heart_wan <- heart_clean %>% filter(chol_group == "well above normal")
heart_wan %>% dim()
[1] 8219    3
ggplot(heart_wan) + geom_histogram(aes(x = age), bins = 15, color="black", fill="white") +
  labs(main = "Age Distribution for Cholesterol Group (well above normal)", x = "Age", y = "Count")

To reduce the skew, the Box-Cox transformation is applied to the subset and visualised by histogram using the function hist(). Note: The transformation would be applied to the complete data set, this is for demonstration purposes only. This method is performed with reference to MATH2349 Module 7 Notes. To reduce the number of \(\lambda\) values printed, the ‘max.print’ argument is added to limit to <100 values [4], and the histogram is manually adjusted [5].

options('max.print' = 60)
getOption('max.print')
[1] 60
boxcox_wan <- BoxCox(heart_wan$age, lambda = "auto")
boxcox_wan
 [1]  683.8387 1623.5640 1567.0813  967.2592 1151.2058 1199.6916 1681.0464 1681.0464
 [9]  923.7716 1859.4911 2519.2817 2111.4121 1457.1147 1457.1147 2111.4121 2111.4121
[17] 1299.6619 1151.2058 1403.6308  759.8178 1351.1466 1103.7197 1299.6619 2176.8913
[25] 1983.4524 1011.7464 1623.5640 2519.2817 1457.1147 1457.1147 1299.6619 1511.5982
[33] 1299.6619 1681.0464  839.7954 1011.7464 1799.0099 1351.1466  881.2837 2243.3702
[41] 2310.8486 1681.0464 1151.2058 1457.1147 1920.9720  923.7716 1403.6308  881.2837
[49] 1739.5284  881.2837 1249.1769 1249.1769 2046.9324 2046.9324 1511.5982 1057.2332
[57] 2046.9324 1739.5284  839.7954 1457.1147
 [ reached getOption("max.print") -- omitted 8159 entries ]
attr(,"lambda")
[1] 1.999924
mean_boxcox <- boxcox_wan %>% mean()
median_boxcox <- boxcox_wan %>% median()

hist_boxcox <- hist(boxcox_wan, breaks = seq(500, 3000, 100), col = "lightgrey",
                    border = "black", xlim = c(500, 3000), ylim = c(0, 1200),
                    main = "Histogram of 'well above normal' Cholesterol Group (Box-Cox)",
                    xlab = "Box-Cox Transformed Values") %>%
  abline(v=c(mean_boxcox, median_boxcox), lty=c(2,3), lwd=2) %>%
  legend(x = "topright", legend = c("Mean ","Median"), lty=c(2,3), lwd=2)

The resulting histogram indicates a left-skew remains in the normalised data. If this were a real study, additional tests would be required (for example, to test for homogeneity of variance) before conducting further statistical analyses.

For this assignment, this concludes the study.

References

[1] Wickham, H., “Tidy Data” (p4), Journal of Statistical Software, Volume 59, Issue 10, August 2014

[2] “Heart Disease and Lowering Cholesterol” (p2), WebMD

[3] “Adding Space between my geom_histogram bars-not barplot” stackoverflow

[4] “Increasing-max-print-does-not-always-work-in-r-and-neither-does-global-options-f” stackoverflow

[5] Davies, Tilman M., 2016 The Book of R: A First Course in Programming and Statistics (Chapter 14, pp294-296), No Starch Press

[6] Lipid Conversion Factors, National Center for Biotechnology Information

[7] Dr Anil Dolgun, 2020 MATH2349 Module Notes

Long, J.D., Teetor, P., 2019 R Cookbook, O’Reilly Media, Inc. 

Xie, Y., Dervieux, C., Riederer, E. 2020 R Markdown Cookbook



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IERhdGEgV3JhbmdsaW5nIg0KYXV0aG9yOiAiV2VuZHkgUnVzc2VsbCwgUzM4NzA3NDMiDQpzdWJ0aXRsZTogIkFzc2lnbm1lbnQgMiINCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50OiANCiAgICBkZl9wcmludDoga2FibGUNCi0tLQ0KDQojIyBSZXF1aXJlZCBwYWNrYWdlcyANCg0KVGhlIGZvbGxvd2luZyBwYWNrYWdlcyB3ZXJlIGNhbGxlZDoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCnJlcXVpcmUocm1hcmtkb3duKQ0KcmVxdWlyZShyZWFkcikNCnJlcXVpcmUocmVhZHhsKQ0KcmVxdWlyZSh0aWR5cikNCnJlcXVpcmUoZHBseXIpDQpyZXF1aXJlKGtuaXRyKQ0KcmVxdWlyZShzdHJpbmdyKQ0KcmVxdWlyZShmb3JlaWduKQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KcmVxdWlyZShsdWJyaWRhdGUpDQpyZXF1aXJlKG91dGxpZXJzKQ0KcmVxdWlyZShmb3JlY2FzdCkNCmBgYA0KDQojIyBFeGVjdXRpdmUgU3VtbWFyeSANCg0KVGhyZWUgZGF0YSBzZXRzIGhhdmUgYmVlbiBzZWxlY3RlZCBmb3IgdGhpcyBhc3NpZ25tZW50Og0KDQoqICAgVHdvIG1lZXQgSGFkbGV5IFdpY2toYW3igJlzIOKAnFRpZHkgRGF0YeKAnSBwcmluY2lwbGVzIFsxXSBhbmQgd2lsbCBiZSBtZXJnZWQgdG8gY3JlYXRlIGEgbGFyZ2VyIHNhbXBsZSBvZiBpbmRpdmlkdWFsIG9ic2VydmF0aW9uczsgYW5kDQoqICAgT25lIGRhdGEgc2V0IGlzIG1lc3N5IGFuZCB3aWxsIGJlIGltcG9ydGVkIHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgdGhhdCBtZWV0cyB0aGUg4oCcVGlkeSBEYXRh4oCdIHByaW5jaXBsZXMuICANCg0KUHJlcHJvY2Vzc2luZyBzdGVwcyBpbmNsdWRlOg0KDQoqICAgQ2hlY2tpbmcgZGF0YSBmcmFtZSBkaW1lbnNpb24gYW5kIHN0cnVjdHVyZSB1c2luZyBkaW0oKSBhbmQgc3RyKCk7DQoqICAgQ29udmVydGluZyB2YXJpYWJsZSB0eXBlcyAoZS5nLiBudW1lcmljIHRvIGZhY3RvciB3aXRoIG9yZGVyZWQgbGV2ZWxzKTsNCiogICBDcmVhdGluZyBhbmQgY29udmVydGluZyB2YXJpYWJsZXMgdG8gc3RhbmRhcmRpc2UgZGF0YSBmb3JtYXRzIGZvciBtZXJnaW5nOyAgDQoqICAgU3ViLXNldHRpbmcgZGF0YSBmcmFtZXMgdXNpbmcgc2VsZWN0KCkgYW5kIGZpbHRlcigpOyAgDQoqICAgUmVuYW1pbmcgdmFyaWFibGVzIHVzaW5nIHJlbmFtZSgpOyBhbmQgIA0KKiAgIE1lcmdpbmcgZGF0YSBzZXRzIHVzaW5nIHJiaW5kKCkgYW5kIGNiaW5kKCkuICANCg0KRGF0YSBzZXRzIHdlcmUgdGhlbiBzY2FubmVkIGZvciBtaXNzaW5nIHZhbHVlcywgc3BlY2lhbCBjaGFyYWN0ZXJzIGFuZCBvdGhlciBvYnZpb3VzIGVycm9ycywgYW5kIGRhdGEgdmlzdWFsaXNhdGlvbiBhbmQgei1zY29yZSB0ZXN0cyB3ZXJlIHVzZWQgdG8gaWRlbnRpZnksIGV2YWx1YXRlIGFuZCBtYW5hZ2Ugb3V0bGllcnMuICANCg0KV2l0aGluIHRoZSBtZXJnZWQgZGF0YSBzZXQsIHR3byB2YXJpYWJsZXMgd2VyZSB0cmFuc2Zvcm1lZCB0bzoNCg0KKiAgIENvbnZlcnQgYSBub24tbGluZWFyIHJlbGF0aW9uc2hpcCB0byBhIGxpbmVhciBvbmU7IGFuZCAgDQoqICAgRGVjcmVhc2UgYSBsZWZ0LXNrZXcgaW4gYW4gYXR0ZW1wdCB0byBjb252ZXJ0IHRoZSBzdWItc2V0IHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCg0KXG5ld3BhZ2UNCiMjIERhdGEgRGVzY3JpcHRpb24gDQoNCiMjIyMgRGF0YSBzZXQgMTogIA0KU291cmNlZCBmcm9tIFtIZWFydCBEaXNlYXNlIFVDSSBvbiBLYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vcm9uaXRmL2hlYXJ0LWRpc2Vhc2UtdWNpL2Rvd25sb2FkKSwgdGhlIGRhdGEgY29udGFpbnMgMzAzIG9ic2VydmF0aW9ucyBvZiAxNCB2YXJpYWJsZXMuICoqImhlYXJ0LmNzdiIqKiB3YXMgZG93bmxvYWRlZCBhcyBhIFpJUCBmaWxlLCB1bmNvbXByZXNzZWQsIGFuZCB0aGUgQ1NWIGZpbGUgc2F2ZWQgbG9jYWxseSwgcHJpb3IgdG8gaW1wb3J0aW5nIGFzICoqaGVhcnQxKiogdXNpbmcgcmVhZF9jc3YoKS4NCg0KVmFyaWFibGVzIGluIHRoZSBkYXRhIHNldDogIA0KDQoqICoqYWdlKio6IEFnZSAoaW4geWVhcnMpICANCiogKipzZXgqKjogU2V4ICgxPU1hbGUsIDA9RmVtYWxlKSAgIA0KKiAqKmNwKio6IENsYXNzaWZpY2F0aW9uIG9mIGNoZXN0IHBhaW4gc3ltcHRvbXMgKDQgY2F0ZWdvcmljYWwgbGV2ZWxzKSAgDQoqICoqdHJlc3RicHMqKjogUmVzdGluZyBibG9vZCBwcmVzc3VyZSAobW0gSGcpICANCiogKipjaG9sKio6IFRvdGFsIGNob2xlc3Rlcm9sIGxldmVsIChtZy9kTCkgIA0KKiAqKmZicyoqOiBGYXN0aW5nIGJsb29kIHN1Z2FyIChpZiA+MTIwIG1nL2RMLCAxPXRydWUsIDA9ZmFsc2UpICANCiogKipyZXN0ZWNnKio6IFJlc3RpbmcgZWxlY3Ryb2NhcmRpb2dyYXBoaWMgcmVzdWx0cyAoMyBjYXRlZ29yaWNhbCBsZXZlbHMpICANCiogKip0aGFsYWNoKio6IE1heGltdW0gaGVhcnQgcmF0ZSBhY2hpZXZlZCAoYnBtKSAgDQoqICoqZXhhbmcqKjogUHJlc2VuY2Ugb2YgZXhlcmNpc2UgaW5kdWNlZCBhbmdpbmEgaW4gdGhlIHBlcnNvbiAoMT15ZXMsIDA9bm8pICANCiogKipvbGRwZWFrKio6IEluZGljYXRlcyB3aGV0aGVyIFNUIGRlcHJlc3Npb24gd2FzIGluZHVjZWQgYnkgZXhlcmNpc2UsIHJlbGF0aXZlIHRvIHJlc3QgIA0KKiAqKnNsb3BlKio6IFNsb3BlIG9mIHRoZSBwZWFrIGV4ZXJjaXNlIFNUIHNlZ21lbnQgKDE9c2xvcGluZyB1cCwgMj1mbGF0LCAzPXNsb3BpbmcgZG93bikgIA0KKiAqKmNhKio6IE51bWJlciBvZiBtYWpvciB2ZXNzZWxzIChmcm9tIDAgdG8gMykgY29sb3VyZWQgYnkgZmx1b3Jvc29weSAgIA0KKiAqKnRoYWwqKjogUHJlc2VuY2Ugb2YgdGhhbGFzc2VtaWEsIGEgYmxvb2QgZGlzb3JkZXIgKDE9bm9ybWFsLCAyPWZpeGVkIGRlZmVjdCwgMz1yZXZlcnNhYmxlIGRlZmVjdCkgIA0KKiAqKnRhcmdldCoqOiBJbmRpY2F0ZXMgd2hldGhlciB0aGUgcGVyc29uIGhhcyBiZWVuIGRpYWdub3NlZCB3aXRoIGhlYXJ0IGRpc2Vhc2UgKDA9bm8sIDE9eWVzKSAgIA0KYGBge3IsIGxheW91dD0ibC1ib2R5IiwgY29scy5wcmludD0xNH0NCmhlYXJ0MSA8LSByZWFkX2Nzdigifi9SU3R1ZGlvIFByb2plY3RzL1JTdHVkaW8gRGF0YS9oZWFydC5jc3YiKQ0KYGBgDQpcbmV3cGFnZQ0KIyMjIERhdGEgRGVzY3JpcHRpb24gY29udC4NCg0KIyMjIyBEYXRhIHNldCAyOg0KU291cmNlZCBmcm9tIFtDYXJkaW92YXNjdWxhciBEaXNlYXNlIG9uIEthZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zdWxpYW5vdmEvY2FyZGlvdmFzY3VsYXItZGlzZWFzZS1kYXRhc2V0P3NlbGVjdD1jYXJkaW9fdHJhaW4uY3N2KS4gVGhlIGRhdGEgY29udGFpbnMgNzAsMDAwIG9ic2VydmF0aW9ucyBvZiAxMyB2YXJpYWJsZXMuICoqImNhcmRpb190cmFpbi5jc3YiKiogd2FzIGRvd25sb2FkZWQgYW5kIHNhdmVkIGxvY2FsbHksIHByaW9yIHRvIGltcG9ydGluZyBhcyAqKmhlYXJ0MioqIHVzaW5nIHJlYWRfZGVsaW0oKS4gVmFyaWFibGUgImlkIiBpcyBub3QgbmVlZGVkIGFuZCB3YXMgcmVtb3ZlZCB1c2luZyBhIHN1YnNldCBjb21tYW5kLiAgDQoNClZhcmlhYmxlcyBpbiB0aGUgZGF0YSBzZXQ6ICANCg0KKiAqKmlkKio6IElkZW50aWZpY2F0aW9uIG51bWJlciBvZiBvYnNlcnZhdGlvbiAgDQoqICoqYWdlKio6IEFnZSAoaW4gZGF5cykgIA0KKiAqKmdlbmRlcioqOiBTZXggKDE9d29tZW4sIDI9bWVuKSAgDQoqICoqaGVpZ2h0Kio6IEhlaWdodCAoY20pICANCiogKipXZWlnaHQqKjogV2VpZ2h0IChrZykgIA0KKiAqKmFwX2hpKio6IFN5c3RvbGljIGJsb29kIHByZXNzdXJlICAgDQoqICoqYXBfbG8qKjogRGlhc3RvbGljIGJsb29kIHByZXNzdXJlICAgIA0KKiAqKmNob2xlc3Rlcm9sKio6IENob2xlc3Rlcm9sIGdyb3VwIGNsYXNzaWZpY2F0aW9uICgxPW5vcm1hbCwgMj1hYm92ZSBub3JtYWwsIDM9d2VsbCBhYm92ZSBub3JtYWwpICANCiogKipnbHVjKio6IEdsdWNvc2UgbGV2ZWwgKDE9bm9ybWFsLCAyPWFib3ZlIG5vcm1hbCwgMz13ZWxsIGFib3ZlIG5vcm1hbCkgIA0KKiAqKnNtb2tlKio6IFdoZXRoZXIgc3ViamVjdCBzbW9rZXMgKDA9bm8sIDE9eWVzKSAgDQoqICoqYWxjbyoqOiBXaGV0aGVyIHN1YmplY3QgY29uc3VtZXMgYWxjb2hvbCAoMD1ubywgMT15ZXMpICANCiogKiphY3RpdmUqKjogV2hldGhlciBzdWJqZWN0IGVuZ2FnZXMgaW4gcGh5c2ljYWwgYWN0aXZpdHkgKDA9bm8sIDE9eWVzKSAgDQoqICoqY2FyZGlvKio6IFByZXNlbmNlIG9yIGFic2VuY2Ugb2YgY2FyZGlvdmFzY3VsYXIgZGlzZWFzZSAoMD1ubywgMT15ZXMpDQoNCmBgYHtyLCBsYXlvdXQ9ImwtYm9keSIsIGNvbHMucHJpbnQ9MTR9DQpoZWFydDIgPC0gcmVhZF9kZWxpbSgifi9SU3R1ZGlvIFByb2plY3RzL1JTdHVkaW8gRGF0YS9jYXJkaW9fdHJhaW4uY3N2IiwgIjsiLA0KICAgICAgICAgICAgICAgICAgICAgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCBjb2xfbmFtZXMgPSBUUlVFLCB0cmltX3dzID0gVFJVRSkNCmhlYXJ0MiA8LSBoZWFydDIgJT4lIHN1YnNldChzZWxlY3QgPSAyOjEzKQ0KYGBgDQpcbmV3cGFnZQ0KIyMjIERhdGEgRGVzY3JpcHRpb24gY29udC4NCg0KIyMjIyBEYXRhIHNldCAzOg0KU291cmNlZCBmcm9tIHRoZSBBQlMsIHRoZSBbQXVzdHJhbGlhbiBIZWFsdGggU3VydmV5OiBCaW9tZWRpY2FsIFJlc3VsdHMgZm9yIENocm9uaWMgRGlzZWFzZXMsIDIwMTHigJMxMl0oaHR0cHM6Ly93d3cuYWJzLmdvdi5hdS9zdGF0aXN0aWNzL2hlYWx0aC9oZWFsdGgtY29uZGl0aW9ucy1hbmQtcmlza3MvYXVzdHJhbGlhbi1oZWFsdGgtc3VydmV5LWJpb21lZGljYWwtcmVzdWx0cy1jaHJvbmljLWRpc2Vhc2VzLzIwMTEtMTIvNDM2NDBkbzAwNF8yMDExMjAxMi54bHMpIGRhdGEgZmlsZSB3YXMgZG93bmxvYWRlZCBhbmQgc2F2ZWQgbG9jYWxseS4gVmFyaWFibGVzIHdlcmUgaW1wb3J0ZWQgaW5kaXZpZHVhbGx5IGFuZCB3aWxsIGJlIHJlbWVyZ2VkIGFzICpoZWFydDMqIHVuZGVyIFRpZHkgYW5kIE1hbmlwdWxhdGUgYmVsb3cuICANCg0KU2VsZWN0ZWQgdmFyaWFibGVzIGZvciBpbXBvcnQgZnJvbSB0aGUgZGF0YSBzZXQ6ICANCg0KKiAqKlRvdGFsIGNob2xlc3Rlcm9sKio6IENob2xlc3Rlcm9sIGdyb3VwIGJhc2VkIG9uIHJlYWRpbmcgKG1tb2wvTCkgIA0KKiAqKk1hbGVzKio6IE51bWJlciBvZiBtYWxlcyBwZXIgY2hvbGVzdGVyb2wgZ3JvdXAgKCcwMDApICANCiogKipGZW1hbGVzKio6IE51bWJlciBvZiBmZW1hbGVzIHBlciBjaG9sZXN0ZXJvbCBncm91cCAoJzAwMCkgIA0KKiAqKlBlcnNvbnMqKjogTnVtYmVyIG9mIHBlcnNvbnMgcGVyIGNob2xlc3Rlcm9sIGdyb3VwICgnMDAwKSAgDQoNCmBgYHtyLCBsYXlvdXQ9ImwtYm9keSIsIGNvbHMucHJpbnQ9MTR9DQphYnNfZmlsZSA8LSAiQzovVXNlcnMvV2VuZHkvRG9jdW1lbnRzL1JTdHVkaW8gUHJvamVjdHMvUnN0dWRpbyBEYXRhLzQzNjQwZG8wMDRfMjAxMTIwMTIueGxzIg0KDQpncm91cCA8LSBhcy5kYXRhLmZyYW1lKHJlYWRfZXhjZWwocGF0aCA9IGFic19maWxlLCBzaGVldCA9ICJUYWJsZV80XzEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gIkE4OkExNSIsIGNvbF9uYW1lcyA9ICJjaG9sX2dyb3VwIikpDQoNCm1hbGVzIDwtIGFzLmRhdGEuZnJhbWUocmVhZF9leGNlbChwYXRoID0gYWJzX2ZpbGUsIHNoZWV0ID0gIlRhYmxlXzRfMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgPSAiQjg6QjE1IiwgY29sX25hbWVzID0gImNvdW50IikpDQoNCmZlbWFsZXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkX2V4Y2VsKHBhdGggPSBhYnNfZmlsZSwgc2hlZXQgPSAiVGFibGVfNF8xIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gIkM4OkMxNSIsIGNvbF9uYW1lcyA9ICJjb3VudCIpKQ0KDQpwZXJzb25zIDwtIGFzLmRhdGEuZnJhbWUocmVhZF9leGNlbChwYXRoID0gYWJzX2ZpbGUsIHNoZWV0ID0gIlRhYmxlXzRfMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9ICJEODpEMTUiLCBjb2xfbmFtZXMgPSAiY291bnQiKSkNCmBgYA0KXG5ld3BhZ2UNCiMjIFVuZGVyc3RhbmRpbmcgdGhlIERhdGEgIA0KDQpBZnRlciBpbXBvcnRpbmcsIGFuZCBiZWZvcmUgcHJlcHJvY2Vzc2luZyB0aGUgZGF0YSwgdGhlIGRpbSgpIGFuZCBzdHIoKSBmdW5jdGlvbnMgYXJlIHVzZWQgdG8gc3VtbWFyaXNlIHRoZSB2YXJpYWJsZXMsIHVuZGVyc3RhbmQgdGhlIHNpemUsIHN0cnVjdHVyZXMsIGFuZCBkYXRhIHR5cGVzLiAgDQoNCkl0IGlzIGFwcGFyZW50IHRoYXQgYm90aCBkYXRhIHNldHMgaGF2ZSBpbXBvcnRlZCB3aXRoIGFsbCB2YXJpYWJsZXMgY2xhc3NpZmllZCBhcyBudW1lcmljLiBUaGlzIGlzIG5vdCBzdXJwcmlzaW5nIGR1ZSB0byB0aGUgdXNlIG9mIG51bWJlcnMgdG8gcmVwcmVzZW50IG9yZGluYWwgdmFyaWFibGVzLiAgDQpgYGB7ciwgbGF5b3V0PSJsLWJvZHkiLCBjb2xzLnByaW50PTE0fQ0KaGVhcnQxICU+JSBkaW0oKQ0KaGVhcnQxICU+JSBzdHIoZ2l2ZS5hdHRyID0gRkFMU0UpDQpoZWFydDEgJT4lIGhlYWQoKQ0KDQpoZWFydDIgJT4lIGRpbSgpDQpoZWFydDIgJT4lIHN0cigpDQpoZWFydDIgJT4lIGhlYWQoKQ0KYGBgDQpcbmV3cGFnZQ0KIyMjIFVuZGVyc3RhbmRpbmcgdGhlIERhdGEgY29udC4NCg0KRGF0YSBjb252ZXJzaW9ucyBhcmUgbmVlZGVkIGJlZm9yZSBtZXJnaW5nICpoZWFydDEqIGFuZCAqaGVhcnQyKiBpbiBvcmRlciB0byBzdGFuZGFyZGlzZSBkYXRhIGZvcm1hdHMuIFRoZSBmb2xsb3dpbmcgYWN0aW9ucyBoYXZlIGJlZW4gdGFrZW46ICANCg0KKiBDb252ZXJ0ICpoZWFydDEqICdzZXgnIGFuZCAqaGVhcnQyKiAnZ2VuZGVyJyB0byBhIGNvbW1vbiB2YXJpYWJsZSAnc2V4JyBhbmQgdmFsdWVzIGNvbnZlcnRlZCBmcm9tIG51bWVyaWMgdG8gZmFjdG9yIHdpdGggbGFiZWxzICdtYWxlJyBhbmQgJ2ZlbWFsZScgIA0KYGBge3J9DQpoZWFydDEkc2V4IDwtIGhlYXJ0MSRzZXggJT4lIGZhY3RvcihsZXZlbHMgPSBjKCIwIiwgIjEiKSwgbGFiZWxzID0gYygiZmVtYWxlIiwgIm1hbGUiKSkNCg0KaGVhcnQyIDwtIGhlYXJ0MiAlPiUgZHBseXI6OnJlbmFtZShzZXggPSBnZW5kZXIpDQpoZWFydDIkc2V4IDwtIGhlYXJ0MiRzZXggJT4lIGZhY3RvcihsZXZlbHMgPSBjKCIxIiwgIjIiKSwgbGFiZWxzID0gYygiZmVtYWxlIiwgIm1hbGUiKSkNCmBgYA0KDQoqIENvbnZlcnQgdmFyaWFibGUgKmhlYXJ0MiogJ2Nob2xlc3Rlcm9sJyBmcm9tIG51bWVyaWMgdG8gZmFjdG9yIHdpdGggb3JkZXJlZCBsZXZlbHMgYnkgY2hhbmdpbmcgY2hvbGVzdGVyb2wgbGV2ZWxzIDEsIDIsIDMgdG8gbm9ybWFsLCBhYm92ZSBub3JtYWwsIGFuZCB3ZWxsIGFib3ZlIG5vcm1hbCwgYWxpZ25lZCB3aXRoIFRvdGFsIENob2xlc3Rlcm9sIGxldmVscyA9IERlc2lyYWJsZSwgQm9yZGVybGluZSBIaWdoLCBhbmQgSGlnaCAocmVzcGVjdGl2ZWx5KSByZXByb2R1Y2VkIGluIHRoZSB0YWJsZSBiZWxvdywgYmFzZWQgb24gYSBXZWJNRCB0YWJsZSBbMl06ICANCg0KIFRvdGFsIENob2xlc3Rlcm9sICB8ICBDYXRlZ29yeSAgICAgICAgIHwgIE5ldyB2YWx1ZXMgIA0KLS0tLS0tLS0tLS0tLS0tLS0tICB8ICAtLS0tLS0tLS0tLS0tLS0gIHwgIC0tLS0tLS0tLS0tLS0tLS0gIA0KICBMZXNzIHRoYW4gMjAwICAgICB8ICBEZXNpcmFibGUgICAgICAgIHwgIE5vcm1hbCAgICANCiAgICAyMDAgLSAyMzkgICAgICAgfCAgQm9yZGVybGluZSBIaWdoICB8ICBBYm92ZSBub3JtYWwgICAgICANCiAgMjQwIGFuZCBhYm92ZSAgICAgfCAgSGlnaCAgICAgICAgICAgICB8ICBXZWxsIGFib3ZlIG5vcm1hbCAgICAgICAgIA0KYGBge3J9DQpoZWFydDIkY2hvbGVzdGVyb2wgPC0gaGVhcnQyJGNob2xlc3Rlcm9sICU+JSBmYWN0b3IobGV2ZWxzID0gYygiMSIsICIyIiwgIjMiKSwNCiAgICAgICAgIGxhYmVscyA9IGMoIm5vcm1hbCIsICJhYm92ZSBub3JtYWwiLCAid2VsbCBhYm92ZSBub3JtYWwiKSwgb3JkZXJlZCA9IFRSVUUpDQpgYGANCg0KKiBSZW5hbWUgdmFyaWFibGUgKmhlYXJ0MSogJ3RhcmdldCcgdG8gJ2hkaXNlYXNlJyBhbmQgY2hhbmdlIHZhbHVlcyBmb3IgZWFzaWVyIGludGVycHJldGF0aW9uDQpgYGB7cn0NCmhlYXJ0MSA8LSBoZWFydDEgJT4lIGRwbHlyOjpyZW5hbWUoaGRpc2Vhc2UgPSB0YXJnZXQpDQoNCmhlYXJ0MSRoZGlzZWFzZSA8LSBoZWFydDEkaGRpc2Vhc2UgJT4lIGZhY3RvcihsZXZlbHMgPSBjKCIwIiwgIjEiKSwgbGFiZWxzID0gYygibm8iLCAieWVzIikpDQpgYGANCg0KKiBDb252ZXJ0IDUgYWRkaXRpb25hbCB2YXJpYWJsZXMgZnJvbSBudW1lcmljIHRvIGZhY3RvcnMuICANCmBgYHtyfQ0KaGVhcnQxJGZicyA8LSBoZWFydDEkZmJzICU+JSBhcy5mYWN0b3IoKQ0KaGVhcnQxJGNwIDwtIGhlYXJ0MSRjcCAlPiUgYXMuZmFjdG9yKCkNCmhlYXJ0MSRleGFuZyA8LSBoZWFydDEkZXhhbmcgJT4lIGFzLmZhY3RvcigpDQpoZWFydDEkc2xvcGUgPC0gaGVhcnQxJHNsb3BlICU+JSBhcy5mYWN0b3IoKQ0KaGVhcnQxJHRoYWwgPC0gaGVhcnQxJHRoYWwgJT4lIGFzLmZhY3RvcigpDQpgYGANClxuZXdwYWdlDQojIyMgVW5kZXJzdGFuZGluZyB0aGUgRGF0YSBjb250Lg0KDQpUaGUgZGF0YSBpcyByZXZpZXdlZCB0byBjb25maXJtIHRoZSBjaGFuZ2VzIGhhdmUgYmVlbiBtYWRlOg0KYGBge3IsIGxheW91dD0iMS1ib2R5IiwgY29scy5wcmludD0xNH0NCmhlYXJ0MSAlPiUgaGVhZCgpDQpoZWFydDIgJT4lIGhlYWQoKQ0KYGBgDQpcbmV3cGFnZQ0KIyMgVGlkeSBhbmQgTWFuaXB1bGF0ZSBEYXRhDQoNCkZpbmRpbmcgYW4gdW50aWR5IGRhdGEgc2V0IHdhcyBjaGFsbGVuZ2luZy4gVG8gbWVldCB0aGUgYXNzaWdubWVudCBjcml0ZXJpYSBhIHRoaXJkIGRhdGEgc2V0IHdhcyBzZWxlY3RlZCBhbmQgd2hpY2ggaXMgJ3VudGlkeScuIEl0IHdhcyBub3Qgc2NyYXBlZCBkaXJlY3RseSBmcm9tIHRoZSB3ZWIuIFRoZSBkYXRhIHNldCBkb2VzIG5vdCBjb21wbHkgd2l0aCBIYWRsZXkgV2lja2hhbSdzIFRpZHkgRGF0YSBwcmluY2lwbGUgdGhhdCAiKGUpYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uIlsxXS4gU3BlY2lmaWNhbGx5LCBpdCBjb250YWlucyB2YXJpYWJsZXMgaW4gYm90aCByb3dzIChjaG9sZXN0ZXJvbCBncm91cHMpIGFuZCBjb2x1bW5zIChtYWxlcywgZmVtYWxlcywgcGVyc29ucykuIFN0ZXBzIHRha2VuIHRvIHByZXBhcmUgdGhpcyBkYXRhIHNldDogIA0KDQoqIE5ldyB2YXJpYWJsZXMgd2VyZSBjcmVhdGVkIChjb3VudHJ5IGFuZCBzZXgpIGFuZCBhcHBlbmRlZCB0byBkYXRhIGZyYW1lcyAgDQpgYGB7cn0NCmdyb3VwJGNvdW50cnkgPC0gImF1cyINCm1hbGVzJHNleCA8LSAibWFsZSINCmZlbWFsZXMkc2V4IDwtICJmZW1hbGUiDQpwZXJzb25zJHNleCA8LSAiYWxsIg0KYGBgDQoNCiogRGF0YSBmcmFtZSAqZ3JvdXAqIG1lcmdlZCB3aXRoICptYWxlcyosICpmZW1hbGVzKiBhbmQgKnBlcnNvbnMqIGRhdGEgZnJhbWVzIHVzaW5nIGNiaW5kKCkgdG8gY3JlYXRlIHRocmVlIGRmcyBvZiBkaW1lbnNpb24gKDggeCA0KSAgDQpgYGB7cn0NCm1hbGVzIDwtIGNiaW5kKGdyb3VwLCBtYWxlcykNCmZlbWFsZXMgPC0gY2JpbmQoZ3JvdXAsIGZlbWFsZXMpDQpwZXJzb25zIDwtIGNiaW5kKGdyb3VwLCBwZXJzb25zKQ0KYGBgDQoNCiogVGhlIHJlc3VsdGluZyAzIGRhdGEgZnJhbWVzICptYWxlcyosICpmZW1hbGVzKiBhbmQgKnBlcnNvbnMqIGFyZSBtZXJnZWQgdXNpbmcgcmJpbmQoKSB0byBjcmVhdGUgKipoZWFydDMqKg0KYGBge3J9DQpoZWFydDMgPC0gcmJpbmQobWFsZXMsIGZlbWFsZXMsIHBlcnNvbnMpDQpgYGANCg0KKiBUaGUgdmFyaWFibGUgJ2Nob2xfZ3JvdXAnIGlzIGNvbnZlcnRlZCB0byBhbiBvcmRlcmVkIGZhY3RvciB3aXRoIDggbGV2ZWxzDQpgYGB7cn0NCmhlYXJ0MyRjaG9sX2dyb3VwIDwtIGhlYXJ0MyRjaG9sX2dyb3VwICU+JSBmYWN0b3Iob3JkZXJlZCA9IFRSVUUpDQpgYGANCg0KUmV2aWV3IHRoZSBjaGFuZ2VzIHVzaW5nIHRoZSBzdHIoKSBmdW5jdGlvbjoNCmBgYHtyfQ0KaGVhcnQzICU+JSBzdHIoKQ0KYGBgDQpcbmV3cGFnZQ0KIyMjIFRpZHkgYW5kIE1hbmlwdWxhdGUgRGF0YSBjb250LiAgDQoNClRvIG1lcmdlIGRhdGEgZnJvbSB0d28gY291bnRyaWVzIHVzaW5nIGRpZmZlcmVudCBjaG9sZXN0ZXJvbCBzY29yZXMsIGl0IGlzIG5lY2Vzc2FyeSB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgZm9yIGNob2xfZ3JvdXAgdG8gY29udmVydCB0aGUgc2NvcmUgcmFuZ2VzIGZvciBBdXN0cmFsaWEgKG1tb2wvTCkgdG8gdGhlIFVTIHNjb3JlIHJhbmdlcyAobWcvZEwpLiAgVGhlIG1nL2RMIHRocmVzaG9sZHMgKDIwMCwgMjM5LCAyNDAgbWcvZEwpIGFyZSBjb252ZXJ0ZWQgdG8gbW1vbC9MIHRvIGRldGVybWluZSB3aGVyZSB0aGV5IGZhbGwgd2l0aGluIHRoZSBBQlMgcmFuZ2VzLiBUaGlzIGlzIGRvbmUgd2l0aCB0aGUgZm9sbG93aW5nIGNvbnZlcnNpb24gZm9ybXVsYSBbNl06ICRjaG9sIChtbW9sL0wpID0gXGZyYWN7Y2hvbCAobWcvZEwpfXszOC42N30kICANCmBgYHtyfQ0Kbm9ybV9hdSA8LSAyMDAgLyAzOC42NyAgIyBEZXRlcm1pbmUgbW1vbC9MIHZhbHVlIGZvciBtYXggb2YgJ25vcm1hbCcNCmFibm9ybV9hdSA8LSAyMzkgLyAzOC42NyAgIyBEZXRlcm1pbmUgbW1vbC9MIHZhbHVlIGZvciBtYXggb2YgJ2Fib3ZlIG5vcm1hbCcNCndhbm9ybV9hdSA8LSAyNDAgLzM4LjY3ICAjIERldGVybWluZSBtbW9sL0wgdmFsdWUgZm9yIG1pbiBvZiAnd2VsbCBhYm92ZSBub3JtYWwnDQpub3JtX2F1DQphYm5vcm1fYXUNCndhbm9ybV9hdQ0KYGBgDQoNCkl0IGlzIGNsZWFyIHRoYXQgdGhlIEFCUyBjaG9sZXN0ZXJvbCBncm91cGluZ3MgY2Fubm90IGJlIGFjY3VyYXRlbHkgbWFwcGVkIHRvIGNob2xlc3Rlcm9sIGxldmVsIGdyb3VwcyBpbiB0aGUgV2ViTUQgdGFibGUgYWJvdmUgYXMgc3VtbWFyaXNlZCBiZWxvdzoNCg0KKiBBQlMgPDQuMCAgICAgICAgICA9IG5vcm1hbA0KKiBBQlMgPTQuMCA8IDQuNSAgICA9IG5vcm1hbA0KKiBBQlMgPTQuNSA8IDUuMCAgICA9IG5vcm1hbA0KKiBBQlMgPTUuMCA8IDUuNSAgICAqKmNhbm5vdCBiZSBjYXRlZ29yaXNlZCwgMjAwbWcvZEwgPSA1LjE3IG1tb2wvTCoqDQoqIEFCUyA9NS41IDwgNi4wICAgID0gYWJvdmUgbm9ybWFsDQoqIEFCUyA9Ni4wIDwgNi41ICAgICoqY2Fubm90IGJlIGNhdGVnb3Jpc2VkLCAyNDBtZy9kTCA9IDYuMjEgbW1vbC9MKioNCiogQUJTID02LjUgPCA3LjAgICAgPSB3ZWxsIGFib3ZlIG5vcm1hbA0KKiBBQlMgPiA3LjAgICAgICAgICA9IHdlbGwgYWJvdmUgbm9ybWFsDQoNCkFzIGEgcmVzdWx0LCB0aGUgZGVjaXNpb24gaXMgbWFkZSBub3QgdG8gYXBwcm94aW1hdGUsIHRoZXJlZm9yZSAqaGVhcnQzKiB3aWxsIG5vdCBiZSBwcmVwcm9jZXNzZWQgZnVydGhlci4gIA0KDQpcbmV3cGFnZQ0KIyMjIFRpZHkgYW5kIE1hbmlwdWxhdGUgRGF0YSBjb250Lg0KDQojIyMjIFJldHVybmluZyB0byB0aGUgKmhlYXJ0MSogYW5kICpoZWFydDIqIGRhdGEgc2V0czoNCg0KKiBDcmVhdGUgYSBuZXcgZmFjdG9yIHZhcmlhYmxlICpoZWFydDEqICdjaG9sX2dyb3VwJyB3aXRoIG9yZGVyZWQgbGV2ZWxzIHVzaW5nIGN1dCgpIGZ1bmN0aW9uDQpgYGB7ciwgbGF5b3V0PSIxLWJvZHkiLCBjb2xzLnByaW50PTE1fQ0KaGVhcnQxJGNob2xfZ3JvdXAgPC0gaGVhcnQxJGNob2wgJT4lIA0KICBjdXQoYnJlYWtzID0gYygwLCAyMDAsIDIzOSwgNjAwKSwgbGFiZWxzID0gYygibm9ybWFsIiwiYWJvdmUgbm9ybWFsIiwid2VsbCBhYm92ZSBub3JtYWwiKSwNCiAgICAgIG9yZGVyZWQgPSBUUlVFKQ0KaGVhcnQxICU+JSBoZWFkKCkNCmBgYA0KICANCiogQ3JlYXRlIGEgbmV3IHZhcmlhYmxlICpoZWFydDIqICdhZ2VfeXInIGJ5IGNhbGN1bGF0aW5nICR7e2FnZVxzcGFjZSAoZGF5cyl9IFxvdmVyIHszNjUuMjV9fSA9IHthZ2Vcc3BhY2UgKHllYXJzKX0kICANCmBgYHtyLCBsYXlvdXQ9IjEtYm9keSIsIGNvbHMucHJpbnQ9MTN9DQpoZWFydDIkYWdlX3lyIDwtIChoZWFydDIkYWdlIC8gMzY1LjI1KSAlPiUgcm91bmQoMCkNCmhlYXJ0MiAlPiUgaGVhZCgpDQpgYGANCg0KXG5ld3BhZ2UNCiMjIyBUaWR5IGFuZCBNYW5pcHVsYXRlIERhdGEgY29udC4NCg0KSXQgaXMgdGltZSB0byBjcmVhdGUgc3Vic2V0cyBvZiAqaGVhcnQxKiBhbmQgKmhlYXJ0MiosIHN0YW5kYXJkaXNlIGNvbHVtbiBuYW1lcy4gVmVyaWZ5IHRoZSBzdWJzZXRzIGFyZSByZWFkeSB0byBiZSBtZXJnZWQgYnkgcmV2aWV3aW5nIHRoZSBoZWFkKCkuIFRoZSBrZXkgdmFyaWFibGVzIGFyZToNCg0KKiBBZ2UgKGluIHllYXJzKSAgDQoqIFNleCAgDQoqIENob2xlc3Rlcm9sIChieSBncm91cCkgIA0KYGBge3J9DQpoZWFydDFfc3ViIDwtIGhlYXJ0MSAlPiUgc2VsZWN0KGMoYWdlLCBzZXgsIGNob2xfZ3JvdXApKQ0KDQpoZWFydDJfc3ViIDwtIGhlYXJ0MiAlPiUgc2VsZWN0KGMoYWdlX3lyLCBzZXgsIGNob2xlc3Rlcm9sKSkNCmhlYXJ0Ml9zdWIgPC0gaGVhcnQyX3N1YiAlPiUgZHBseXI6OnJlbmFtZShhZ2UgPSBhZ2VfeXIpDQpoZWFydDJfc3ViIDwtIGhlYXJ0Ml9zdWIgJT4lIGRwbHlyOjpyZW5hbWUoY2hvbF9ncm91cCA9IGNob2xlc3Rlcm9sKQ0KDQpoZWFydDFfc3ViICU+JSBoZWFkKCkNCmhlYXJ0Ml9zdWIgJT4lIGhlYWQoKQ0KYGBgDQpcbmV3cGFnZQ0KIyMjIFRpZHkgYW5kIE1hbmlwdWxhdGUgRGF0YSBjb250Lg0KDQoqIE1lcmdlIHRoZSBzdWJzZXRzIHVzaW5nIHRoZSByYmluZCgpIGZ1bmN0aW9uIGFuZCByZXRhaW4gdGhlIHZhcmlhYmxlIG5hbWVzIG9mICpoZWFydDEqDQpgYGB7cn0NCmhlYXJ0X21lcmdlIDwtIHJiaW5kKGhlYXJ0MV9zdWIsIGhlYXJ0Ml9zdWIpDQpgYGANCg0KKiBDb25maXJtIHRoZSBjaGFuZ2VzIHVzaW5nIGRpbSgpIGFuZCBzdHIoKSBmdW5jdGlvbnMNCmBgYHtyfQ0KaGVhcnRfbWVyZ2UgJT4lIGRpbSgpDQpoZWFydF9tZXJnZSAlPiUgc3RyKCkNCmBgYA0KDQojIwlTY2FuIHRoZSBEYXRhICANCg0KVGhlIGRhdGEgaXMgY2hlY2tlZCBmb3IgaXNzdWVzOiAgDQoNCiogVGhlIGNvbFN1bXMoKSBmdW5jdGlvbiB3YXMgdXNlZCB0byBjaGVjayBmb3IgTkEgb3IgbWlzc2luZyB2YWx1ZXMgYWNyb3NzIGFsbCBjb2x1bW5zIGluIHRoZSBkYXRhIGZyYW1lczsgIA0KYGBge3J9DQpjb2xTdW1zKGlzLm5hKGhlYXJ0X21lcmdlKSkNCmBgYA0KDQoqIFRoZSBzdW1tYXJ5KCkgZnVuY3Rpb24gd2FzIHVzZWQgdG8gY2hlY2sgZm9yIG5vbi1udW1lcmljIHZhbHVlcyBvciBzcGVjaWFsIGNoYXJhY3RlcnMgaW4gbnVtZXJpYyB2YXJpYWJsZSBmaWVsZHMNCmBgYHtyfQ0KaGVhcnRfbWVyZ2UgJT4lIHN1bW1hcnkoKQ0KYGBgDQpcbmV3cGFnZQ0KIyMjCVNjYW4gdGhlIERhdGEgY29udC4NCg0KKiBUbyBjaGVjayBmb3Igb3V0bGllcnMsIFR1a2V5J3MgbWV0aG9kIFs3XSBpcyB1c2VkIHZpYSBhIGJveHBsb3QgdG8gdmlzdWFsaXNlIHRoZSBkYXRhLiAgDQpgYGB7ciBvdXQuaGVpZ2h0PSc2MCUnLCBvdXQud2lkdGg9JzYwJSd9DQpoZWFydF9tZXJnZSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNob2xfZ3JvdXAsIHkgPSBhZ2UpKSArIA0KICBnZW9tX2JveHBsb3QoY29sID0gImJsYWNrIiwgb3V0bGllci5jb2xvdXIgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIkJveHBsb3Qgb2YgQWdlIFJhbmdlcyBieSBDaG9sZXN0ZXJvbCBHcm91cHMgKGhlYXJ0X21lcmdlKSIsIHggPSAiQ2hvbGVzdGVyb2wgR3JvdXAiLCB5ID0gIkFnZSIpDQpgYGANCg0KVGhlIGJveHBsb3QgaW5kaWNhdGVzIHBvc3NpYmxlIG91dGxpZXJzIChhZ2UgcGVyIGNob2xlc3Rlcm9sIGdyb3VwKSB3aGljaCBtYXkgYmUgc2tld2luZyB0aGUgcmVzdWx0cy4gIA0KDQpBc3Nlc3Mgb3V0bGllcnMgYnkgY2FsY3VsYXRpbmcgb2JzZXJ2YXRpb25zIHdpdGggYW4gYWJzb2x1dGUgei1zY29yZSBncmVhdGVyIHRoYW4gMy4gVGhpcyBtZXRob2QgaXMgcGVyZm9ybWVkIHdpdGggcmVmZXJlbmNlIHRvIE1BVEgyMzQ5IE1vZHVsZSA2IE5vdGVzLls3XQ0KYGBge3J9DQp6LnNjb3JlcyA8LSBoZWFydF9tZXJnZSRhZ2UgJT4lIHNjb3Jlcyh0eXBlID0gInoiKQ0Kei5zY29yZXMgJT4lIHN1bW1hcnkoKQ0Kd2hpY2goYWJzKHouc2NvcmVzKSA+IDMpDQpgYGANClxuZXdwYWdlDQojIyMJU2NhbiB0aGUgRGF0YSBjb250Lg0KDQpFaWdodCBvYnNlcnZhdGlvbnMgKG91dCBvZiA3MCwzMDMpIHdlcmUgaWRlbnRpZmllZCBhcyBvdXRsaWVycyB3aXRoIGEgWi1zY29yZSA+IDMsIHNvIHRoZXNlIGFyZSByZW1vdmVkIGFuZCBhIG5ldyBkYXRhIGZyYW1lICpoZWFydF9jbGVhbiogaXMgY3JlYXRlZCBmb3IgZnVydGhlciBhbmFseXNpcy4NCmBgYHtyfQ0KaGVhcnRfY2xlYW4gPC0gaGVhcnRfbWVyZ2VbIC0gd2hpY2goIGFicyh6LnNjb3JlcykgPjMgKSwgXQ0KaGVhcnRfY2xlYW4gJT4lIGRpbSgpDQpoZWFydF9jbGVhbiAlPiUgaGVhZCgpDQpgYGANCg0KUmVydW4gdGhlIGJveHBsb3QsIGFmdGVyIHJlbW92aW5nIG91dGxpZXJzIHdpdGggYSB6LXNjb3JlID4gMy4gIEl0IGlzIGFwcGFyZW50IHRoYXQgdGhlICd3ZWxsIGFib3ZlIG5vcm1hbCcgZ3JvdXAgc3RpbGwgaGFzIG9ic2VydmF0aW9ucyB3aGljaCBhcmUgb3V0c2lkZSB0aGUgZXhwZWN0ZWQgcmFuZ2UsIGFuZCByZWxhdGVzIHRvIHBlb3BsZSBhZ2VkIGxlc3MgdGhhbiA0MCB3aXRoICd3ZWxsIGFib3ZlIG5vcm1hbCcgY2hvbGVzdGVyb2wgbGV2ZWxzLiANCmBgYHtyIG91dC5oZWlnaHQ9JzYwJScsIG91dC53aWR0aD0nNjAlJ30NCmhlYXJ0X2NsZWFuICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY2hvbF9ncm91cCwgeSA9IGFnZSkpICsNCiAgZ2VvbV9ib3hwbG90KGNvbCA9ICJibGFjayIsIG91dGxpZXIuY29sb3VyID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJCb3hwbG90IG9mIEFnZSBSYW5nZXMgYnkgQ2hvbGVzdGVyb2wgR3JvdXBzIChoZWFydF9jbGVhbikiLA0KICAgICAgIHggPSAiQ2hvbGVzdGVyb2wgR3JvdXAiLCB5ID0gIkFnZSIpDQpgYGANCg0KIyMJVHJhbnNmb3JtIHRoZSBEYXRhICANCg0KVGhlIGZpcnN0IGRhdGEgdHJhbnNmb3JtYXRpb24gb2NjdXJyZWQgaW4gVGlkeSBhbmQgTWFuaXB1bGF0ZSB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgYWdlX3lyIGZyb20gKmhlYXJ0MiogYWdlIChkYXlzKSB0aHJvdWdoIGEgbWF0aGVtYXRpY2FsIGNhbGN1bGF0aW9uLiBUaGlzIG5lZWRlZCB0byBvY2N1ciBwcmlvciB0byBtZXJnaW5nIHRoZSBkYXRhIHNldHMgdG8gc3RhbmRhcmRpc2UgdGhlIGRhdGEgKGkuZS4gdG8gY29udmVydCBhIG5vbi1saW5lYXIgcmVsYXRpb24gaW50byBhIGxpbmVhciBvbmUpLg0KDQpOb3RpbmcgdGhhdCAqaGVhcnRfY2xlYW4qIGNob2xfZ3JvdXAgIndlbGwgYWJvdmUgbm9ybWFsIiBzdGlsbCBoYXMgb3V0bGllcnMsIGEgc3Vic2V0ICpoZWFydF93YW4qIGlzIGNyZWF0ZWQgYW5kIHZpc3VhbGlzZWQgd2l0aCBhIGhpc3RvZ3JhbS4gVG8gbWFrZSB0aGUgaGlzdG9ncmFtIGNsZWFyZXIsIGFuIG91dGxpbmUvZmlsbCBoYXMgYmVlbiBhZGRlZCB0byBjbGVhcmx5IHNlZSBlYWNoIGNvbHVtbiBbM10uIFRoZSBoaXN0b2dyYW0gc2hvd3MgYSBsZWZ0LXNrZXcuDQpgYGB7ciBvdXQud2lkdGg9JzYwJSd9DQpoZWFydF93YW4gPC0gaGVhcnRfY2xlYW4gJT4lIGZpbHRlcihjaG9sX2dyb3VwID09ICJ3ZWxsIGFib3ZlIG5vcm1hbCIpDQpoZWFydF93YW4gJT4lIGRpbSgpDQoNCmdncGxvdChoZWFydF93YW4pICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBhZ2UpLCBiaW5zID0gMTUsIGNvbG9yPSJibGFjayIsIGZpbGw9IndoaXRlIikgKw0KICBsYWJzKG1haW4gPSAiQWdlIERpc3RyaWJ1dGlvbiBmb3IgQ2hvbGVzdGVyb2wgR3JvdXAgKHdlbGwgYWJvdmUgbm9ybWFsKSIsIHggPSAiQWdlIiwgeSA9ICJDb3VudCIpDQpgYGANCg0KVG8gcmVkdWNlIHRoZSBza2V3LCB0aGUgQm94LUNveCB0cmFuc2Zvcm1hdGlvbiBpcyBhcHBsaWVkIHRvIHRoZSBzdWJzZXQgYW5kIHZpc3VhbGlzZWQgYnkgaGlzdG9ncmFtIHVzaW5nIHRoZSBmdW5jdGlvbiBoaXN0KCkuICpOb3RlOiBUaGUgdHJhbnNmb3JtYXRpb24gd291bGQgYmUgYXBwbGllZCB0byB0aGUgY29tcGxldGUgZGF0YSBzZXQsIHRoaXMgaXMgZm9yIGRlbW9uc3RyYXRpb24gcHVycG9zZXMgb25seS4qIFRoaXMgbWV0aG9kIGlzIHBlcmZvcm1lZCB3aXRoIHJlZmVyZW5jZSB0byBNQVRIMjM0OSBNb2R1bGUgNyBOb3Rlcy4gVG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgJFxsYW1iZGEkIHZhbHVlcyBwcmludGVkLCB0aGUgJ21heC5wcmludCcgYXJndW1lbnQgaXMgYWRkZWQgdG8gbGltaXQgdG8gPDEwMCB2YWx1ZXMgWzRdLCBhbmQgdGhlIGhpc3RvZ3JhbSBpcyBtYW51YWxseSBhZGp1c3RlZCBbNV0uDQpgYGB7ciBvdXQuaGVpZ2h0PSc2MCUnLCBvdXQud2lkdGg9JzYwJSd9DQpvcHRpb25zKCdtYXgucHJpbnQnID0gNjApDQpnZXRPcHRpb24oJ21heC5wcmludCcpDQoNCmJveGNveF93YW4gPC0gQm94Q294KGhlYXJ0X3dhbiRhZ2UsIGxhbWJkYSA9ICJhdXRvIikNCmJveGNveF93YW4NCg0KbWVhbl9ib3hjb3ggPC0gYm94Y294X3dhbiAlPiUgbWVhbigpDQptZWRpYW5fYm94Y294IDwtIGJveGNveF93YW4gJT4lIG1lZGlhbigpDQoNCmhpc3RfYm94Y294IDwtIGhpc3QoYm94Y294X3dhbiwgYnJlYWtzID0gc2VxKDUwMCwgMzAwMCwgMTAwKSwgY29sID0gImxpZ2h0Z3JleSIsDQogICAgICAgICAgICAgICAgICAgIGJvcmRlciA9ICJibGFjayIsIHhsaW0gPSBjKDUwMCwgMzAwMCksIHlsaW0gPSBjKDAsIDEyMDApLA0KICAgICAgICAgICAgICAgICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiAnd2VsbCBhYm92ZSBub3JtYWwnIENob2xlc3Rlcm9sIEdyb3VwIChCb3gtQ294KSIsDQogICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQm94LUNveCBUcmFuc2Zvcm1lZCBWYWx1ZXMiKSAlPiUNCiAgYWJsaW5lKHY9YyhtZWFuX2JveGNveCwgbWVkaWFuX2JveGNveCksIGx0eT1jKDIsMyksIGx3ZD0yKSAlPiUNCiAgbGVnZW5kKHggPSAidG9wcmlnaHQiLCBsZWdlbmQgPSBjKCJNZWFuICIsIk1lZGlhbiIpLCBsdHk9YygyLDMpLCBsd2Q9MikNCmBgYA0KDQpUaGUgcmVzdWx0aW5nIGhpc3RvZ3JhbSBpbmRpY2F0ZXMgYSBsZWZ0LXNrZXcgcmVtYWlucyBpbiB0aGUgbm9ybWFsaXNlZCBkYXRhLiBJZiB0aGlzIHdlcmUgYSByZWFsIHN0dWR5LCBhZGRpdGlvbmFsIHRlc3RzIHdvdWxkIGJlIHJlcXVpcmVkIChmb3IgZXhhbXBsZSwgdG8gdGVzdCBmb3IgaG9tb2dlbmVpdHkgb2YgdmFyaWFuY2UpIGJlZm9yZSBjb25kdWN0aW5nIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzZXMuDQoNCkZvciB0aGlzIGFzc2lnbm1lbnQsIHRoaXMgY29uY2x1ZGVzIHRoZSBzdHVkeS4gIA0KDQpcbmV3cGFnZSAgDQojIyBSZWZlcmVuY2VzICANCg0KWzFdIFdpY2toYW0sIEguLCAqIlRpZHkgRGF0YSIqIChwNCksIFtKb3VybmFsIG9mIFN0YXRpc3RpY2FsIFNvZnR3YXJlLCBWb2x1bWUgNTksIElzc3VlIDEwLCBBdWd1c3QgMjAxNF0oaHR0cHM6Ly93d3cuanN0YXRzb2Z0Lm9yZy9pbmRleC5waHAvanNzL2FydGljbGUvdmlldy92MDU5aTEwL3Y1OWkxMC5wZGYpICANCg0KWzJdICoiSGVhcnQgRGlzZWFzZSBhbmQgTG93ZXJpbmcgQ2hvbGVzdGVyb2wiKiAocDIpLCBbV2ViTURdKGh0dHBzOi8vd3d3LndlYm1kLmNvbS9oZWFydC1kaXNlYXNlL2d1aWRlL2hlYXJ0LWRpc2Vhc2UtbG93ZXItY2hvbGVzdGVyb2wtcmlzayMyKSAgDQoNClszXSAqIkFkZGluZyBTcGFjZSBiZXR3ZWVuIG15IGdlb21faGlzdG9ncmFtIGJhcnMtbm90IGJhcnBsb3QiKiBbc3RhY2tvdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDQ4ODkzMzQvYWRkaW5nLXNwYWNlLWJldHdlZW4tbXktZ2VvbS1oaXN0b2dyYW0tYmFycy1ub3QtYmFycGxvdCkgDQoNCls0XSAqIkluY3JlYXNpbmctbWF4LXByaW50LWRvZXMtbm90LWFsd2F5cy13b3JrLWluLXItYW5kLW5laXRoZXItZG9lcy1nbG9iYWwtb3B0aW9ucy1mIiogW3N0YWNrb3ZlcmZsb3ddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzUzNjIzNDMwL2luY3JlYXNpbmctbWF4LXByaW50LWRvZXMtbm90LWFsd2F5cy13b3JrLWluLXItYW5kLW5laXRoZXItZG9lcy1nbG9iYWwtb3B0aW9ucy1mKQ0KDQpbNV0gRGF2aWVzLCBUaWxtYW4gTS4sIDIwMTYgKlRoZSBCb29rIG9mIFI6IEEgRmlyc3QgQ291cnNlIGluIFByb2dyYW1taW5nIGFuZCBTdGF0aXN0aWNzKiAoQ2hhcHRlciAxNCwgcHAyOTQtMjk2KSwgTm8gU3RhcmNoIFByZXNzIA0KDQpbNl0gTGlwaWQgQ29udmVyc2lvbiBGYWN0b3JzLCBbTmF0aW9uYWwgQ2VudGVyIGZvciBCaW90ZWNobm9sb2d5IEluZm9ybWF0aW9uXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jvb2tzL05CSzgzNTA1LykNCg0KWzddIERyIEFuaWwgRG9sZ3VuLCAyMDIwIFtNQVRIMjM0OSBNb2R1bGUgTm90ZXNdKGh0dHA6Ly9yYXJlLXBob2VuaXgtMTYxNjEwLmFwcHNwb3QuY29tL3NlY3VyZWQvaW5kZXguaHRtbCkgIA0KDQpMb25nLCBKLkQuLCBUZWV0b3IsIFAuLCAyMDE5ICpSIENvb2tib29rKiwgTydSZWlsbHkgTWVkaWEsIEluYy4gIA0KDQpYaWUsIFkuLCBEZXJ2aWV1eCwgQy4sIFJpZWRlcmVyLCBFLiAyMDIwIFtSIE1hcmtkb3duIENvb2tib29rXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24tY29va2Jvb2svKQ0KDQoNCjxicj4NCjxicj4NCg==