Halle (Saale), July 2023.





# Add paths to the different data sets

# path for training data set
path <- "/Users/clemensscholle/Desktop/Master/Statistical_Machine_Learning/Project/titanic/train.csv"
# path for test data set
path1 <- "/Users/clemensscholle/Desktop/Master/Statistical_Machine_Learning/Project/titanic/test.csv"

# path for the corresponding dependent variables for the test data set 
path2 <- "/Users/clemensscholle/Desktop/Master/Statistical_Machine_Learning/Project/titanic/result.csv"



Data Dictionary

 ## Variable              ## Definition               ## Key

 # survived               # Survival                 # 0 = No, 1 = Yes
 # pclass                 # Ticket class             # 1, 2, 3 (e.g. 1 for first class.)
 # sex                    # Sex                      # 0 = Female, 1 = Male
 # Age                    # Age in years               
 # Sibsp                  # [#] of siblings / spouses abroad the Titanic (Brother, sister... & husband, wife)
 # parch                  # [#] of parents / children abroad the Titanic (mother, father, daugher, son...)
 # fare                   # Passenger fare
 # embarked               # Port of Embarkation      # C = Cherbourg, Q = Queenstown, S = Southampton

It is important to acknowledge that the variable called Passenger ID was not useful for our analysis. We, therefore, dropped this column. Next, we assigned a binary variable(s) for Survived variable, which is better illustration for showing if the person alive (1) or dead (0). When it comes to the graphical illustration we decided to choose “deepskyblue2" colour for the person who survived, "black" otherwise.

The Fare variable indicates the passenger fare the person paid. Being able to pay higher prices may be good illustration of the wealth of passenger.

There are three different classes on the ship, which were indicated as 1, 2 and 3. We created separate columns for each class in order to assign a binary variable for each class. Thus, we will be able to analyse them. For example, 1 if the passenger is in the first class, 0 otherwise. It is clear that the variable Pclass is also essential for our analysis. It might be a good illustration of the wealth of the passengers who were able to afford first class. Thus, it is highly relevant to the Fare variable. Reasonably, FirstClass passengers had more luxuries available to them. Most of first class was located in the middle hull and in the upper structures. There were two staircases connecting the different decks. SecondClass was distributed along the height in the middle of the ship with only one staircase connecting the different decks. Amenities there would be the same as for the first class of other ships of the time. The sole staircase may have impeded evacuation during the crash. ThirdClass was located on the deeper parts of the ship as well as parts directly on the bow and the rear of the ship. The location of the cabins would have obvious disadvantages during the crash.

In addition, we assigned a binary variable for determining the gender of the observed passengers. Therefore, Sex equals 1 if the passenger is a man and 0 otherwise. It is important to note that during evacuation women were given priority, whilst men might have higher chances of survival due to their physique. Children were prioritized during evacuation but they would also be less likely to survive due to their lesser strength compared to adults. Older people may exhibit the same impediment to a higher degree.

When it comes to the Age variable, it indicates the age of the passenger. Unfortunately, the Age variable was not available for all cases of this data set. We, therefore, decided to drop these missing cases in the cleaning process.

The variables SibSp and Parch were related to their family bonds. The SibSp variable showing how many siblings or spouses were with the person aboard the ship. We assumed that higher numbers may mean that more people assisted the person during evacuation or that they may have spend valuable time during evacuation looking for relatives. The Parch variable showing how many parents or children were with the person aboard the ship. In case of young children this variable may increase survival as parents will try to save their children. We also assumed that in case of parents this may lower chances of survival in case they were looking for their children or gave up seats on a lifeboat for them or it may improve survival chances as they would gain a seat on a lifeboat together with their children.

Finally, the Embarked variable indicates the port of embarkation of the passenger. There were three different ports of embarkation available, that was: Southhampton (S), Cherbourg (C), and Queenstown (Q). Similarly, we created separate columns for each port of embarkation of the passengers in order to assign a binary variable for each of them. Furhermore, we assumed that embarking earlier offers the passenger more time and opportunity to explore the ship, know their surroundings and get used to sea travel and possible sea sickness. This factor may be compounded by earlier passengers having a less populated ship to explore.

# Importing the dataset and preparing data to analysis

clean_data <- function(raw_data) {
  # Find NA values in Age and Embarked
  print(paste0("The number of missing values in Age variables: ", sum(is.na(raw_data$Age))))
  print(paste0("The number of missing values in Embarked variables: ", sum(is.na(raw_data$Embarked))))
  print(paste0("The number of missing values in Fare variables: ", sum(is.na(raw_data$Fare))))
  
  
  # Delete rows with NA values
  raw_data <- raw_data[complete.cases(raw_data$Age), ]
  raw_data <- raw_data[complete.cases(raw_data$Embarked), ]  
  raw_data <- raw_data[complete.cases(raw_data$Fare), ]  
  
  # Converting Sex variable as boolean type.
  raw_data$Sex <- ifelse(raw_data$Sex == "male", 1, 0)
  
  # Putting dummies for our variables which are Pclass and Embarked (thus, we will be able to analyse them in later process)
  # Basically what we do is creating a new column for all Embarked destionations Southhamptopn, Cherbourg and Queenstown with binary values. For example, in Southhampton 1 if "Embarked" is "S", 0 otherwise
  raw_data$Southhampton <- ifelse(raw_data$Embarked == "S", 1, 0)
  raw_data$Cherbourg <- ifelse(raw_data$Embarked == "C", 1, 0)
  raw_data$Queenstown <- ifelse(raw_data$Embarked == "Q", 1, 0)
  
  # Let us do the same process for the Pclass variable:
  raw_data$FirstClass <- ifelse(raw_data$Pclass == "1", 1, 0)
  raw_data$SecondClass <- ifelse(raw_data$Pclass == "2", 1, 0)
  raw_data$ThirdClass <- ifelse(raw_data$Pclass == "3", 1, 0) 
  
  # Creating new data frame for cleaned data
  clean_data <- data.frame(Name = raw_data$Name, Sex = raw_data$Sex, Age = raw_data$Age, Sibsp = raw_data$SibSp, Parch = raw_data$Parch, FirstClass = raw_data$FirstClass, SecondClass = raw_data$SecondClass, ThirdClass = raw_data$ThirdClass ,Southhampton = raw_data$Southhampton, Cherbourg = raw_data$Cherbourg, Queenstown = raw_data$Queenstown, Fare = raw_data$Fare, Survived = raw_data$Survived, Embarked = raw_data$Embarked, Pclass = raw_data$Pclass)
  
  return(clean_data)
  
}

raw_data <- read_csv(path)
Rows: 891 Columns: 12── Column specification ────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): Name, Sex, Ticket, Cabin, Embarked
dbl (7): PassengerId, Survived, Pclass, Age, SibSp, Parch, Fare
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data <- clean_data(raw_data)
[1] "The number of missing values in Age variables: 177"
[1] "The number of missing values in Embarked variables: 2"
[1] "The number of missing values in Fare variables: 0"
data

# Clean up
rm(list = c("raw_data"))


Basic calculations of the dataset

# Let's illustrate the basic calculations
total_passenger <- length(data$Sex)
total_female <- sum(data$Sex == 0)
total_male <- sum(data$Sex == 1)
total_survival <- sum(data$Survived == 1)
total_female_survival <- sum(data$Sex == 0 & data$Survived == 1)
total_male_survival <- sum(data$Sex == 1 & data$Survived == 1)

## Class calculations ##
total_pass_1_class <- sum(data$Pclass == 1)
total_pass_1_class_survived <- sum(data$Pclass == 1 & data$Survived == 1)
total_pass_2_class <- sum(data$Pclass == 2)
total_pass_2_class_survived <- sum(data$Pclass == 2 & data$Survived == 1)
total_pass_3_class <- sum(data$Pclass == 3)
total_pass_3_class_survived <- sum(data$Pclass == 3 & data$Survived == 1)
## Class calculation ends ##
ggplot(data, aes(x = factor(Survived), fill = factor(Sex))) +
  geom_bar() +
  labs(
    title = "Number of female and male passengers survived",
    caption = "Sex: 0 = Female, 1 = Male; \n Survived: 0 = No, 1 = Yes",
    x = "Number of passengers survived",
    y = "Count",
    fill = "Sex") +
  scale_fill_manual(values = c("0" = "orchid3", "1" = "skyblue3")) +
  theme(axis.line = element_line(color = "#000000"))


## General information ##
section1_gen_info_1 <- paste0("The total number of passengers: ", total_passenger)
section1_gen_info_2 <- paste0("The total number of female passengers: ", total_female)
section1_gen_info_3 <- paste0("The total number of male passengers: ", total_male)
section1_gen_info_4 <- paste0("The total number of passengers who survived: ", total_survival)
section1_gen_info_5 <- paste0("The total number of female passengers who survived: ", total_female_survival)
section1_gen_info_6 <- paste0("The total number of male passengers who survived: ", total_male_survival)
## General information ends ##

for (i in 1:6) {
  variable_name <- paste0("section1_gen_info_", i)
  variable_value <- get(variable_name)
  print(variable_value)
}
[1] "The total number of passengers: 712"
[1] "The total number of female passengers: 259"
[1] "The total number of male passengers: 453"
[1] "The total number of passengers who survived: 288"
[1] "The total number of female passengers who survived: 195"
[1] "The total number of male passengers who survived: 93"
# Plotting the age distribution though histogram. We also wanted to illustrate the estimate of the density.
ggplot(data = data, aes(x=Age)) +
  geom_histogram( aes(y=..density..)) +
  geom_density(fill="#f1b147", color="#f1b147", alpha=0.11) +
  labs(
    title = "Age distribution",
    x = "Age",
    y = "Density") +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"))


# Illustrating the number of age group and their fare. We also would like to illustrate survival rate as a color and parch as a size.
ggplot() +
  geom_point(data = data, aes(x = Age, y = Fare, color = factor(Survived), size = Parch)) +
  scale_color_manual(values = c("black", "deepskyblue3")) +
  labs(
    title = "Age & Fare & Familial relations & Survival",
    x = "Age",
    y = "Fare",
    color = "Survival") +
  geom_point() +
  theme(axis.line = element_line(color = "#000000"))



scatter3D(data$Age, data$Fare, data$Pclass, 
        theta=22, phi=29, 
        xlab="Age of passengers", ylab="Fare", zlab="Ticket classes of passengers",
        ticktype="detailed",
        pch = 20,
        cex.axis=0.45, cex.lab=0.95, expand=0.73, 
        colvar = data$Survived,
        col = c("black", "deepskyblue3"))


# Illustrating scatter plot of Survival vs. Age with color by Sex
ggplot(data = data, aes(x = Age, y = factor(Survived), color = factor(Sex))) +
  geom_jitter(width = 0.3, height = 0.3) +
  labs(
    title = "Distribution of survival by age and gender",
    caption = "Sex: 0 = Female, 1 = Male; \n Survived: 0 = No, 1 = Yes",
    x = "Age of passengers",
    y = "Surviving passengers",
    color = "Sex"
  ) +
  theme(axis.line = element_line(color = "#000000"))


ggplot(data = data, aes(x = "", y = Pclass, fill = factor(Pclass))) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  labs(
    title = "Number of passengers from each classes",
    fill = "Class"
  ) +
  theme_void()



## Class information ##
section1_gen_class_info_1 <- paste0("The total number of passengers from first class: ", total_pass_1_class)
section1_gen_class_info_2 <- paste0("The total number of passengers from first class who survived: ", total_pass_1_class_survived)
section1_gen_class_info_3 <- paste0("The total number of passengers from second class: ", total_pass_2_class)
section1_gen_class_info_4 <- paste0("The total number of passengers from second class who survived: ", total_pass_2_class_survived)
section1_gen_class_info_5 <- paste0("The total number of passengers from third class: ", total_pass_3_class)
section1_gen_class_info_6 <- paste0("The total number of passengers from third class who survived: ", total_pass_3_class_survived)
## Class information ends ##

for (i in 1:6) {
  variable_name <- paste0("section1_gen_class_info_", i)
  variable_value <- get(variable_name)
  print(variable_value)
}
[1] "The total number of passengers from first class: 184"
[1] "The total number of passengers from first class who survived: 120"
[1] "The total number of passengers from second class: 173"
[1] "The total number of passengers from second class who survived: 83"
[1] "The total number of passengers from third class: 355"
[1] "The total number of passengers from third class who survived: 85"
# Scatter plot of Sex vs. Pclass with color by Survived
ggplot(data = data, aes(x = factor(Pclass), y = factor(Survived), color = factor(Sex))) +
  geom_jitter(width = 0.3, height = 0.3) +
  labs(
    title = "Scatter plot of gender and class coloured by survival",
    caption = "Sex: 0 = Female, 1 = Male; \n Survived: 0 = No, 1 = Yes \n 1 = first class, 2 = second class, 3 = third class",
    x = "Ticket classes of passengers",
    y = "Number of passengers survived", 
    color = "Sex") +
  scale_color_manual(values = c("0" = "orchid3", "1" = "skyblue4")) +
  theme(axis.line = element_line(color = "#000000"))


# Clean up
rm(list = c("i", "section1_gen_class_info_1", "section1_gen_class_info_2", "section1_gen_class_info_3", "section1_gen_class_info_4", "section1_gen_class_info_5", "section1_gen_class_info_6", "section1_gen_info_1", "section1_gen_info_2", "section1_gen_info_3", "section1_gen_info_4", "section1_gen_info_5", "section1_gen_info_6", "total_female", "total_female_survival", "total_male", "total_male_survival", "total_pass_1_class", "total_pass_2_class", "total_pass_3_class", "total_pass_1_class_survived", "total_pass_2_class_survived", "total_pass_3_class_survived", "total_passenger", "total_survival", "variable_name", "variable_value"))



Clustering

# We decided to work on two columns that are Amount of Fare from passengers and Age of passengers. This is because all the other columns contain small numeric variables such as boolean 1 or 2, TRUE or FALSE or 1,2 and 3 for class.
set.seed(1234)
dt_kmeans <- data[, c("Age", "Fare")]

# First of all, let's start with 2 k-means (two centers) for now.
model_kmeans <- kmeans(dt_kmeans, centers = 2)

clustering_1_info1 <- paste0("The total number of observations for first cluster is ", model_kmeans$size[1], ".")
clustering_1_info2 <- paste0("The total number of observations for second cluster is ", model_kmeans$size[2], ".")
print(c(clustering_1_info1, clustering_1_info2 ))
[1] "The total number of observations for first cluster is 46."  
[2] "The total number of observations for second cluster is 666."
# Here is the center of our clusters:
print(model_kmeans$centers)
       Age      Fare
1 31.60696 192.59855
2 29.50638  23.65218
# Let us visualise it by showing the centers as well
ggplot(data = dt_kmeans, aes(x = Age, y = Fare, color = as.factor(model_kmeans$cluster))) +
  geom_point() +
  # Let us illustrate the centers of clusters
  geom_point(data = data.frame(model_kmeans$centers),
             shape = "circle filled",
             size = 7, 
             color = "#000000",
             fill = c("deepskyblue2", "gold2"),
             alpha = 0.75) +
  guides(color = "none") +
  scale_color_manual(values = c("deepskyblue2","gold2")) +
  labs(
    x = "Age of passengers",
    y = "Passenger fare") +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"))



# Let us standardise inputs
# We have seen the standardization of variables before: Every variable is centered on its mean and then scaled by its standard deviation.
dt_kmeans_scaled <- data.table(scale(dt_kmeans))

model_kmeans_scaled <- kmeans(dt_kmeans_scaled, centers = 2)

ggplot(data = dt_kmeans_scaled, aes(x = Age, y = Fare, color = as.factor(model_kmeans_scaled$cluster))) +
  geom_point() +
  geom_point(data = data.frame(model_kmeans_scaled$centers),
             shape = "circle filled",
             size = 7, 
             color = "#000000",
             fill = c("deepskyblue2", "gold2"),
             alpha = 0.75) +
  guides(color="none") +
  scale_color_manual(values = c("deepskyblue2", "gold2")) +
  labs(
    x="Age of passengers",
    y= "Passenger fare") +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"))


We find two clusters mainly depending on the age variable.

Elbow plot

We also would like to illustrate elbow plot to find the optimal k. To do so, we will be needing the total within sum of squares (TWSS) of our scaled model. TWSS also means that the squared distance of all data points to their cluster centers. First of all, let us create a loop function and we will loop through different k values from 1 to 20 in this case. Next, we will illustrate an elbow plot to investigate the sensitivity of TWSS to the number of clusters k.

k <- 25
elbow <- data.table("k" = 1:k, "WSS" = NA)

# Let us create our loop function
for (i in 1:k) {
  elbow$k[i] <- i
  # Next, we are setting the new center of our cluster
  elbow$WSS[i] <- kmeans(dt_kmeans_scaled, centers = i, nstart=50)$tot.withinss
}
Warning: did not converge in 10 iterations
# Illustrating our findings with the help of line chart
ggplot(data = elbow, aes(x = k, y = WSS)) +
  geom_line() +
  labs(
    title = "Elbow plot",
    x = "k",
    y = "TWSS") +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"))


NbClust(data=dt_kmeans_scaled, method="kmeans")
*** : The Hubert index is a graphical method of determining the number of clusters.
                In the plot of Hubert index, we seek a significant knee that corresponds to a 
                significant increase of the value of the measure i.e the significant peak in Hubert
                index second differences plot. 
 

*** : The D index is a graphical method of determining the number of clusters. 
                In the plot of D index, we seek a significant knee (the significant peak in Dindex
                second differences plot) that corresponds to a significant increase of the value of
                the measure. 
 
******************************************************************* 
* Among all indices:                                                
* 5 proposed 2 as the best number of clusters 
* 4 proposed 3 as the best number of clusters 
* 1 proposed 4 as the best number of clusters 
* 1 proposed 6 as the best number of clusters 
* 1 proposed 10 as the best number of clusters 
* 6 proposed 12 as the best number of clusters 
* 1 proposed 13 as the best number of clusters 
* 4 proposed 15 as the best number of clusters 

                   ***** Conclusion *****                            
 
* According to the majority rule, the best number of clusters is  12 
 
 
******************************************************************* 
$All.index
         KL       CH Hartigan     CCC    Scott  Marriot     TrCovW   TraceW
2    2.2054 372.2797 483.7325 -5.9295  729.605 719419.3 380094.971 932.8642
3    3.1755 554.0442 201.6686 -7.1991 1334.611 692042.4  77488.085 554.8426
4    0.7899 540.8836 137.7367 -7.7737 1729.603 706446.6  43973.842 431.9720
5    0.3825 518.2829 252.7282 -8.8798 2064.002 690125.1  37237.721 361.6210
6    4.3812 612.5190  93.7143 -4.4907 2393.777 625373.5  16891.388 266.3942
7    1.9768 592.9652 112.7439 -5.3835 2600.348 636842.7  15356.183 235.1769
8    0.2219 604.7835  38.0767 -4.8875 2818.839 611990.3  11175.844 202.7526
9    0.8115 561.7677  21.8105 -6.8808 2926.707 665662.2  10806.627 192.3492
10   4.5647 516.5289  82.5888 -9.1249 2977.649 765061.0  10364.909 186.5612
11   0.0511 527.0747 413.5455 -8.6338 3121.229 756663.2   7496.572 166.9230
12  70.0097 798.2867  63.3209  2.4591 3773.675 360173.1   2800.028 104.9872
13   0.1160 802.0861  13.3817  2.5410 3851.963 378688.5   1814.260  96.2781
14   0.6323 754.5096 119.1954  0.8346 3912.351 403475.5   2160.602  94.4695
15 110.4838 827.5847  47.0911  3.2983 4130.540 340922.9   1703.729  80.6903
   Friedman   Rubin Cindex     DB Silhouette   Duda  Pseudot2   Beale Ratkowsky
2    1.4887  1.5243 0.1050 1.3807     0.4615 1.4540 -211.6973 -0.3116    0.3954
3    3.1304  2.5629 0.1238 0.9002     0.4603 1.3762 -115.0938 -0.2722    0.4508
4    4.9496  3.2919 0.0993 0.9209     0.4266 1.3420  -74.6670 -0.2542    0.4167
5    7.3138  3.9323 0.0891 1.0011     0.3872 2.9908 -161.7503 -0.6632    0.3853
6    8.9009  5.3380 0.0879 0.8025     0.4369 2.7302 -169.2059 -0.6309    0.3680
7   10.8642  6.0465 0.0763 0.8895     0.4247 2.0025 -113.1438 -0.4982    0.3452
8   13.0753  7.0135 0.0697 0.8262     0.4441 2.5076 -117.2349 -0.5980    0.3273
9   14.6396  7.3928 0.0656 0.8731     0.3919 1.9970 -143.7807 -0.4958    0.3099
10  15.3350  7.6222 0.0622 0.9273     0.3894 2.6234  -68.0691 -0.6110    0.2947
11  16.9784  8.5189 0.0630 0.9180     0.3972 1.7144 -105.8414 -0.4137    0.2832
12  27.8408 13.5445 0.0940 0.8708     0.4085 3.7345 -133.9980 -0.7264    0.2778
13  28.5170 14.7697 0.1006 0.8715     0.4153 2.8057  -34.7535 -0.6241    0.2678
14  30.6223 15.0525 0.0887 0.8572     0.4065 1.3694  -18.6112 -0.2656    0.2582
15  35.8629 17.6229 0.0867 0.8185     0.4165 1.9926  -78.2067 -0.4897    0.2508
       Ball Ptbiserial    Frey McClain   Dunn Hubert SDindex Dindex   SDbw
2  466.4321     0.4615  0.1400  0.3579 0.0026 0.0010 11.5350 0.8343 1.7601
3  184.9475     0.5148  0.5897  0.4115 0.0095 0.0014 12.9759 0.7088 1.4853
4  107.9930     0.5316  1.8006  0.5287 0.0096 0.0016 10.8509 0.6034 1.1531
5   72.3242     0.4595  0.3418  0.8339 0.0050 0.0017 10.2125 0.5320 0.9741
6   44.3990     0.4600  0.7410  0.8683 0.0059 0.0018  8.9781 0.4534 0.7840
7   33.5967     0.4367  0.2640  0.9762 0.0118 0.0018  8.8506 0.4119 0.6948
8   25.3441     0.4360  2.9541  0.9639 0.0118 0.0019  8.1356 0.3791 0.5993
9   21.3721     0.3774  0.0339  1.3135 0.0118 0.0019  9.4129 0.3542 0.5528
10  18.6561     0.3800  0.1934  1.2715 0.0059 0.0019  8.6732 0.3447 0.5089
11  15.1748     0.3792  1.1639  1.2443 0.0064 0.0019  9.1083 0.3332 0.5383
12   8.7489     0.3692 -0.5533  1.3041 0.0196 0.0020  8.8987 0.2982 0.2490
13   7.4060     0.3806  1.5236  1.1929 0.0110 0.0020  8.0162 0.2875 0.2119
14   6.7478     0.3771 -0.6041  1.2135 0.0098 0.0020 11.6938 0.2834 0.2225
15   5.3794     0.3826  1.4004  1.1620 0.0098 0.0020 11.5027 0.2713 0.1787

$All.CriticalValues
   CritValue_Duda CritValue_PseudoT2 Fvalue_Beale
2          0.5699           511.7558            1
3          0.5221           385.4155            1
4          0.5572           232.8363            1
5          0.5318           213.9526            1
6          0.5179           248.5450            1
7          0.5093           217.7170            1
8          0.5022           193.3021            1
9          0.4775           315.1968            1
10         0.4058           161.0794            1
11         0.4739           282.0160            1
12         0.4618           213.2379            1
13         0.2585           154.9208            1
14         0.3779           113.5741            1
15         0.3631           275.3848            1

$Best.nc
                      KL       CH Hartigan     CCC    Scott  Marriot   TrCovW
Number_clusters  15.0000  15.0000  12.0000 15.0000  12.0000     12.0      3.0
Value_Index     110.4838 827.5847 350.2246  3.2983 652.4456 415005.5 302606.9
                  TraceW Friedman   Rubin  Cindex     DB Silhouette  Duda  PseudoT2
Number_clusters   3.0000  12.0000 12.0000 10.0000 6.0000     2.0000 2.000    2.0000
Value_Index     255.1511  10.8624 -3.8004  0.0622 0.8025     0.4615 1.454 -211.6973
                  Beale Ratkowsky     Ball PtBiserial Frey McClain    Dunn Hubert
Number_clusters  2.0000    3.0000   3.0000     4.0000    1  2.0000 12.0000      0
Value_Index     -0.3116    0.4508 281.4846     0.5316   NA  0.3579  0.0196      0
                SDindex Dindex    SDbw
Number_clusters 13.0000      0 15.0000
Value_Index      8.0162      0  0.1787

$Best.partition
  [1]  1  4 12  4 10 11  3 12  5  3  7  1  2  1  7  3 10 10 10  1 12  3 10  8  2  7
 [27]  4  2  1  1  1  2 12  3  1  1  5  1 11 12  7  1 12  3  5  1 11  3 12  1  1 12
 [53] 10  5  4 12  4 12  3 10  1 12  4  1 10  5  8 12 12  1 11 12  7  7  4 10 10 12
 [79]  4 10 10 12  1 10  2  1  1  1  1  1  7 12  8  3  4 10 10 11  5 12  2 10  1  2
[105] 12 12 12  1  4  1  4  1 12  1  1  1 12  5 10  2  2  4  7  2 11  1 10  2  2 12
[131]  1  3  5  2  4  7  3  3  1  7  1  2 10 10  5  3  3  2  2 10 10  1  1  3  2 11
[157]  2 12 12 10  2  1  3 10 12  1  2 12 10  1 10  9 12  2  4 10  1 12  2  4  1  1
[183]  1  1  4 12  7  3 12  2  5  1 10 12  1 10 11 12 12  4  7 12  7 10  2 12  4  6
[209]  2  3 11  2 10  5 12 11  9 12  2 10 11  2  3 10  7 12  1  1 10 10  1  2  1  4
[235]  4 10 12 12 12  9  8  1  9  9 10  4  4  8 12 12  2 12 12  7  9  9  1 12 10 12
[261]  9  7 10 10  5  2  9  1  4  9  2  2  3  8 12 12 10 12  2  3  2 12  1 12 12  4
[287] 10  2 12  2 10 10 11  4  4  1  1  9  3  1  8  1  1  8  3 10  4  4  3 10  1  9
[313]  1 12  9 12  1 10  2 12 12 10 12  1 12  1 10  2  3  1  4  2 10  1 10  5  1 12
[339] 12  1 12  1 10 12  2  1 11  9  1 12  8 10  2  1 12 12  5  5 10  3  2 10 10 11
[365] 12  7  2  2 10  2  2 10  7  3 10 10 12  1 10 12  1  3  5  2  7  4  4  7 10  5
[391]  1  7  7  1 11  9 12  1  1 10  4  9 10 12  4 12 10 11 12  2 10 10 10  4  1  2
[417]  2  2 10 12  3  1 10  3  2  9  4  4  5  5 10 11  7  1 10  5  9 12  1  1  7  2
[443]  4 10  2 12 12  1 12 10  7 11 10  1  1 10  2 10 12  9  7 10  4  2 11  1 10 11
[469]  2 10 10  2 11 12  2 10 10 10 12 12  9  2 10  4 10 12  3 12 12  2  1  1  1  7
[495]  7  4 12  7  2 10  5 12 10 10  2  1  4  3  3 11  1  7 12  1  1  1  4 10 12 11
[521] 11  2  2 10  1  4 12  2  2  4  7 10  1 12  1  2  6  4  1  5  7 12  5  1  1  8
[547]  4  3 12  7  2  2 11  2  8 10  1 12 12  2  2  2  9  4  2 12  2  1  8 12 10  3
[573]  1 10  2  4  1 10 12 12  8  5 12 12 12  2  6  4  8 12 10  7  1 10  5 10  3  3
[599] 10 12 11  3 12  1 10  4  2  1  9  1 11 10 10 12  2  7  7  1  3  8  1  5 12 12
[625] 12  1  5  3 11  5 12 10  2 10 10 10 10 10  9  3 12 10 10  1 10  4 12  2 10  3
[651] 10 12 10  2  5 11 12 10 12  3  3  1  3 12  1  4  1  4  1  1 10 10  1  2 10 12
[677]  3  7  5  5  2  1  9  2 12  2  1  2 12  2 12  4  3 12  2 10  2 12  1  1  1 11
[703] 12 10  1 12 12  2 12  1 12 10

# According to the majority rule, the best number of clusters is  3 

It is clear that 3 centers would make more suitable for our work, so let us replicate our clustering work with 3 centers.

dt_kmeans_scaled <- data.table(scale(dt_kmeans))

model_kmeans_scaled <- kmeans(dt_kmeans_scaled, centers = 3)

ggplot(data = dt_kmeans_scaled, aes(x = Age, y = Fare, color = as.factor(model_kmeans_scaled$cluster))) +
  geom_point() +
  geom_point(data = data.frame(model_kmeans_scaled$centers),
             shape = "circle filled",
             size = 7, 
             color = "#000000",
             fill = c("deepskyblue2", "gold2", "forestgreen"),
             alpha = 0.75) +
  guides(color="none") +
  scale_color_manual(values = c("deepskyblue2", "gold2", "forestgreen")) +
  labs(
    x="Age of passengers",
    y= "Passenger fare") +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"))


# Clean up
rm(list = c("k", "i", "clustering_1_info1", "clustering_1_info2", "dt_kmeans", "dt_kmeans_scaled", "elbow", "model_kmeans", "model_kmeans_scaled"))



Logistic regression

# Estimate the logistic model
model_glm <- glm(formula = Survived ~ FirstClass + SecondClass + Sex + Age + Sibsp + Parch + Fare + Cherbourg + Queenstown, 
                 data = data, family = binomial(link = "logit"))

# View summary statistics
print(summary(model_glm))

Call:
glm(formula = Survived ~ FirstClass + SecondClass + Sex + Age + 
    Sibsp + Parch + Fare + Cherbourg + Queenstown, family = binomial(link = "logit"), 
    data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.7220  -0.6460  -0.3796   0.6329   2.4461  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept)  1.634864   0.325362   5.025 5.04e-07 ***
FirstClass   2.395220   0.343356   6.976 3.04e-12 ***
SecondClass  1.205583   0.249757   4.827 1.39e-06 ***
Sex         -2.637859   0.223006 -11.829  < 2e-16 ***
Age         -0.043308   0.008322  -5.204 1.95e-07 ***
Sibsp       -0.362925   0.129290  -2.807    0.005 ** 
Parch       -0.060365   0.123944  -0.487    0.626    
Fare         0.001451   0.002595   0.559    0.576    
Cherbourg    0.402848   0.274556   1.467    0.142    
Queenstown  -0.420532   0.556371  -0.756    0.450    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 960.90  on 711  degrees of freedom
Residual deviance: 632.34  on 702  degrees of freedom
AIC: 652.34

Number of Fisher Scoring iterations: 5
with(model_glm, pchisq(null.deviance - deviance, df.null - df.residual, lower.tail = FALSE))
[1] 2.248584e-65

Looking at the result of the model, it is clear that being in first class, being female and having a younger age were statistically significant predictors of survival, while the number of siblings and other variables did not show significant influence on survival in the given data set.


Tree

### Base tree 
# Build a decision tree model using rpart
model_tree <- rpart(data = data, formula = Survived ~ Pclass + Sex + Age + Sibsp + Parch + Fare + Embarked, method = "class")

# Plot the decision tree model using prp, with extra details (extra = 2)
prp(model_tree, extra = 2, main = "Base tree for model")


# Print an plot data on tree
printcp(model_tree)

Classification tree:
rpart(formula = Survived ~ Pclass + Sex + Age + Sibsp + Parch + 
    Fare + Embarked, data = data, method = "class")

Variables actually used in tree construction:
[1] Age    Fare   Parch  Pclass Sex    Sibsp 

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.454861      0   1.00000 1.00000 0.045472
2 0.029514      1   0.54514 0.54514 0.038412
3 0.027778      3   0.48611 0.55208 0.038586
4 0.024306      4   0.45833 0.52778 0.037965
5 0.010417      5   0.43403 0.47569 0.036523
6 0.010000      9   0.39236 0.49306 0.037021
plotcp(model_tree)

# The tree shows that the first deciding factor seems to be gender. Afterwards mainly class and age show seem prominent deciding factors for branches though interestingly enough fare seems to play an important role as well.

# Determine the minimum complexity parameter (cp) for pruning
#cp parameter: captures model complexity
min_cp <- model_tree$cptable[which.min(model_tree$cptable[, "xerror"]), "CP"]

# Prune the decision tree model using the minimum cp
model_tree_2 <- prune(model_tree, cp = min_cp)

# Plot the pruned decision tree model using prp, with extra details (extra = 2)
prp(model_tree_2, extra = 2, main = "Pruned base tree with less complexity")


# The branch determined by being male and over the age of 6 shows the biggest group and the most wrong guesses of the model.

# Calculate the mean accuracy of the model predictions
mean(predict(model_tree, data, type = "class")==data$Survived)
[1] 0.8412921
mean(predict(model_tree_2, data, type = "class")==data$Survived)
[1] 0.8244382
# Between the accuracy for the pruned and not-pruned model there is only a 2% difference so we will choose the pruned model as representative as it is less complex
model_trees <- model_tree_2

# Print an plot data on tree
printcp(model_tree_2)

Classification tree:
rpart(formula = Survived ~ Pclass + Sex + Age + Sibsp + Parch + 
    Fare + Embarked, data = data, method = "class")

Variables actually used in tree construction:
[1] Age    Fare   Pclass Sex    Sibsp 

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.454861      0   1.00000 1.00000 0.045472
2 0.029514      1   0.54514 0.54514 0.038412
3 0.027778      3   0.48611 0.55208 0.038586
4 0.024306      4   0.45833 0.52778 0.037965
5 0.010417      5   0.43403 0.47569 0.036523
plotcp(model_tree_2)


### Visualization for interesting variable combinations
# Convert 'Survived' variable to a factor
tree_data <- data
tree_data$Survived <- as.factor(tree_data$Survived) 

# Build a decision tree model using rpart
model_tree_4 <- rpart(data = tree_data, formula = Survived ~ Age + Sibsp, method = "class")

# Plot the decision tree model using prp
prp(model_tree_4, extra = 2, main = "Tree for Age and Sibling/Spouse")


# Determine the minimum complexity parameter (cp) for pruning
min_cp <- model_tree_4$cptable[which.min(model_tree_4$cptable[, "xerror"]), "CP"]

# Prune the decision tree model using the minimum cp
model_tree_5 <- prune(model_tree_4, cp = min_cp)

# Plot the pruned decision tree model using prp
prp(model_tree_5, extra = 2, main = "Pruned tree for Age and Sibling/Spouse")


# Print an plot data on tree
printcp(model_tree_5)

Classification tree:
rpart(formula = Survived ~ Age + Sibsp, data = tree_data, method = "class")

Variables actually used in tree construction:
[1] Age   Sibsp

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.065972      0   1.00000 1.00000 0.045472
2 0.034722      1   0.93403 0.94792 0.045049
3 0.010000      2   0.89931 0.92014 0.044786
plotcp(model_tree_5)




# Create a scatter plot with decision tree boundaries for Age and Sibsp variables
plot1 <- ggplot(data = tree_data, aes(x = Sibsp, y = Age)) +
  geom_point(aes(color = Survived)) +
  geom_parttree(data = model_tree_5, aes(fill = Survived), alpha = 0.1) +
  scale_color_manual(values = c("black", "deepskyblue2")) +
  scale_fill_manual(values = c("black", "deepskyblue2")) +
  guides(color = "none") +
  labs(
    x = "Sibsp",
    y = "Age",
    color = "Survived", 
    title = "Scatter plot of age and familial relations showing survival and tree estimation of survival"
  ) +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"), legend.position = "none")

# Build another decision tree model using rpart
model_tree_4 <- rpart(data = tree_data, formula = Survived ~ Age + Parch, method = "class")

# Plot the decision tree model using prp
prp(model_tree_4, extra = 2, main = "Tree for Age and Parent/Child")


# Determine the minimum complexity parameter (cp) for pruning
min_cp <- model_tree_4$cptable[which.min(model_tree_4$cptable[, "xerror"]), "CP"]

# Prune the decision tree model using the minimum cp
model_tree_6 <- prune(model_tree_4, cp = min_cp)

# Plot the pruned decision tree model using prp
prp(model_tree_6, extra = 2, main = "Pruned tree for Age and Parent/Child")


# Print an plot data on tree
printcp(model_tree_6)

Classification tree:
rpart(formula = Survived ~ Age + Parch, data = tree_data, method = "class")

Variables actually used in tree construction:
[1] Age   Parch

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.052083      0   1.00000 1.00000 0.045472
2 0.027778      1   0.94792 0.97917 0.045313
3 0.017361      2   0.92014 0.98611 0.045368
4 0.011574      3   0.90278 0.98958 0.045395
5 0.010000      6   0.86806 0.96528 0.045200
plotcp(model_tree_6)



# Create a scatter plot with decision tree boundaries for Age and Parch variables
plot2 <- ggplot(data = tree_data, aes(x = Parch, y = Age)) +
  geom_point(aes(color = Survived)) +
  geom_parttree(data = model_tree_6, aes(fill = Survived), alpha = 0.1) +
  scale_color_manual(values = c("black", "deepskyblue2")) +
  scale_fill_manual(values = c("black", "deepskyblue2")) +
  guides(color = "none") +
  labs(
    x = "Parch",
    y = "Age",
    color = "Survived"
  ) +
  theme_minimal() +
  theme(
    axis.line = element_line(color = "#000000"),
    legend.position = "bottom",
    legend.box = "horizontal"
  )

# Combine the two plots
plot1 + plot2


# Visualizing both age and fare as the only variables containing throughout distributed values.
# Build a decision tree model using rpart
model_tree_4 <- rpart(data = tree_data, formula = Survived ~ Age + Fare, method = "class")

# Plot the decision tree model using prp
prp(model_tree_4, extra = 2, main = "Tree for Age and Fare")


# Determine the minimum complexity parameter (cp) for pruning
min_cp <- model_tree_4$cptable[which.min(model_tree_4$cptable[, "xerror"]), "CP"]

# Prune the decision tree model using the minimum cp
model_tree_7 <- prune(model_tree_4, cp = min_cp)

# Plot the pruned decision tree model using prp
prp(model_tree_7, extra = 2, main = "pruned tree for Age and Fare")


# Print an plot data on tree
printcp(model_tree_7)

Classification tree:
rpart(formula = Survived ~ Age + Fare, data = tree_data, method = "class")

Variables actually used in tree construction:
[1] Age  Fare

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.201389      0   1.00000 1.00000 0.045472
2 0.031250      1   0.79861 0.81597 0.043567
3 0.012153      3   0.73611 0.77083 0.042918
plotcp(model_tree_7)



# Create a scatter plot with decision tree boundaries for Age and fare variables
ggplot(data = tree_data, aes(x = Fare, y = Age)) +
  geom_point(aes(color = Survived)) +
  geom_parttree(data = model_tree_7, aes(fill = Survived), alpha = 0.1) +
  scale_color_manual(values = c("black", "deepskyblue2")) +
  scale_fill_manual(values = c("black", "deepskyblue2")) +
  guides(color = "none") +
  labs(
    x = "Fare",
    y = "Age",
    color = "Survived",
    title = "Scatter plot of age and fare showing survival and tree estimation of survival"
  ) +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"), legend.position = "none")


# This visualization though looking quite nice does not seem to indicate any notable impact of fare in my opinion.

# Clean up
rm(list = c("min_cp", "plot1", "plot2", "model_tree", "model_tree_2", "model_tree_4", "model_tree_5", "model_tree_6", "model_tree_7", "tree_data"))

It is clear that there is only small difference between Pruned base tree and complex base tree. Between the accuracy for the pruned and not-pruned model there is only a 2% difference so we will choose the pruned model as representative as it is less complex. This is because, we believe that this will save computational memory. Moreover, the higher complexity tree does only improve on a less relevant branch.

The more important branch determined by being male and over the age of 6 shows the biggest group and the most wrong guesses of the model.

Investigating hyperparameters for the tree model

### Investigating hyperparameters
# Define the values for minimum observations, maximum depth, and complexity parameter (cp)
min_obs <- seq(from = 5, to = 50, by = 5)
max_depth <- c(1, 5, 10, 15)
cp_val <- c(0.001, 0.01, 0.05, 0.1)

# Create an empty data frame to store the results
results <- data.frame()

# Iterate over the combinations of min_obs, max_depth, and cp_val
for (i in 1:length(min_obs)) {
  for (j in 1:length(max_depth)) {
    for (k in 1:length(cp_val)) {
      # Build a decision tree model with the specified parameters
      model <- rpart(data = data, formula = Survived ~ Pclass + Sex + Age + Sibsp + Parch + Fare + Embarked, 
                     method = "class",
                     cp = cp_val[k],
                     maxdepth = max_depth[j],
                     minbucket = min_obs[i])
      
      # Calculate the accuracy of the model predictions
      acc <- mean(predict(model, data, type = "class") == data$Survived)
      
      # Append the accuracy and parameter values to the results data frame
      results <- rbind(results, c(acc, min_obs[i], max_depth[j], cp_val[k]))
    }
  }
}

# Set column names for the results data frame
colnames(results) <- c("accuracy", "min_obs", "max_depth", "cp")

# Create a 3D scatter plot using scatter3D
plot3D::scatter3D(x = results$min_obs, y = results$max_depth, z = results$cp,
                  theta = 30, phi = 10,
                  xlab = "Min Obs", ylab = "Max Depth", zlab = "CP",
                  ticktype = "detailed",
                  pch = 19,
                  cex.axis = 0.75, cex.lab = 0.75, expand = 0.75,
                  colvar = results$accuracy, col = viridis(100))



# Select the optimal parameter combination with the highest accuracy
opt_params <- results[which.max(results$accuracy),]

# Build a decision tree model using the optimal parameters
model_tree_3 <- rpart(data=data, formula = Survived ~ Pclass + Sex + Age + Sibsp + Parch + Fare + Embarked, 
                      method = "class",
                      cp = opt_params$cp, 
                      maxdepth = opt_params$max_depth, 
                      minbucket = opt_params$min_obs)

# Calculate the mean accuracy of the model predictions
mean(predict(model_tree_3, data, type = "class") == data$Survived)
[1] 0.875
# Plot the pruned decision tree with additional details
prp(model_tree_3, extra = 2, main = "Tree with optimal parameters")


# Print an plot data on tree
printcp(model_tree_3)

Classification tree:
rpart(formula = Survived ~ Pclass + Sex + Age + Sibsp + Parch + 
    Fare + Embarked, data = data, method = "class", cp = opt_params$cp, 
    maxdepth = opt_params$max_depth, minbucket = opt_params$min_obs)

Variables actually used in tree construction:
[1] Age    Fare   Parch  Pclass Sex    Sibsp 

Root node error: 288/712 = 0.40449

n= 712 

          CP nsplit rel error  xerror     xstd
1  0.4548611      0   1.00000 1.00000 0.045472
2  0.0295139      1   0.54514 0.54514 0.038412
3  0.0277778      3   0.48611 0.54861 0.038499
4  0.0243056      4   0.45833 0.52083 0.037782
5  0.0104167      5   0.43403 0.48264 0.036724
6  0.0092593      6   0.42361 0.49653 0.037119
7  0.0078125      9   0.39583 0.51042 0.037502
8  0.0069444     13   0.36458 0.50347 0.037312
9  0.0052083     15   0.35069 0.50347 0.037312
10 0.0034722     21   0.31944 0.50000 0.037215
11 0.0017361     23   0.31250 0.49653 0.037119
12 0.0010000     25   0.30903 0.49653 0.037119
plotcp(model_tree_3)


# Clean up
rm(list = c("acc", "cp_val", "i", "j", "k", "max_depth", "min_obs", "results", "opt_params", "model_tree_3", "model"))

The uncertain branch discussed before (male, age above 6) can reasonably be further distinguished by class and maybe age. Afterwards the classification seems to be mostly dependent on fare with decisions along the branch leading to no real insights. The optimal model shows only marginal improvement compared to accuracy of earlier models.



Neural Networks

set.seed(12345)   # Set a seed for reproducibility

# Data setup
X <- as.matrix(data[, c("FirstClass", "SecondClass", "ThirdClass", "Sex", "Age", "Sibsp", "Parch", "Fare", "Southhampton", "Cherbourg", "Queenstown")])
y <- data$Survived

## Model as in lecture

normalize <- layer_normalization()   # Create a normalization layer
normalize %>% adapt(X)   # Adapt the normalization layer to the data

print(normalize$mean)   # Print the mean values used for normalization
tf.Tensor(
[[ 0.25842693  0.24297753  0.49859545  0.6362359  29.64209     0.51404494
   0.4325842  34.567238    0.77808994  0.18258427  0.03932584]], shape=(1, 11), dtype=float32)
model_neural <- keras_model_sequential()   # Create a sequential model

model_neural %>%
  normalize() %>%   # Apply normalization # L1

  layer_dense(name = "HiddenLayer_1", units = 16, activation = 'relu') %>%   # Add a hidden layer with 16 units and ReLU activation
  layer_dense(name = "HiddenLayer_2", units = 16, activation = 'relu') %>%   # Add another hidden layer with 16 units and ReLU activation
  layer_dense(name = "HiddenLayer_3", units = 16, activation = 'relu') %>%   # Add a third hidden layer with 16 units and ReLU activation

   layer_dense(name = "OutputLayer", units = 1)   # Add an output layer with 1 unit

summary(model_neural)   # Print the summary of the model
Model: "sequential_82"
____________________________________________________________________________________
 Layer (type)                   Output Shape                  Param #    Trainable  
====================================================================================
 normalization_82 (Normalizatio  (None, 11)                   23         Y          
 n)                                                                                 
 HiddenLayer_1 (Dense)          (None, 16)                    192        Y          
 HiddenLayer_2 (Dense)          (None, 16)                    272        Y          
 HiddenLayer_3 (Dense)          (None, 16)                    272        Y          
 OutputLayer (Dense)            (None, 1)                     17         Y          
====================================================================================
Total params: 776
Trainable params: 753
Non-trainable params: 23
____________________________________________________________________________________
#plot_model(model_base)

model_neural %>% compile(
  loss = "MSE",
  optimizer = optimizer_adam(learning_rate = 0.001),
  metrics = "Accuracy"
)   # Compile the model with mean squared error loss, Adam optimizer, and accuracy as a metric

model_neural %>% fit(
  x = X,
  y = y,
  epochs = 500,
  verbose = 0
)   # Train the model on the data for 500 epochs

# model_neural$layers[[2]]$kernel   # Print the kernel (weights) of the second layer
# model_neural$layers[[3]]$kernel   # Print the kernel (weights) of the third layer
# model_neural$layers[[4]]$kernel   # Print the kernel (weights) of the fourth layer


model_neural %>% evaluate(X, y, verbose = 0)   # Evaluate the model on the data
      loss   Accuracy 
0.07786684 0.89747190 
rm(list = c("X", "y"))

Investigating hyperparameters for the neural networks

### Investigating hyperparameters
# Define the values
learning_rate <- c(0.0001, 0.001, 0.01, 0.1)
width <- c(4, 16, 24)
length <- c(1, 3, 10)

# Data setup
X <- as.matrix(data[, c("FirstClass", "SecondClass", "ThirdClass", "Sex", "Age", "Sibsp", "Parch", "Fare", "Southhampton", "Cherbourg", "Queenstown")])
y <- data$Survived

# Create an empty data frame to store the results
results <- data.frame()

# Iterate over the combinations
for (i in 1:length(learning_rate)) {
  for (j in 1:length(width)) {
    for (k in 1:length(length)) {

      #print(i)
      #print(j)
      #print(k)

      if (length[k] == 1) {

        normalize_i <- layer_normalization()   # Create a normalization layer
        normalize_i %>% adapt(X)   # Adapt the normalization layer to the data

        model_neural_i <- keras_model_sequential()   # Create a sequential model

        model_neural_i %>%
          normalize_i() %>%   # Apply normalization

          layer_dense(name = "HiddenLayer_1", units = width[j], activation = 'relu') %>%

           layer_dense(name = "OutputLayer", units = 1)   # Add output layer with 1 unit

        model_neural_i %>% compile(loss="MSE", optimizer= optimizer_adam(learning_rate = learning_rate[i]), metrics="Accuracy")

        model_neural_i %>% fit(x = X, y = y, epochs = 250, verbose = 0)

      } else if (length[k] == 3) {

        normalize_i <- layer_normalization()   # Create a normalization layer
        normalize_i %>% adapt(X)   # Adapt the normalization layer to the data

        model_neural_i <- keras_model_sequential()   # Create a sequential model

        model_neural_i %>%
          normalize_i() %>%   # Apply normalization

          layer_dense(name = "HiddenLayer_1", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_2", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_3", units = width[j], activation = 'relu') %>%

           layer_dense(name = "OutputLayer", units = 1)   # Add output layer with 1 unit

        model_neural_i %>% compile(loss="MSE", optimizer=optimizer_adam(learning_rate = learning_rate[i]), metrics="Accuracy")

        model_neural_i %>% fit(x = X, y = y, epochs = 250, verbose = 0)

      } else if (length[k] == 10) {

        normalize_i <- layer_normalization()   # Create a normalization layer
        normalize_i %>% adapt(X)   # Adapt the normalization layer to the data

        model_neural_i <- keras_model_sequential()   # Create a sequential model

        model_neural_i %>%
          normalize_i() %>%   # Apply normalization

          layer_dense(name = "HiddenLayer_1", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_2", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_3", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_4", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_5", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_6", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_7", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_8", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_9", units = width[j], activation = 'relu') %>%
          layer_dense(name = "HiddenLayer_10", units = width[j], activation = 'relu') %>%

           layer_dense(name = "OutputLayer", units = 1)   # Add output layer with 1 unit

        model_neural_i %>% compile(loss="MSE", optimizer=optimizer_adam(learning_rate = learning_rate[i]), metrics="Accuracy")

        model_neural_i %>% fit(x = X, y = y, epochs = 250, verbose = 0)

      }

      # Calculate the accuracy of the model predictions
      eval <- model_neural_i %>% evaluate(X, y, verbose = 0)
      acc <- eval[2]

      # Append the accuracy and parameter values to the results data frame
      results <- rbind(results, c(acc, learning_rate[i], width[j], length[k]))
    }
  }
}

# Set column names for the results data frame
colnames(results) <- c("accuracy", "learning_rate", "width", "length")

# Create a 3D scatter plot using scatter3D
plot3D::scatter3D(x = results$learning_rate, y = results$width, z = results$length,
                  theta = 30, phi = 10,
                  xlab = "Learning Rate", ylab = "Width", zlab = "Length",
                  ticktype = "detailed",
                  pch = 19,
                  cex.axis = 0.75, cex.lab = 0.75, expand = 0.75,
                  colvar = results$accuracy, col = viridis(100))



# Select the optimal parameter combination with the highest accuracy
opt_params <- results[which.max(results$accuracy),]

opt_params

# Clean up
rm(list = c("X", "y", "i", "j", "k", "width", "length", "learning_rate", "results", "opt_params", "normalize_i", "model_neural_i", "eval"))

Looking at the graph, it is evident that we improve our model with more neurons per layer as well as the with higher number of layers but in order to save computational memory we still decided to choose just three layers and sixteen neurons per layer for our model. Furthermore, our investigation shows that the best learning rate in our case is the default of 0.001.

Ensemble methods

formula <- Survived ~ FirstClass + SecondClass + Sex + Age + Sibsp + Parch + Fare + Cherbourg + Queenstown

# Building a random forest model using the randomForest function
# mtry parameter is set to 9, which controls feature subsampling
# ntree parameter is set to 500, specifying the number of trees in the random forest
model_ensemble_bagging <- randomForest(formula,
                                       data=data,
                                       mtry=9,
                                       ntree=500)
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
# Creating a variable importance plot for the random forest model
varImpPlot(model_ensemble_bagging, main="Bagging: Variable Importance")


# Creating a data.table to compare actual survival values with predicted values from the bagging model
model_bag_result_comparison <- data.table(actual = data$Survived,
                                          prediction = predict(model_ensemble_bagging, newdata = data))

# Building a gradient boosting model using the gbm function
# distribution is set to "gaussian" for regression, but could be modified for classification
# interaction.depth sets the maximum depth of the individual trees
# n.trees is set to 500, indicating the number of boosting iterations
# shrinkage controls the step size when updating the weights of each tree
# cv.folds is set to 5, specifying the number of cross-validation folds for model evaluation
model_ensemble_boosting <- gbm(formula = formula,
                               data=data,
                               distribution="gaussian",
                               interaction.depth=1,
                               n.trees=500,
                               shrinkage=0.1,
                               cv.folds = 5)

# Calculating the performance of the boosting model using cross-validation
performance_ensemble <- gbm.perf(model_ensemble_boosting, method = "cv")


# Generating a summary of the boosting model, but plotit parameter is set to FALSE, so no plot will be generated
summary(model_ensemble_boosting, plotit = FALSE)

# Merging the variable importance data from the bagging model with the summary data from the boosting model
# Preparing the data for plotting by transforming percentages and converting to long format using melt function
dt_varimp <- merge(data.table(importance(model_ensemble_bagging, type = 2), keep.rownames = TRUE),
                   data.table(summary(model_ensemble_boosting, plotit = FALSE)),
                   by.x = "rn", by.y = "var")
dt_varimp <- setNames(dt_varimp, c("Variable", "Bagging", "Boosting"))
dt_varimp[, Bagging := 100*Bagging/sum(Bagging)]
dt_varimp <- melt(dt_varimp, id.vars = "Variable",
                  measure.vars = c("Bagging","Boosting"),
                  variable.name = "y",
                  value.name = "x")
dt_varimp <- dt_varimp[order(y,-x)]

# Plotting the variable importance comparison using ggplot2
ggplot(data = dt_varimp) +
  geom_col(aes(y=Variable, x=x, fill=y)) +
  labs(
    x = "Relative Importance",
    y = "Variables") +
  scale_fill_manual(values = c("#f1b147","#4CA7DE")) +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"),
        axis.ticks=element_blank(),
        legend.position = "none") +
  facet_wrap(~y)


rm(list = c("dt_varimp", "model_bag_result_comparison", "performance_ensemble"))

In analysis of two ensemble methods which are Bagging and Boosting, it is clear that after Sex and Age, Passenger Fare is the most important variable. This supports our assumption that the wealth of passengers may actually influence chances of survival heavily. The structure of our data supports the assumption that Fare does not depend on the family size and the class of the passenger also does not have an impact on survival chances.Comparing the two models shows that both target the same variables.

Investigating hyperparameters for the boosting

### Investigating hyperparameters
# Define the values
n_trees <- seq(from = 250, to = 750, by = 250)
shrinking <- seq(from = 0.05, to = 0.2, by = 0.05)
cv_folds <- seq(from = 2, to = 8, by = 3)

# Create an empty data frame to store the results
results <- data.frame()

# Iterate over the combinations
for (i in 1:length(n_trees)) {
  for (j in 1:length(shrinking)) {
    for (k in 1:length(cv_folds)) {
      # Build a  model with the specified parameters
      model  <- gbm(formula = formula,
                               data=data,
                               distribution="gaussian",
                               interaction.depth=1,
                               n.trees=n_trees[i],
                               shrinkage=shrinking[j],
                               cv.folds = cv_folds[k])

      # Calculate the accuracy of the model predictions
      acc <- gbm.perf(model, method = "cv", plot.it = FALSE)

      # Append the accuracy and parameter values to the results data frame
      results <- rbind(results, c(acc, n_trees[i], shrinking[j], cv_folds[k]))
    }
  }
}

# Set column names for the results data frame
colnames(results) <- c("accuracy", "n_trees", "shrinking", "cv_folds")

# Create a 3D scatter plot using scatter3D
plot3D::scatter3D(x = results$n_trees, y = results$shrinking, z = results$cv_folds,
                  theta = 30, phi = 10,
                  xlab = "Number of trees", ylab = "Shrinkage", zlab = "CV folds",
                  ticktype = "detailed",
                  pch = 19,
                  cex.axis = 0.75, cex.lab = 0.75, expand = 0.75,
                  colvar = results$accuracy, col = viridis(100))



# Select the optimal parameter combination with the highest accuracy
opt_params <- results[which.max(results$accuracy),]

print(opt_params)

# Build a model using the optimal parameters
model_opt  <- gbm(formula = formula,
               data=data,
               distribution="gaussian",
               interaction.depth=1,
               n.trees = opt_params$n_trees,
               shrinkage = opt_params$shrinking,
               cv.folds = opt_params$cv_folds)

# Calculate the mean accuracy of the model predictions
mean(predict(model_opt, data) == data$Survived)
Using 95 trees...
[1] 0
# Clean up
rm(list = c("acc", "cv_folds", "i", "j", "k", "n_trees", "shrinking", "results", "opt_params", "model_opt"))

As the performance of the boosting model seems to vary, we did not change our hyperparameters from our default options. Though since bagging does lead to the best results, we are confident in our hyperparameter choice.



PCA

Covariance matrix describe the data to finding the best direction where the data most spread. So let us find the covariance matrix of the data: center the data points and calculate the variances and covariances Next, find the eigenvectors and the corresponding eigenvalues of the covariance matrix. Sort the eigenvectors v by the absolute magnitude of their eigenvalues: these are our principal components (PCs) and the basis vectors of our new coordinate system.

Reducing principle components

We are back to dropping dimensions but this time we drop principal components instead of variables. We hope to greatly reduce the PCs while capturing most varioation along the first PCs.

We may want to capture a minimum amount of variance of the data in total or keep all the principle components that –individually– explain a minimum amount of variance. Similar to k-means, we can produce a scree plot of our principal components in descending order of their eigenvalues.

What is more is instead of dropping PCs we may be interested in exploring and possibly interpreting them. Certain real estate variables may drive the PC1 coordinate (amenities) while others drive the PC2 coordinate (location). We have already seen that principle components no longer represent single variables as our original basis vectors did. Each PC coordinate is a linear combination of the original variables. The eigen vectors v determine how single variables of x contribute to each PC coordinate via v^Tx.

# We exclude thirdClass because it is our baseline class. 
dt_pca <- data.table(scale(data[, c("Age", "Fare", "Sex", "Parch", "Sibsp", "Queenstown", "Cherbourg", "FirstClass", "SecondClass")]))
# Will always use a library which decomposes the covariance matrix for us and supplies other fundamental outputs.
model_pca <- prcomp(dt_pca)
summary(model_pca)
Importance of components:
                          PC1    PC2    PC3    PC4     PC5     PC6     PC7    PC8
Standard deviation     1.4475 1.3223 1.0565 0.9919 0.93994 0.84767 0.78654 0.7385
Proportion of Variance 0.2328 0.1943 0.1240 0.1093 0.09817 0.07984 0.06874 0.0606
Cumulative Proportion  0.2328 0.4271 0.5511 0.6604 0.75861 0.83844 0.90718 0.9678
                           PC9
Standard deviation     0.53853
Proportion of Variance 0.03222
Cumulative Proportion  1.00000

It is clear that the model enables us to dive into our 9 principle componets. In the summary, we also find the share that each PC contributes to explaing the variance of our data in descending order of the eigenvalues.

fviz_eig(model_pca, addlabels = TRUE) + 
         theme(plot.title = element_text(hjust = 0.5))

Looking at the Scree plot, it is evidence that we explain ~66% of the variance with first 4 PCs.

To investigate how our original variables contribute to the PCs and to possibly interpret these contributions, we can look at the eigenvectors v. They essentially conribute the coefficients to the linear combination of our variables that produce the PCs. We find the eigenvectors at model_pca$rotation.

Here, $rotation gives the eigenvectors scaled to a specific length which are also called loadings: eigenvectors ∗ √eigenvalues

The above eigenvectors take each data point to the PC coordinate system by multiplying vector x. V: xV.

Let’s illustrate Score Plot. It plotos our passengers in only the first two dimensions (PC1 and PC2) which capture 55% of variance.

Finally, let us illustrate our principle components in a loadings plot. Basically, the direction of an arrow shows which PCs a variable contributes to and the angles between arrows indicate the difference in these directions.

# Matrix of (scaled) eigenvectors: V
round(model_pca$rotation, 2)
              PC1   PC2   PC3   PC4   PC5   PC6   PC7   PC8   PC9
Age          0.20 -0.47  0.11 -0.34  0.47 -0.01 -0.37  0.42  0.28
Fare         0.55  0.12  0.08 -0.06  0.16 -0.26  0.34 -0.43  0.53
Sex         -0.18 -0.33 -0.37  0.50  0.34 -0.50 -0.16 -0.26 -0.09
Parch        0.12  0.55  0.08 -0.04  0.31  0.10 -0.67 -0.32 -0.15
Sibsp        0.05  0.56 -0.18  0.16  0.26 -0.31  0.19  0.65  0.03
Queenstown  -0.09  0.07 -0.58 -0.70 -0.18 -0.33 -0.05 -0.10 -0.07
Cherbourg    0.40 -0.06  0.11  0.20 -0.64 -0.39 -0.43  0.20  0.04
FirstClass   0.60 -0.16 -0.06 -0.05  0.16  0.04  0.21  0.02 -0.74
SecondClass -0.29  0.03  0.67 -0.25  0.09 -0.56  0.11 -0.07 -0.26
fviz_pca_ind(model_pca, 
             geom.ind = "point", 
             col.ind = as.factor(data$Survived), 
             palette = c("#f1b147", "#4CA7DE")) +
  ggtitle("Score Plot") +
  theme(plot.title = element_text(hjust = 0.5))


fviz_pca_var(model_pca, 
             col.var="contrib", 
             repel = TRUE) +
    theme_minimal()

We find here that Fare and First Class seem to indicate strongly in one direction possibly signifying wealth. Secondly, Sibsp and Parch indiacte closely together pointing at an overarching concept of family size.

Reconstructing Variables

Our matrix of (scaled) eigenvectors V above transformed our original 9 coordinates (variables) into 9 PC coordinates.

# Insert PCA into our data for continued use
pca <- as.data.frame(as.matrix(dt_pca) %*%
  as.matrix(model_pca$rotation))

data <- cbind(data, pca)

# Define a function to perform PCA transformations on the test data
apply_pca <- function(data, model) {
  dt_pca <- data.table(scale(data[, c("Age", "Fare", "Sex", "Parch", "Sibsp", "Queenstown", "Cherbourg", "FirstClass", "SecondClass")]))
  
  pca <- as.data.frame(as.matrix(dt_pca) %*%
    as.matrix(model$rotation))
  
  return(cbind(data, pca))
}

To reconstruct the original variables we can multiply the PC coordinates by VT:

# Passenger 1: Original variables
# x
round(as.matrix(dt_pca)[1,],2)
        Age        Fare         Sex       Parch       Sibsp  Queenstown   Cherbourg 
      -0.53       -0.52        0.76       -0.51        0.52       -0.20       -0.47 
 FirstClass SecondClass 
      -0.59       -0.57 
# Passenger 1: PC coordinates
# x * V
round(as.matrix(dt_pca)[1,] %*%
        as.matrix(model_pca$rotation), 2)
       PC1  PC2  PC3  PC4 PC5  PC6  PC7  PC8  PC9
[1,] -0.92 0.04 -0.8 0.91 0.1 0.09 0.36 0.26 0.19
# Passenger 1: Reconstructed original variables
# x * V * VT
round(as.matrix(dt_pca)[1,] %*%
        as.matrix(model_pca$rotation) %*%
        t(as.matrix(model_pca$rotation)), 2)
       Age  Fare  Sex Parch Sibsp Queenstown Cherbourg FirstClass SecondClass
[1,] -0.53 -0.52 0.76 -0.51  0.52       -0.2     -0.47      -0.59       -0.57

However, not much is gained in terms of dimensionality reduction if we simply keep 7 PC coordinates instead of 10 original variables. We have seen at the very beginning that the first 4 PCs capture >65% of the variance of our data, so let us reduce the principle components to 4. We will call our eigenvector matrix V with only the first 4 columns Vreduced If we projected our passenger 1 onto only 4 PCs, we had only 4 values to store: xVreduced

# x * V_reduced
as.matrix(dt_pca)[1,] %*%
  as.matrix(model_pca$rotation[,1:4])
            PC1        PC2        PC3       PC4
[1,] -0.9219128 0.04287835 -0.7984102 0.9140071
# x * V_reduced * V_reducedT
as.matrix(dt_pca)[1,] %*%
  as.matrix(model_pca$rotation[,1:4]) %*%
  t(as.matrix(model_pca$rotation[,1:4]))
            Age       Fare       Sex      Parch     Sibsp  Queenstown  Cherbourg
[1,] -0.6042263 -0.6158179 0.9094717 -0.1944706 0.2761572 -0.09504479 -0.2792797
     FirstClass SecondClass
[1,] -0.5505641  -0.4989356

If we have Vreduced we can reconstruct any passenger from its 4 PC values via xVreduced VTreduced In other words, we only need the PC coordinates xVreduced and can reconstruct the original data. This reconstruction is approximate and depends on the number of PCs we use. Compare the original variable values above to the reconstruction below.

How many dimensions we can/shall drop depends on our purpose. Let us investigate the total reconstruction error for our one passenger depending on the number of eigenvectors we retain in Vreduced. We loop through the reconstruction of passenger 1 for 1 to 9 PCs:

results <- list()

for (pcs in 1:9){
  passenger_reconstructed <- as.matrix(dt_pca)[1,] %*% 
    as.matrix(model_pca$rotation[,1:pcs]) %*% 
    t(as.matrix(model_pca$rotation[,1:pcs])) 
  
  results[[pcs]] <- data.table("PCs used" = pcs,
                               "MAE"= sum(abs(as.matrix(dt_pca)[1,]-passenger_reconstructed)),
                               passenger_reconstructed)
  }

dt_reconstruction <- rbindlist(results)
round(dt_reconstruction,2)
# Clean up
rm(list = c("dt_pca", "dt_reconstruction", "pca", "passenger_reconstructed", "pcs", "results"))

Models with PCA

PCA: Logistic regression

# Define the logistic regression using PCAs
model_pca_glm <- glm(formula = Survived ~ PC1 + PC2 + PC3 + PC4, 
                 data = data, family = binomial(link = "logit"))

# Print summary statistic
print(summary(model_pca_glm))

Call:
glm(formula = Survived ~ PC1 + PC2 + PC3 + PC4, family = binomial(link = "logit"), 
    data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.5196  -0.8359  -0.4931   0.8222   2.1363  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) -0.45619    0.09078  -5.025 5.02e-07 ***
PC1          0.61801    0.07207   8.575  < 2e-16 ***
PC2          0.33371    0.06741   4.950 7.40e-07 ***
PC3          0.70019    0.08884   7.881 3.23e-15 ***
PC4         -0.56679    0.09276  -6.110 9.94e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 960.90  on 711  degrees of freedom
Residual deviance: 742.15  on 707  degrees of freedom
AIC: 752.15

Number of Fisher Scoring iterations: 4
# Fit of the model
with(model_pca_glm, pchisq(null.deviance - deviance, df.null - df.residual, lower.tail = FALSE))
[1] 3.469072e-46
# A p-value below 0.001 indicates that the model performs better than an empty model

As all inputs seem to be statistically significant at the 0.1% level.

PCA: Tree

### Base tree 
# Build a decision tree model using rpart
model_tree <- rpart(data = data, formula = Survived ~ PC1 + PC2 + PC3 + PC4, method = "class")

# Plot the decision tree model using prp, with extra details (extra = 2)
prp(model_tree, extra = 2, main = "Base tree for model")


# Print an plot data on tree
printcp(model_tree)

Classification tree:
rpart(formula = Survived ~ PC1 + PC2 + PC3 + PC4, data = data, 
    method = "class")

Variables actually used in tree construction:
[1] PC1 PC2 PC3 PC4

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.215278      0   1.00000 1.00000 0.045472
2 0.142361      1   0.78472 0.81944 0.043614
3 0.041667      2   0.64236 0.69792 0.041704
4 0.027778      4   0.55903 0.65625 0.040912
5 0.017361      5   0.53125 0.62153 0.040194
6 0.015046      6   0.51389 0.62500 0.040268
7 0.013889      9   0.46875 0.63542 0.040487
8 0.010417     10   0.45486 0.64931 0.040773
9 0.010000     16   0.38889 0.62153 0.040194
plotcp(model_tree)

# The tree shows that the first deciding factor seems to be gender. Afterwards mainly class and age show seem prominent deciding factors for branches though interestingly enough fare seems to play an important role as well.

# Determine the minimum complexity parameter (cp) for pruning
#cp parameter: captures model complexity
min_cp <- model_tree$cptable[which.min(model_tree$cptable[, "xerror"]), "CP"]

# Prune the decision tree model using the minimum cp
model_tree_2 <- prune(model_tree, cp = min_cp)

# Plot the pruned decision tree model using prp, with extra details (extra = 2)
prp(model_tree_2, extra = 2, main = "Pruned base tree with less complexity")


# The branch determined by being male and over the age of 6 shows the biggest group and the most wrong guesses of the model.

# Calculate the mean accuracy of the model predictions
mean(predict(model_tree, data, type = "class")==data$Survived)
[1] 0.8426966
mean(predict(model_tree_2, data, type = "class")==data$Survived)
[1] 0.7851124
# Between the accuracy for the pruned and not-pruned model there is only a 2% difference so we will choose the pruned model as representative as it is less complex
model_pca_trees <- model_tree

# Print an plot data on tree
printcp(model_tree_2)

Classification tree:
rpart(formula = Survived ~ PC1 + PC2 + PC3 + PC4, data = data, 
    method = "class")

Variables actually used in tree construction:
[1] PC1 PC3 PC4

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.215278      0   1.00000 1.00000 0.045472
2 0.142361      1   0.78472 0.81944 0.043614
3 0.041667      2   0.64236 0.69792 0.041704
4 0.027778      4   0.55903 0.65625 0.040912
5 0.017361      5   0.53125 0.62153 0.040194
plotcp(model_tree_2)


rm(list = c("model_tree", "model_tree_2"))

As we find a notable disparity in accuracy between the base tree and the pruned tree favoring the un-pruned base tree in the case of using PCA, we choose the un-pruned model for further application.

PCA: Neural Networks

set.seed(12345)   # Set a seed for reproducibility

# Data setup
X <- as.matrix(data[, c("PC1", "PC2", "PC3", "PC4")])
y <- data$Survived

## Model as in lecture

normalize_pca <- layer_normalization()   # Create a normalization layer
normalize_pca %>% adapt(X)   # Adapt the normalization layer to the data

print(normalize_pca$mean)   # Print the mean values used for normalization
tf.Tensor([[ 2.3283064e-09 -1.0186341e-08  5.1222742e-09  2.7939677e-09]], shape=(1, 4), dtype=float32)
model_pca_neural <- keras_model_sequential()   # Create a sequential model

model_pca_neural %>% 
  normalize_pca() %>%   # Apply normalization
  
  layer_dense(name = "HiddenLayer_1", units = 16, activation = 'relu') %>%   # Add a hidden layer with 16 units and ReLU activation
  layer_dense(name = "HiddenLayer_2", units = 16, activation = 'relu') %>%   # Add another hidden layer with 16 units and ReLU activation
  layer_dense(name = "HiddenLayer_3", units = 16, activation = 'relu') %>%   # Add a third hidden layer with 16 units and ReLU activation
 
   layer_dense(name = "OutputLayer", units = 1)   # Add an output layer with 1 unit

summary(model_pca_neural)   # Print the summary of the model
Model: "sequential_119"
____________________________________________________________________________________
 Layer (type)                   Output Shape                  Param #    Trainable  
====================================================================================
 normalization_119 (Normalizati  (None, 4)                    9          Y          
 on)                                                                                
 HiddenLayer_1 (Dense)          (None, 16)                    80         Y          
 HiddenLayer_2 (Dense)          (None, 16)                    272        Y          
 HiddenLayer_3 (Dense)          (None, 16)                    272        Y          
 OutputLayer (Dense)            (None, 1)                     17         Y          
====================================================================================
Total params: 650
Trainable params: 641
Non-trainable params: 9
____________________________________________________________________________________
#plot_model(model_base)

model_pca_neural %>% compile(
  loss = "MSE",
  optimizer = optimizer_adam(learning_rate = 0.001),
  metrics = "Accuracy"
)   # Compile the model with mean squared error loss, Adam optimizer, and accuracy as a metric

model_pca_neural %>% fit(
  x = X,
  y = y,
  epochs = 500,
  verbose = 0
)   # Train the model on the data for 500 epochs

#model_pca_neural$layers[[2]]$kernel   # Print the kernel (weights) of the second layer
#model_pca_neural$layers[[3]]$kernel   # Print the kernel (weights) of the third layer
#model_pca_neural$layers[[4]]$kernel   # Print the kernel (weights) of the fourth layer


model_pca_neural %>% evaluate(X, y, verbose = 0)   # Evaluate the model on the data
      loss   Accuracy 
0.09847966 0.86516851 
rm(list = c("X", "y"))

PCA: Ensemble methods

formula_pca <- Survived ~ PC1 + PC2 + PC3 + PC4

# Building a random forest model using the randomForest function
# mtry parameter is set to 9, which controls feature subsampling
# ntree parameter is set to 500, specifying the number of trees in the random forest
model_pca_ensemble_bagging <- randomForest(formula_pca, 
                                       data=data, 
                                       mtry=9, 
                                       ntree=500)
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?Warning: invalid mtry: reset to within valid range
# Creating a variable importance plot for the random forest model
varImpPlot(model_pca_ensemble_bagging, main="Variable Importance")


# Creating a data.table to compare actual survival values with predicted values from the bagging model
model_bag_result_comparison <- data.table(actual = data$Survived,
                                          prediction = predict(model_pca_ensemble_bagging, newdata = data))

# Building a gradient boosting model using the gbm function
# distribution is set to "gaussian" for regression, but could be modified for classification
# interaction.depth sets the maximum depth of the individual trees
# n.trees is set to 500, indicating the number of boosting iterations
# shrinkage controls the step size when updating the weights of each tree
# cv.folds is set to 5, specifying the number of cross-validation folds for model evaluation
model_pca_ensemble_boosting <- gbm(formula = formula_pca, 
                               data=data, 
                               distribution="gaussian",
                               interaction.depth=1,
                               n.trees=500, 
                               shrinkage=0.1,
                               cv.folds = 5)

# Calculating the performance of the boosting model using cross-validation
performance_ensemble <- gbm.perf(model_pca_ensemble_boosting, method = "cv")


# Generating a summary of the boosting model, but plotit parameter is set to FALSE, so no plot will be generated
summary(model_pca_ensemble_boosting, plotit = FALSE)

# Merging the variable importance data from the bagging model with the summary data from the boosting model
# Preparing the data for plotting by transforming percentages and converting to long format using melt function
dt_varimp <- merge(data.table(importance(model_pca_ensemble_bagging, type = 2), keep.rownames = TRUE), 
                   data.table(summary(model_pca_ensemble_boosting, plotit = FALSE)), 
                   by.x = "rn", by.y = "var")
dt_varimp <- setNames(dt_varimp, c("Variable", "Bagging", "Boosting"))
dt_varimp[, Bagging := 100*Bagging/sum(Bagging)]
dt_varimp <- melt(dt_varimp, id.vars = "Variable", 
                  measure.vars = c("Bagging","Boosting"), 
                  variable.name = "y", 
                  value.name = "x")
dt_varimp <- dt_varimp[order(y,-x)]

# Plotting the variable importance comparison using ggplot2
ggplot(data = dt_varimp) +
  geom_col(aes(y=Variable, x=x, fill=y)) +
  labs(
    x = "Relative Importance",
    y = "Variables") +
  scale_fill_manual(values = c("#f1b147","#4CA7DE")) +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"),
        axis.ticks=element_blank(), 
        legend.position = "none") +
  facet_wrap(~y)


rm(list = c("dt_varimp", "model_bag_result_comparison", "performance_ensemble"))

Models with full PCA

Full PCA: Logistic regression

# Define a logistic model using all PCAs
model_pca_full_glm <- glm(formula = Survived ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + PC7 + PC8, 
                 data = data, family = binomial(link = "logit"))

# Print summary statistic
print(summary(model_pca_full_glm))

Call:
glm(formula = Survived ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + 
    PC7 + PC8, family = binomial(link = "logit"), data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.6786  -0.6521  -0.4426   0.6552   2.4048  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) -0.43758    0.09871  -4.433 9.30e-06 ***
PC1          0.75416    0.08797   8.572  < 2e-16 ***
PC2          0.31435    0.07661   4.103 4.07e-05 ***
PC3          0.79900    0.10184   7.846 4.30e-15 ***
PC4         -0.55678    0.10008  -5.563 2.65e-08 ***
PC5         -0.65080    0.10552  -6.168 6.93e-10 ***
PC6          0.35515    0.11641   3.051  0.00228 ** 
PC7          0.72030    0.13223   5.447 5.11e-08 ***
PC8         -0.28703    0.14355  -1.999  0.04556 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 960.90  on 711  degrees of freedom
Residual deviance: 652.47  on 703  degrees of freedom
AIC: 670.47

Number of Fisher Scoring iterations: 5
# Fit of the model
with(model_pca_full_glm, pchisq(null.deviance - deviance, df.null - df.residual, lower.tail = FALSE))
[1] 6.593554e-62
# A p-value below 0.001 indicates that the model performs better than an empty model

As all inputs seem to be statistically significant at the 0.1% level.

Full PCA: Tree

### Base tree 
# Build a decision tree model using rpart
model_tree <- rpart(data = data, formula = Survived ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + PC7 + PC8, method = "class")

# Plot the decision tree model using prp, with extra details (extra = 2)
prp(model_tree, extra = 2, main = "Base tree for model")


# Print an plot data on tree
printcp(model_tree)

Classification tree:
rpart(formula = Survived ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + 
    PC7 + PC8, data = data, method = "class")

Variables actually used in tree construction:
[1] PC1 PC2 PC3 PC4 PC5 PC6 PC7

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.215278      0   1.00000 1.00000 0.045472
2 0.034722      2   0.56944 0.67708 0.041317
3 0.027778      3   0.53472 0.60764 0.039891
4 0.010417      4   0.50694 0.57639 0.039176
5 0.010000     10   0.43750 0.56944 0.039010
plotcp(model_tree)

# The tree shows that the first deciding factor seems to be gender. Afterwards mainly class and age show seem prominent deciding factors for branches though interestingly enough fare seems to play an important role as well.

# Determine the minimum complexity parameter (cp) for pruning
#cp parameter: captures model complexity
min_cp <- model_tree$cptable[which.min(model_tree$cptable[, "xerror"]), "CP"]

# Prune the decision tree model using the minimum cp
model_tree_2 <- prune(model_tree, cp = min_cp)

# Plot the pruned decision tree model using prp, with extra details (extra = 2)
prp(model_tree_2, extra = 2, main = "Pruned base tree with less complexity")


# The branch determined by being male and over the age of 6 shows the biggest group and the most wrong guesses of the model.

# Calculate the mean accuracy of the model predictions
mean(predict(model_tree, data, type = "class")==data$Survived)
[1] 0.8230337
mean(predict(model_tree_2, data, type = "class")==data$Survived)
[1] 0.8230337
# Between the accuracy for the pruned and not-pruned model there is only a 2% difference so we will choose the pruned model as representative as it is less complex
model_pca_full_trees <- model_tree_2

# Print an plot data on tree
printcp(model_tree_2)

Classification tree:
rpart(formula = Survived ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + 
    PC7 + PC8, data = data, method = "class")

Variables actually used in tree construction:
[1] PC1 PC2 PC3 PC4 PC5 PC6 PC7

Root node error: 288/712 = 0.40449

n= 712 

        CP nsplit rel error  xerror     xstd
1 0.215278      0   1.00000 1.00000 0.045472
2 0.034722      2   0.56944 0.67708 0.041317
3 0.027778      3   0.53472 0.60764 0.039891
4 0.010417      4   0.50694 0.57639 0.039176
5 0.010000     10   0.43750 0.56944 0.039010
plotcp(model_tree_2)


rm(list = c("model_tree", "model_tree_2"))

In this case the pruned and un-pruned model are identical which makes sense given the nature of PCA.

Full PCA: Neural Networks

set.seed(12345)   # Set a seed for reproducibility

# Data setup
X <- as.matrix(data[, c("PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", "PC8")])
y <- data$Survived

## Model as in lecture

normalize_pca_full <- layer_normalization()   # Create a normalization layer
normalize_pca_full %>% adapt(X)   # Adapt the normalization layer to the data

print(normalize_pca_full$mean)   # Print the mean values used for normalization
tf.Tensor(
[[ 1.3969839e-09 -2.3283064e-09  8.8475645e-09  0.0000000e+00
  -1.3969839e-09  2.6775524e-09 -1.3969839e-09 -3.2596290e-09]], shape=(1, 8), dtype=float32)
model_pca_full_neural <- keras_model_sequential()   # Create a sequential model

model_pca_full_neural %>% 
  normalize_pca_full() %>%   # Apply normalization
  
  layer_dense(name = "HiddenLayer_1", units = 16, activation = 'relu') %>%   # Add a hidden layer with 16 units and ReLU activation
  layer_dense(name = "HiddenLayer_2", units = 16, activation = 'relu') %>%   # Add another hidden layer with 16 units and ReLU activation
  layer_dense(name = "HiddenLayer_3", units = 16, activation = 'relu') %>%   # Add a third hidden layer with 16 units and ReLU activation
 
   layer_dense(name = "OutputLayer", units = 1)   # Add an output layer with 1 unit

summary(model_pca_full_neural)   # Print the summary of the model
Model: "sequential_120"
____________________________________________________________________________________
 Layer (type)                   Output Shape                  Param #    Trainable  
====================================================================================
 normalization_120 (Normalizati  (None, 8)                    17         Y          
 on)                                                                                
 HiddenLayer_1 (Dense)          (None, 16)                    144        Y          
 HiddenLayer_2 (Dense)          (None, 16)                    272        Y          
 HiddenLayer_3 (Dense)          (None, 16)                    272        Y          
 OutputLayer (Dense)            (None, 1)                     17         Y          
====================================================================================
Total params: 722
Trainable params: 705
Non-trainable params: 17
____________________________________________________________________________________
#plot_model(model_base)

model_pca_full_neural %>% compile(
  loss = "MSE",
  optimizer = optimizer_adam(learning_rate = 0.001),
  metrics = "Accuracy"
)   # Compile the model with mean squared error loss, Adam optimizer, and accuracy as a metric

model_pca_full_neural %>% fit(
  x = X,
  y = y,
  epochs = 500,
  verbose = 0
)   # Train the model on the data for 500 epochs

#model_pca_full_neural$layers[[2]]$kernel   # Print the kernel (weights) of the second layer
#model_pca_full_neural$layers[[3]]$kernel   # Print the kernel (weights) of the third layer
#model_pca_full_neural$layers[[4]]$kernel   # Print the kernel (weights) of the fourth layer


model_pca_full_neural %>% evaluate(X, y, verbose = 0)   # Evaluate the model on the data
      loss   Accuracy 
0.07700509 0.90028089 
rm(list = c("X", "y"))

Full PCA: Ensemble methods

formula_pca_full <- Survived ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + PC7 + PC8

# Building a random forest model using the randomForest function
# mtry parameter is set to 9, which controls feature subsampling
# ntree parameter is set to 500, specifying the number of trees in the random forest
model_pca_full_ensemble_bagging <- randomForest(formula_pca, 
                                       data=data, 
                                       mtry=9, 
                                       ntree=500)
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?Warning: invalid mtry: reset to within valid range
# Creating a variable importance plot for the random forest model
varImpPlot(model_pca_full_ensemble_bagging, main="Variable Importance")


# Creating a data.table to compare actual survival values with predicted values from the bagging model
model_bag_result_comparison <- data.table(actual = data$Survived,
                                          prediction = predict(model_pca_full_ensemble_bagging, newdata = data)) 

# Building a gradient boosting model using the gbm function
# distribution is set to "gaussian" for regression, but could be modified for classification
# interaction.depth sets the maximum depth of the individual trees
# n.trees is set to 500, indicating the number of boosting iterations
# shrinkage controls the step size when updating the weights of each tree
# cv.folds is set to 5, specifying the number of cross-validation folds for model evaluation
model_pca_full_ensemble_boosting <- gbm(formula = formula_pca_full, 
                               data=data, 
                               distribution="gaussian",
                               interaction.depth=1,
                               n.trees=500, 
                               shrinkage=0.1,
                               cv.folds = 5)

# Calculating the performance of the boosting model using cross-validation
performance_ensemble <- gbm.perf(model_pca_full_ensemble_boosting, method = "cv")


# Generating a summary of the boosting model, but plotit parameter is set to FALSE, so no plot will be generated
summary(model_pca_full_ensemble_boosting, plotit = FALSE)

# Merging the variable importance data from the bagging model with the summary data from the boosting model
# Preparing the data for plotting by transforming percentages and converting to long format using melt function
dt_varimp <- merge(data.table(importance(model_pca_full_ensemble_bagging, type = 2), keep.rownames = TRUE), 
                   data.table(summary(model_pca_full_ensemble_boosting, plotit = FALSE)), 
                   by.x = "rn", by.y = "var")
dt_varimp <- setNames(dt_varimp, c("Variable", "Bagging", "Boosting"))
dt_varimp[, Bagging := 100*Bagging/sum(Bagging)]
dt_varimp <- melt(dt_varimp, id.vars = "Variable", 
                  measure.vars = c("Bagging","Boosting"), 
                  variable.name = "y", 
                  value.name = "x")
dt_varimp <- dt_varimp[order(y,-x)]

# Plotting the variable importance comparison using ggplot2
ggplot(data = dt_varimp) +
  geom_col(aes(y=Variable, x=x, fill=y)) +
  labs(
    x = "Relative Importance",
    y = "Variables") +
  scale_fill_manual(values = c("#f1b147","#4CA7DE")) +
  theme_minimal() +
  theme(axis.line = element_line(color = "#000000"),
        axis.ticks=element_blank(), 
        legend.position = "none") +
  facet_wrap(~y)


rm(list = c("dt_varimp", "model_bag_result_comparison", "performance_ensemble"))

Notably again in this case our bagging model performs the same as the bagging model using only the first four PCs. This occurs as the relatively unimportant last four PCs will never be chosen by the trees in this case.

Data for Validation

test_data <- read_csv(path1)  # Read the CSV file located at the specified path and store the test_data in the 'test_data' variable
Rows: 418 Columns: 11── Column specification ────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): Name, Sex, Ticket, Cabin, Embarked
dbl (6): PassengerId, Pclass, Age, SibSp, Parch, Fare
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
test_solution <- read_csv(path2)
Rows: 418 Columns: 2── Column specification ────────────────────────────────────────────────────────────
Delimiter: ","
dbl (2): PassengerId, Survived
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
test_data$Survived <- test_solution$Survived

test_data <- clean_data(test_data)
[1] "The number of missing values in Age variables: 86"
[1] "The number of missing values in Embarked variables: 0"
[1] "The number of missing values in Fare variables: 1"
test_data <- apply_pca(test_data, model_pca)

test_solution <- as.data.frame(test_data$Survived)
colnames(test_solution) <- "Survived"

rm(list = c("path1", "path2"))

Predictions for validation

Let us perform predictions for different models on the validation data.

# Prediction for logistic regression
test_solution$logistic_prediction <- predict(model_glm, 
                                             newdata = test_data)
# Converting the predicted probabilities to binary predictions (0 or 1) using a threshold of 0.5
test_solution$logistic_prediction <- ifelse(test_solution$logistic_prediction > 0, 1, 0)

# Prediction for tree regression
test_solution$tree_prediction <- predict(model_trees, 
                                         newdata = test_data)[,"1"]
# Converting the predicted probabilities to binary predictions (0 or 1) using a threshold of 0.5
test_solution$tree_prediction <- ifelse(test_solution$tree_prediction > 0.5, 1, 0)

# Prediction for neural network
test_solution$neural_prediction <- predict(model_neural, 
                                           x = as.matrix(test_data[, c("FirstClass", "SecondClass", "ThirdClass", "Sex", "Age", "Sibsp", "Parch", "Fare", "Southhampton", "Cherbourg", "Queenstown")]), verbose = 0)

# Converting the predicted probabilities to binary predictions (0 or 1) using a threshold of 0.5
test_solution$neural_prediction <- ifelse(test_solution$neural_prediction > 0.5, 1, 0)

# Prediction for bagging
test_solution$bagging_prediction <- predict(model_ensemble_bagging, 
                                            newdata = test_data)
# Converting the predicted probabilities to binary predictions (0 or 1) using a threshold of 0.5
test_solution$bagging_prediction <- ifelse(test_solution$bagging_prediction > 0.5, 1, 0)

# Prediction for boosting
test_solution$boosting_prediction <- predict(model_ensemble_boosting, 
                                             newdata = test_data)
Using 107 trees...
# Converting the predicted probabilities to binary predictions (0 or 1) using a threshold of 0.5
test_solution$boosting_prediction <- ifelse(test_solution$boosting_prediction > 0.5, 1, 0)

##PCA

# Prediction for logistic regression
test_solution$logistic_pca_prediction <- predict(model_pca_glm, 
                                             newdata = test_data)
test_solution$logistic_pca_prediction <- ifelse(test_solution$logistic_pca_prediction > 0, 1, 0)

# Prediction for tree regression
test_solution$tree_pca_prediction <- predict(model_pca_trees, 
                                         newdata = test_data)[,"1"]

test_solution$tree_pca_prediction <- ifelse(test_solution$tree_pca_prediction > 0.5, 1, 0)

# Prediction for neural network
test_solution$neural_pca_prediction <- predict(model_pca_neural, 
                                           x = as.matrix(test_data[, c("PC1", "PC2", "PC3", "PC4")]), verbose = 0)
test_solution$neural_pca_prediction <- ifelse(test_solution$neural_pca_prediction > 0.5, 1, 0)

# Prediction for bagging
test_solution$bagging_pca_prediction <- predict(model_pca_ensemble_bagging, 
                                            newdata = test_data)
test_solution$bagging_pca_prediction <- ifelse(test_solution$bagging_pca_prediction > 0.5, 1, 0)

# Prediction for boosting
test_solution$boosting_pca_prediction <- predict(model_pca_ensemble_boosting, 
                                             newdata = test_data)
Using 305 trees...
test_solution$boosting_pca_prediction <- ifelse(test_solution$boosting_pca_prediction > 0.5, 1, 0)

##Full PCA

# Prediction for logistic regression
test_solution$logistic_pca_full_prediction <- predict(model_pca_full_glm, 
                                             newdata = test_data)
test_solution$logistic_pca_full_prediction <- ifelse(test_solution$logistic_pca_full_prediction > 0, 1, 0)

# Prediction for tree regression
test_solution$tree_pca_full_prediction <- predict(model_pca_full_trees, 
                                         newdata = test_data)[,"1"]
test_solution$tree_pca_full_prediction <- ifelse(test_solution$tree_pca_full_prediction > 0.5, 1, 0)

# Prediction for neural network
test_solution$neural_pca_full_prediction <- predict(model_pca_full_neural, 
                                           x = as.matrix(test_data[, c("PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", "PC8")]), verbose = 0)
test_solution$neural_pca_full_prediction <- ifelse(test_solution$neural_pca_full_prediction > 0.5, 1, 0)

# Prediction for bagging
test_solution$bagging_pca_full_prediction <- predict(model_pca_full_ensemble_bagging, 
                                            newdata = test_data)
test_solution$bagging_pca_full_prediction <- ifelse(test_solution$bagging_pca_full_prediction > 0.5, 1, 0)

# Prediction for boosting
test_solution$boosting_pca_full_prediction <- predict(model_pca_full_ensemble_boosting, 
                                             newdata = test_data)
Using 208 trees...
# Converting the predicted probabilities to binary predictions (0 or 1) using a threshold of 0.5
test_solution$boosting_pca_full_prediction <- ifelse(test_solution$boosting_pca_full_prediction > 0.5, 1, 0)

Validation

# Function to calculate correct guesses for a column
# This function calculates the overall accuracy of predictions for a given column by comparing it with the true 'Survived' values.
calculate_correct_guesses <- function(column, survived) {
  sum(column == survived) / length(column)
}

# Function to calculate correct guesses for survived cases in a column
# This function calculates the accuracy of predictions for the 'Survived' cases (where 'Survived' = 1) in a given column.
calculate_correct_guesses_survived <- function(column, survived) {
  sum(column == 1 & survived == 1) / sum(survived == 1)
}

# Function to calculate correct guesses for dead cases in a column
# This function calculates the accuracy of predictions for the 'Dead' cases (where 'Survived' = 0) in a given column.
calculate_correct_guesses_dead <- function(column, survived) {
  sum(column == 0 & survived == 0) / sum(survived == 0)
}

# Calculate correct guesses for each column
# The 'sapply()' function applies the 'calculate_correct_guesses()' function to each column of 'test_solution' (excluding the first column, which is 'Survived').
# The result is the overall accuracy of predictions for each column.
correct_guesses <- sapply(test_solution[-1], calculate_correct_guesses, test_solution$Survived)
correct_guesses <- c(1, correct_guesses)


# Calculate correct guesses for survived cases in each column
correct_guesses_survived <- sapply(test_solution[-1], calculate_correct_guesses_survived, test_solution$Survived)
correct_guesses_survived <- c(1, correct_guesses_survived)

# Calculate correct guesses for dead cases in each column
correct_guesses_dead <- sapply(test_solution[-1], calculate_correct_guesses_dead, test_solution$Survived)
correct_guesses_dead <- c(1, correct_guesses_dead)

# Add the three rows to the original dataframe
# Create a new data frame 'solution' with the calculated accuracy values and corresponding column names.
solution <- data.frame("Accuracy" = correct_guesses, 
                 "Accuracy: Survival" = correct_guesses_survived,
                 "Accuracy: Dead" = correct_guesses_dead)
# Remove the first row (with the value 1) as it represents the overall accuracy, not related to any specific column.
solution <- solution[-1,]
solution

The table above indicates the output of various models. The “Accuracy” column shows the overall accuracy of the model while “Accuracy Survival” indicates the accuracy of the survival rate in the specific model.

Overall the boosting without PCA does best at predicting our test data. It does when considering accuracy on all cases but also when just trying to predict only the survival of the observation. Predictions of death are surprisingly enough better performed by including all PCAs in a logistic regression. This variant also predicts the second best overall closely followed by the basic tree classification. Notably bagging and boosting under PCA perform the worst even though boosting without PCA performed second best. As a comparison we also ran validation on models which include all PCA variables to check if we could achieve more accuracy that way. Unfortunately, this did not yield better predictions except for the logistic regression.

It is important to note that errors may occur because of changing the seed.

Cross-Validation

#Optional: Merge training and test data for cross-validation 

full_data <- rbind(data, test_data)
# Check if data was merged before
if (exists("full_data")) {
  cv_data <- full_data
} else {
  cv_data <- data
}

# Define a function to split data into K folds
# Assign a fold number to each row randomly using 'ceiling' and 'sample'.
split_K <- function(dt, folds){
  dt$id <- ceiling(sample(1:nrow(dt), replace = FALSE, nrow(dt)) / 
                     (nrow(dt) / folds))
  return(dt)
}

# Set the number of folds (K) for cross-validation
K <- 10

cv_data <- split_K(cv_data, K)
# Create a data frame 'cross_validation' to store accuracy values for each model and fold
cross_validation <- data.frame(matrix(NA, nrow = 10, ncol = K))
rownames(cross_validation) <- c("Logistic", "Tree Model", "Neural Network", 
                                "Bagging", "Boosting", "PCA Logistic", "PCA Tree Model",
                                "PCA Neural Network", "PCA Bagging", "PCA Boosting")

for (i in 1:K) {
  # Select the current fold as the validation set and the rest as the training set
  dt <- cv_data[cv_data$id == i, ]
  
  # Prediction for logistic regression
  dt$logistic_prediction <- predict(model_glm, 
                                               newdata = dt)
  dt$logistic_prediction <- ifelse(dt$logistic_prediction > 0, 1, 0)
  
  cross_validation[1, i] <- calculate_correct_guesses(dt$logistic_prediction, dt$Survived)
  
  # Prediction for tree regression
  dt$tree_prediction <- predict(model_trees, 
                                           newdata = dt)[,"1"]
  dt$tree_prediction <- ifelse(dt$tree_prediction > 0.5, 1, 0)
  
  cross_validation[2, i] <- calculate_correct_guesses(dt$tree_prediction, dt$Survived)
  
  # Prediction for neural network
  dt$neural_prediction <- predict(model_neural, x = as.matrix(dt[, c("FirstClass", "SecondClass", "ThirdClass", "Sex", "Age", "Sibsp", "Parch", "Fare", "Southhampton", "Cherbourg", "Queenstown")]), verbose = 0)
  
  dt$neural_prediction <- ifelse(dt$neural_prediction > 0.5, 1, 0)

  cross_validation[3, i] <- calculate_correct_guesses(dt$neural_prediction, dt$Survived)
  
  # Prediction for bagging
  dt$bagging_prediction <- predict(model_ensemble_bagging, 
                                              newdata = dt)
  dt$bagging_prediction <- ifelse(dt$bagging_prediction > 0.5, 1, 0)
  
  cross_validation[4, i] <- calculate_correct_guesses(dt$bagging_prediction, dt$Survived)
  
  # Prediction for boosting
  dt$boosting_prediction <- predict(model_ensemble_boosting, 
                                               newdata = dt)
  dt$boosting_prediction <- ifelse(dt$boosting_prediction > 0.5, 1, 0)
  
  cross_validation[5, i] <- calculate_correct_guesses(dt$boosting_prediction, dt$Survived)
  
  ##PCA
  
  # Prediction for logistic regression
  dt$logistic_pca_prediction <- predict(model_pca_glm, 
                                               newdata = dt)
  dt$logistic_pca_prediction <- ifelse(dt$logistic_pca_prediction > 0, 1, 0)
  
  cross_validation[6, i] <- calculate_correct_guesses(dt$logistic_pca_prediction, dt$Survived)
  
  # Prediction for tree regression
  dt$tree_pca_prediction <- predict(model_pca_trees, 
                                           newdata = dt)[,"1"]
  dt$tree_pca_prediction <- ifelse(dt$tree_pca_prediction > 0.5, 1, 0)
  
  cross_validation[7, i] <- calculate_correct_guesses(dt$tree_pca_prediction, dt$Survived)
  
  # Prediction for neural network
  dt$neural_pca_prediction <- predict(model_pca_neural, x = as.matrix(dt[, c("PC1", "PC2", "PC3", "PC4")]), verbose = 0)
  dt$neural_pca_prediction <- ifelse(dt$neural_pca_prediction > 0.5, 1, 0)
  
  cross_validation[8, i] <- calculate_correct_guesses(dt$neural_pca_prediction, dt$Survived)
  
  # Prediction for bagging
  dt$bagging_pca_prediction <- predict(model_pca_ensemble_bagging, 
                                              newdata = dt)
  dt$bagging_pca_prediction <- ifelse(dt$bagging_pca_prediction > 0.5, 1, 0)
  
  cross_validation[9, i] <- calculate_correct_guesses(dt$bagging_pca_prediction, dt$Survived)
  
  # Prediction for boosting
  dt$boosting_pca_prediction <- predict(model_pca_ensemble_boosting, 
                                               newdata = dt)
  dt$boosting_pca_prediction <- ifelse(dt$boosting_pca_prediction > 0.5, 1, 0)
  
  cross_validation[10, i] <- calculate_correct_guesses(dt$boosting_pca_prediction, dt$Survived)
    
  
}
Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...

Using 107 trees...

Using 305 trees...
cross_validation$Mean_accuracy <- rowMeans(cross_validation)
cross_validation$Expected_Loss <- (1 - cross_validation$Mean_accuracy)
cross_validation[, c(11, 12)]

The table indicates Mean_accuracy and Expected_Loss for each model. Mean_accuracy is good example of representing the average accuracy of the model across all the 10 folds in the cross-validation process. In other words, it shows how well the model performs on average in predicting the survival rate. On the other hand, Expected_Loss indicates the proportion of misclassified instances. In short, the lower the expected loss, the better the performance of the model. It is clear that, after cross-validation we find that bagging without PCA seems to perform best, closely followed by bagging with PCA.

LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgTWFjaGluZSBMZWFybmluZyBQcm9qZWN0IC0gVGl0YW5pYyIKc3VidGl0bGU6ICJNYXJ0aW4tTHV0aGVyLVVuaXZlcnNpdHkgSGFsbGUtV2l0dGVuYmVyZyIKYXV0aG9yOiAiU2Nob2x6LCBDbGVtZW5zICYgU2lyLCBPbnVyIEFraW4iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZmlnX3dpZHRoOiAxMAogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBmaWdfd2lkdGg6IDEwCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDogeWVzCi0tLQpjbGVtZW5zLnNjaG9sekBzdHVkZW50LnVuaS1oYWxsZS5kZSA8YnI+CmFraW4uc2lyQHN0dWRlbnQudW5pLWhhbGxlLmRlCgojIyMjIyBIYWxsZSAoU2FhbGUpLCBKdWx5IDIwMjMuCgo8YnI+Cjxicj4KCjxzdHlsZT4KLm1hdGggewogIGZvbnQtc2l6ZTogc21hbGw7Cn0KPC9zdHlsZT4KCjxzY3JpcHQ+CiQoZG9jdW1lbnQpLnJlYWR5KGZ1bmN0aW9uKCkgewogICRpdGVtcyA9ICQoJ2RpdiNUT0MgbGknKTsKICAkaXRlbXMuZWFjaChmdW5jdGlvbihpZHgpIHsKICAgIG51bV91bCA9ICQodGhpcykucGFyZW50c1VudGlsKCcjVE9DJykubGVuZ3RoOwogICAgJCh0aGlzKS5jc3Moeyd0ZXh0LWluZGVudCc6IG51bV91bCAqIDEwLCAncGFkZGluZy1sZWZ0JzogMH0pOwogIH0pOwoKfSk7Cjwvc2NyaXB0PgoKPGJyPgo8YnI+CgoKYGBge3Igc2V0dXAsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CiMgSW1wb3J0aW5nIHRoZSBlc3NlbnRpYWwgbGlicmFyaWVzCiNpbnN0YWxsLnBhY2thZ2VzKGMoImdncGxvdDIiLCJqYW5pdG9yIiwgInRpYmJsZSIsICJsdWJyaWRhdGUiLCAiZGF0YS50YWJsZSIsICJ0aWR5ciIsICJkcGx5ciIsICJ4bHN4IiwgIm9wZW54bHN4IiwgImZpbGVzc3RyaW5ncyIsICJ0aWR5dmVyc2UiLCAiTUFTUyIsICJtbm9ybXQiLCAiZmFjdG9leHRyYSIsICJOYkNsdXN0IiwgInNjYXR0ZXJwbG90M2QiLCAicGxvdDNEIiwgInZpcmlkaXNMaXRlIiwgInJwYXJ0LnBsb3QiLCAicnBhcnQiLCAncmFuZG9tRm9yZXN0JywgJ2dibScsICJ0ZW5zb3JmbG93IiwgImtlcmFzIiwgInBhdGNod29yayIsICJyYW5kb21Gb3Jlc3QiLCAiZ2JtIikpCiNyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZ3JhbnRtY2Rlcm1vdHQvcGFydHRyZWUiKQoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLT4gTk9URSA8LS0tLS0tLS0tLS0tLS0tLS0tLSAjCiMgSWYgeW91IGVuY291bnRlciBhbiBlcnJvciB3aGVuIGV4ZWN1dGluZyB0aGUgTmV1cmFsIE5ldHdvcmtzIHNlY3Rpb24uIFdlIHN1Z2dlc3QgaW5zdGFsbGluZyB0aGUgZm9sbG93aW5nIHBhY2thZ2VzIGFuZCByZXN0YXJ0aW5nIFIgU3R1ZGlvOgoKIyBpbnN0YWxsLnBhY2thZ2VzKCJyZW1vdGVzIikKIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YihwYXN0ZTAoInJzdHVkaW8vIiwgYygicmV0aWN1bGF0ZSIsICJ0ZW5zb3JmbG93IiwgImtlcmFzIikpKQojIHJldGljdWxhdGU6Omluc3RhbGxfbWluaWNvbmRhKCkgIyBza2lwIHRoaXMgaWYgeW91IHdhbnQgdG8gc2VsZi1pbnN0YWxsIGNvbmRhIG9yIHVzZSB2ZW52CiMga2VyYXM6Omluc3RhbGxfa2VyYXMoKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIwoKCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShwbG90M0QpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KHZpcmlkaXNMaXRlKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeShnYm0pCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeSh0ZW5zb3JmbG93KQpsaWJyYXJ5KHBhcnR0cmVlKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShOYkNsdXN0KQpsaWJyYXJ5KHJlYWR4bCkgICAgCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkoZmlsZXNzdHJpbmdzKQpsaWJyYXJ5KHNjYXR0ZXJwbG90M2QpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2djb3JycGxvdCkKbGlicmFyeShnZ0V4dHJhKQpsaWJyYXJ5KGtlcmFzKQpsaWJyYXJ5KGdncmlkZ2VzKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkobW5vcm10KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKCnJtKGxpc3Q9bHMoKSkKCmBgYAoKYGBge3J9CiMgQWRkIHBhdGhzIHRvIHRoZSBkaWZmZXJlbnQgZGF0YSBzZXRzCgojIHBhdGggZm9yIHRyYWluaW5nIGRhdGEgc2V0CnBhdGggPC0gIi9Vc2Vycy9jbGVtZW5zc2Nob2xsZS9EZXNrdG9wL01hc3Rlci9TdGF0aXN0aWNhbF9NYWNoaW5lX0xlYXJuaW5nL1Byb2plY3QvdGl0YW5pYy90cmFpbi5jc3YiCiMgcGF0aCBmb3IgdGVzdCBkYXRhIHNldApwYXRoMSA8LSAiL1VzZXJzL2NsZW1lbnNzY2hvbGxlL0Rlc2t0b3AvTWFzdGVyL1N0YXRpc3RpY2FsX01hY2hpbmVfTGVhcm5pbmcvUHJvamVjdC90aXRhbmljL3Rlc3QuY3N2IgoKIyBwYXRoIGZvciB0aGUgY29ycmVzcG9uZGluZyBkZXBlbmRlbnQgdmFyaWFibGVzIGZvciB0aGUgdGVzdCBkYXRhIHNldCAKcGF0aDIgPC0gIi9Vc2Vycy9jbGVtZW5zc2Nob2xsZS9EZXNrdG9wL01hc3Rlci9TdGF0aXN0aWNhbF9NYWNoaW5lX0xlYXJuaW5nL1Byb2plY3QvdGl0YW5pYy9yZXN1bHQuY3N2IgpgYGAKCgo8YnI+Cjxicj4KCiMgRGF0YSBEaWN0aW9uYXJ5CmBgYHtyfQogIyMgVmFyaWFibGUgICAgICAgICAgICAgICMjIERlZmluaXRpb24gICAgICAgICAgICAgICAjIyBLZXkKCiAjIHN1cnZpdmVkICAgICAgICAgICAgICAgIyBTdXJ2aXZhbCAgICAgICAgICAgICAgICAgIyAwID0gTm8sIDEgPSBZZXMKICMgcGNsYXNzICAgICAgICAgICAgICAgICAjIFRpY2tldCBjbGFzcyAgICAgICAgICAgICAjIDEsIDIsIDMgKGUuZy4gMSBmb3IgZmlyc3QgY2xhc3MuKQogIyBzZXggICAgICAgICAgICAgICAgICAgICMgU2V4ICAgICAgICAgICAgICAgICAgICAgICMgMCA9IEZlbWFsZSwgMSA9IE1hbGUKICMgQWdlICAgICAgICAgICAgICAgICAgICAjIEFnZSBpbiB5ZWFycyAgICAgICAgICAgICAgIAogIyBTaWJzcCAgICAgICAgICAgICAgICAgICMgWyNdIG9mIHNpYmxpbmdzIC8gc3BvdXNlcyBhYnJvYWQgdGhlIFRpdGFuaWMgKEJyb3RoZXIsIHNpc3Rlci4uLiAmIGh1c2JhbmQsIHdpZmUpCiAjIHBhcmNoICAgICAgICAgICAgICAgICAgIyBbI10gb2YgcGFyZW50cyAvIGNoaWxkcmVuIGFicm9hZCB0aGUgVGl0YW5pYyAobW90aGVyLCBmYXRoZXIsIGRhdWdoZXIsIHNvbi4uLikKICMgZmFyZSAgICAgICAgICAgICAgICAgICAjIFBhc3NlbmdlciBmYXJlCiAjIGVtYmFya2VkICAgICAgICAgICAgICAgIyBQb3J0IG9mIEVtYmFya2F0aW9uICAgICAgIyBDID0gQ2hlcmJvdXJnLCBRID0gUXVlZW5zdG93biwgUyA9IFNvdXRoYW1wdG9uCgpgYGAKCkl0IGlzIGltcG9ydGFudCB0byBhY2tub3dsZWRnZSB0aGF0IHRoZSB2YXJpYWJsZSBjYWxsZWQgYFBhc3NlbmdlciBJRGAgd2FzIG5vdCB1c2VmdWwgZm9yIG91ciBhbmFseXNpcy4gV2UsIHRoZXJlZm9yZSwgZHJvcHBlZCB0aGlzIGNvbHVtbi4KTmV4dCwgd2UgYXNzaWduZWQgYSBiaW5hcnkgdmFyaWFibGUocykgZm9yIGBTdXJ2aXZlZGAgdmFyaWFibGUsIHdoaWNoIGlzIGJldHRlciBpbGx1c3RyYXRpb24gZm9yIHNob3dpbmcgaWYgdGhlIHBlcnNvbiBhbGl2ZSAoMSkgb3IgZGVhZCAoMCkuIFdoZW4gaXQgY29tZXMgdG8gdGhlIGdyYXBoaWNhbCBpbGx1c3RyYXRpb24gd2UgZGVjaWRlZCB0byBjaG9vc2UgImBkZWVwc2t5Ymx1ZTIiYCBjb2xvdXIgZm9yIHRoZSBwZXJzb24gd2hvIHN1cnZpdmVkLCBgImJsYWNrImAgb3RoZXJ3aXNlLgoKVGhlIGBGYXJlYCB2YXJpYWJsZSBpbmRpY2F0ZXMgdGhlIHBhc3NlbmdlciBmYXJlIHRoZSBwZXJzb24gcGFpZC4gQmVpbmcgYWJsZSB0byBwYXkgaGlnaGVyIHByaWNlcyBtYXkgYmUgZ29vZCBpbGx1c3RyYXRpb24gb2YgdGhlIHdlYWx0aCBvZiBwYXNzZW5nZXIuCgpUaGVyZSBhcmUgdGhyZWUgZGlmZmVyZW50IGNsYXNzZXMgb24gdGhlIHNoaXAsIHdoaWNoIHdlcmUgaW5kaWNhdGVkIGFzIDEsIDIgYW5kIDMuIFdlIGNyZWF0ZWQgc2VwYXJhdGUgY29sdW1ucyBmb3IgZWFjaCBjbGFzcyBpbiBvcmRlciB0byBhc3NpZ24gYSBiaW5hcnkgdmFyaWFibGUgZm9yIGVhY2ggY2xhc3MuIFRodXMsIHdlIHdpbGwgYmUgYWJsZSB0byBhbmFseXNlIHRoZW0uIEZvciBleGFtcGxlLCAxIGlmIHRoZSBwYXNzZW5nZXIgaXMgaW4gdGhlIGZpcnN0IGNsYXNzLCAwIG90aGVyd2lzZS4gSXQgaXMgY2xlYXIgdGhhdCB0aGUgdmFyaWFibGUgYFBjbGFzc2AgaXMgYWxzbyBlc3NlbnRpYWwgZm9yIG91ciBhbmFseXNpcy4gSXQgbWlnaHQgYmUgYSBnb29kIGlsbHVzdHJhdGlvbiBvZiB0aGUgd2VhbHRoIG9mIHRoZSBwYXNzZW5nZXJzIHdobyB3ZXJlIGFibGUgdG8gYWZmb3JkIGZpcnN0IGNsYXNzLiBUaHVzLCBpdCBpcyBoaWdobHkgcmVsZXZhbnQgdG8gdGhlIGBGYXJlYCB2YXJpYWJsZS4KUmVhc29uYWJseSwgYEZpcnN0Q2xhc3NgIHBhc3NlbmdlcnMgaGFkIG1vcmUgbHV4dXJpZXMgYXZhaWxhYmxlIHRvIHRoZW0uIE1vc3Qgb2YgZmlyc3QgY2xhc3Mgd2FzIGxvY2F0ZWQgaW4gdGhlIG1pZGRsZSBodWxsIGFuZCBpbiB0aGUgdXBwZXIgc3RydWN0dXJlcy4gVGhlcmUgd2VyZSB0d28gc3RhaXJjYXNlcyBjb25uZWN0aW5nIHRoZSBkaWZmZXJlbnQgZGVja3MuIGBTZWNvbmRDbGFzc2Agd2FzIGRpc3RyaWJ1dGVkIGFsb25nIHRoZSBoZWlnaHQgaW4gdGhlIG1pZGRsZSBvZiB0aGUgc2hpcCB3aXRoIG9ubHkgb25lIHN0YWlyY2FzZSBjb25uZWN0aW5nIHRoZSBkaWZmZXJlbnQgZGVja3MuIEFtZW5pdGllcyB0aGVyZSB3b3VsZCBiZSB0aGUgc2FtZSBhcyBmb3IgdGhlIGZpcnN0IGNsYXNzIG9mIG90aGVyIHNoaXBzIG9mIHRoZSB0aW1lLiBUaGUgc29sZSBzdGFpcmNhc2UgbWF5IGhhdmUgaW1wZWRlZCBldmFjdWF0aW9uIGR1cmluZyB0aGUgY3Jhc2guIGBUaGlyZENsYXNzYCB3YXMgbG9jYXRlZCBvbiB0aGUgZGVlcGVyIHBhcnRzIG9mIHRoZSBzaGlwIGFzIHdlbGwgYXMgcGFydHMgZGlyZWN0bHkgb24gdGhlIGJvdyBhbmQgdGhlIHJlYXIgb2YgdGhlIHNoaXAuIFRoZSBsb2NhdGlvbiBvZiB0aGUgY2FiaW5zIHdvdWxkIGhhdmUgb2J2aW91cyBkaXNhZHZhbnRhZ2VzIGR1cmluZyB0aGUgY3Jhc2guCgpJbiBhZGRpdGlvbiwgd2UgYXNzaWduZWQgYSBiaW5hcnkgdmFyaWFibGUgZm9yIGRldGVybWluaW5nIHRoZSBnZW5kZXIgb2YgdGhlIG9ic2VydmVkIHBhc3NlbmdlcnMuIFRoZXJlZm9yZSwgYFNleGAgZXF1YWxzIDEgaWYgdGhlIHBhc3NlbmdlciBpcyBhIG1hbiBhbmQgMCBvdGhlcndpc2UuIEl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgZHVyaW5nIGV2YWN1YXRpb24gd29tZW4gd2VyZSBnaXZlbiBwcmlvcml0eSwgd2hpbHN0IG1lbiBtaWdodCBoYXZlIGhpZ2hlciBjaGFuY2VzIG9mIHN1cnZpdmFsIGR1ZSB0byB0aGVpciBwaHlzaXF1ZS4gQ2hpbGRyZW4gd2VyZSBwcmlvcml0aXplZCBkdXJpbmcgZXZhY3VhdGlvbiBidXQgdGhleSB3b3VsZCBhbHNvIGJlIGxlc3MgbGlrZWx5IHRvIHN1cnZpdmUgZHVlIHRvIHRoZWlyIGxlc3NlciBzdHJlbmd0aCBjb21wYXJlZCB0byBhZHVsdHMuIE9sZGVyIHBlb3BsZSBtYXkgZXhoaWJpdCB0aGUgc2FtZSBpbXBlZGltZW50IHRvIGEgaGlnaGVyIGRlZ3JlZS4KCldoZW4gaXQgY29tZXMgdG8gdGhlIGBBZ2VgIHZhcmlhYmxlLCBpdCBpbmRpY2F0ZXMgdGhlIGFnZSBvZiB0aGUgcGFzc2VuZ2VyLiBVbmZvcnR1bmF0ZWx5LCB0aGUgYEFnZWAgdmFyaWFibGUgd2FzIG5vdCBhdmFpbGFibGUgZm9yIGFsbCBjYXNlcyBvZiB0aGlzIGRhdGEgc2V0LiBXZSwgdGhlcmVmb3JlLCBkZWNpZGVkIHRvIGRyb3AgdGhlc2UgbWlzc2luZyBjYXNlcyBpbiB0aGUgY2xlYW5pbmcgcHJvY2Vzcy4KClRoZSB2YXJpYWJsZXMgYFNpYlNwYCBhbmQgYFBhcmNoYCB3ZXJlIHJlbGF0ZWQgdG8gdGhlaXIgZmFtaWx5IGJvbmRzLiBUaGUgYFNpYlNwYCB2YXJpYWJsZSBzaG93aW5nIGhvdyBtYW55IHNpYmxpbmdzIG9yIHNwb3VzZXMgd2VyZSB3aXRoIHRoZSBwZXJzb24gYWJvYXJkIHRoZSBzaGlwLiBXZSBhc3N1bWVkIHRoYXQgaGlnaGVyIG51bWJlcnMgbWF5IG1lYW4gdGhhdCBtb3JlIHBlb3BsZSBhc3Npc3RlZCB0aGUgcGVyc29uIGR1cmluZyBldmFjdWF0aW9uIG9yIHRoYXQgdGhleSBtYXkgaGF2ZSBzcGVuZCB2YWx1YWJsZSB0aW1lIGR1cmluZyBldmFjdWF0aW9uIGxvb2tpbmcgZm9yIHJlbGF0aXZlcy4gVGhlIGBQYXJjaGAgdmFyaWFibGUgc2hvd2luZyBob3cgbWFueSBwYXJlbnRzIG9yIGNoaWxkcmVuIHdlcmUgd2l0aCB0aGUgcGVyc29uIGFib2FyZCB0aGUgc2hpcC4gSW4gY2FzZSBvZiB5b3VuZyBjaGlsZHJlbiB0aGlzIHZhcmlhYmxlIG1heSBpbmNyZWFzZSBzdXJ2aXZhbCBhcyBwYXJlbnRzIHdpbGwgdHJ5IHRvIHNhdmUgdGhlaXIgY2hpbGRyZW4uIFdlIGFsc28gYXNzdW1lZCB0aGF0IGluIGNhc2Ugb2YgcGFyZW50cyB0aGlzIG1heSBsb3dlciBjaGFuY2VzIG9mIHN1cnZpdmFsIGluIGNhc2UgdGhleSB3ZXJlIGxvb2tpbmcgZm9yIHRoZWlyIGNoaWxkcmVuIG9yIGdhdmUgdXAgc2VhdHMgb24gYSBsaWZlYm9hdCBmb3IgdGhlbSBvciBpdCBtYXkgaW1wcm92ZSBzdXJ2aXZhbCBjaGFuY2VzIGFzIHRoZXkgd291bGQgZ2FpbiBhIHNlYXQgb24gYSBsaWZlYm9hdCB0b2dldGhlciB3aXRoIHRoZWlyIGNoaWxkcmVuLgoKRmluYWxseSwgdGhlIGBFbWJhcmtlZGAgdmFyaWFibGUgaW5kaWNhdGVzIHRoZSBwb3J0IG9mIGVtYmFya2F0aW9uIG9mIHRoZSBwYXNzZW5nZXIuIFRoZXJlIHdlcmUgdGhyZWUgZGlmZmVyZW50IHBvcnRzIG9mIGVtYmFya2F0aW9uIGF2YWlsYWJsZSwgdGhhdCB3YXM6IFNvdXRoaGFtcHRvbiAoUyksIENoZXJib3VyZyAoQyksIGFuZCBRdWVlbnN0b3duIChRKS4gU2ltaWxhcmx5LCB3ZSBjcmVhdGVkIHNlcGFyYXRlIGNvbHVtbnMgZm9yIGVhY2ggcG9ydCBvZiBlbWJhcmthdGlvbiBvZiB0aGUgcGFzc2VuZ2VycyBpbiBvcmRlciB0byBhc3NpZ24gYSBiaW5hcnkgdmFyaWFibGUgZm9yIGVhY2ggb2YgdGhlbS4gRnVyaGVybW9yZSwgd2UgYXNzdW1lZCB0aGF0IGVtYmFya2luZyBlYXJsaWVyIG9mZmVycyB0aGUgcGFzc2VuZ2VyIG1vcmUgdGltZSBhbmQgb3Bwb3J0dW5pdHkgdG8gZXhwbG9yZSB0aGUgc2hpcCwga25vdyB0aGVpciBzdXJyb3VuZGluZ3MgYW5kIGdldCB1c2VkIHRvIHNlYSB0cmF2ZWwgYW5kIHBvc3NpYmxlIHNlYSBzaWNrbmVzcy4gVGhpcyBmYWN0b3IgbWF5IGJlIGNvbXBvdW5kZWQgYnkgZWFybGllciBwYXNzZW5nZXJzIGhhdmluZyBhIGxlc3MgcG9wdWxhdGVkIHNoaXAgdG8gZXhwbG9yZS4KCgpgYGB7cn0KIyBJbXBvcnRpbmcgdGhlIGRhdGFzZXQgYW5kIHByZXBhcmluZyBkYXRhIHRvIGFuYWx5c2lzCgpjbGVhbl9kYXRhIDwtIGZ1bmN0aW9uKHJhd19kYXRhKSB7CiAgIyBGaW5kIE5BIHZhbHVlcyBpbiBBZ2UgYW5kIEVtYmFya2VkCiAgcHJpbnQocGFzdGUwKCJUaGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGluIEFnZSB2YXJpYWJsZXM6ICIsIHN1bShpcy5uYShyYXdfZGF0YSRBZ2UpKSkpCiAgcHJpbnQocGFzdGUwKCJUaGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGluIEVtYmFya2VkIHZhcmlhYmxlczogIiwgc3VtKGlzLm5hKHJhd19kYXRhJEVtYmFya2VkKSkpKQogIHByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBpbiBGYXJlIHZhcmlhYmxlczogIiwgc3VtKGlzLm5hKHJhd19kYXRhJEZhcmUpKSkpCiAgCiAgCiAgIyBEZWxldGUgcm93cyB3aXRoIE5BIHZhbHVlcwogIHJhd19kYXRhIDwtIHJhd19kYXRhW2NvbXBsZXRlLmNhc2VzKHJhd19kYXRhJEFnZSksIF0KICByYXdfZGF0YSA8LSByYXdfZGF0YVtjb21wbGV0ZS5jYXNlcyhyYXdfZGF0YSRFbWJhcmtlZCksIF0gIAogIHJhd19kYXRhIDwtIHJhd19kYXRhW2NvbXBsZXRlLmNhc2VzKHJhd19kYXRhJEZhcmUpLCBdICAKICAKICAjIENvbnZlcnRpbmcgU2V4IHZhcmlhYmxlIGFzIGJvb2xlYW4gdHlwZS4KICByYXdfZGF0YSRTZXggPC0gaWZlbHNlKHJhd19kYXRhJFNleCA9PSAibWFsZSIsIDEsIDApCiAgCiAgIyBQdXR0aW5nIGR1bW1pZXMgZm9yIG91ciB2YXJpYWJsZXMgd2hpY2ggYXJlIFBjbGFzcyBhbmQgRW1iYXJrZWQgKHRodXMsIHdlIHdpbGwgYmUgYWJsZSB0byBhbmFseXNlIHRoZW0gaW4gbGF0ZXIgcHJvY2VzcykKICAjIEJhc2ljYWxseSB3aGF0IHdlIGRvIGlzIGNyZWF0aW5nIGEgbmV3IGNvbHVtbiBmb3IgYWxsIEVtYmFya2VkIGRlc3Rpb25hdGlvbnMgU291dGhoYW1wdG9wbiwgQ2hlcmJvdXJnIGFuZCBRdWVlbnN0b3duIHdpdGggYmluYXJ5IHZhbHVlcy4gRm9yIGV4YW1wbGUsIGluIFNvdXRoaGFtcHRvbiAxIGlmICJFbWJhcmtlZCIgaXMgIlMiLCAwIG90aGVyd2lzZQogIHJhd19kYXRhJFNvdXRoaGFtcHRvbiA8LSBpZmVsc2UocmF3X2RhdGEkRW1iYXJrZWQgPT0gIlMiLCAxLCAwKQogIHJhd19kYXRhJENoZXJib3VyZyA8LSBpZmVsc2UocmF3X2RhdGEkRW1iYXJrZWQgPT0gIkMiLCAxLCAwKQogIHJhd19kYXRhJFF1ZWVuc3Rvd24gPC0gaWZlbHNlKHJhd19kYXRhJEVtYmFya2VkID09ICJRIiwgMSwgMCkKICAKICAjIExldCB1cyBkbyB0aGUgc2FtZSBwcm9jZXNzIGZvciB0aGUgUGNsYXNzIHZhcmlhYmxlOgogIHJhd19kYXRhJEZpcnN0Q2xhc3MgPC0gaWZlbHNlKHJhd19kYXRhJFBjbGFzcyA9PSAiMSIsIDEsIDApCiAgcmF3X2RhdGEkU2Vjb25kQ2xhc3MgPC0gaWZlbHNlKHJhd19kYXRhJFBjbGFzcyA9PSAiMiIsIDEsIDApCiAgcmF3X2RhdGEkVGhpcmRDbGFzcyA8LSBpZmVsc2UocmF3X2RhdGEkUGNsYXNzID09ICIzIiwgMSwgMCkgCiAgCiAgIyBDcmVhdGluZyBuZXcgZGF0YSBmcmFtZSBmb3IgY2xlYW5lZCBkYXRhCiAgY2xlYW5fZGF0YSA8LSBkYXRhLmZyYW1lKE5hbWUgPSByYXdfZGF0YSROYW1lLCBTZXggPSByYXdfZGF0YSRTZXgsIEFnZSA9IHJhd19kYXRhJEFnZSwgU2lic3AgPSByYXdfZGF0YSRTaWJTcCwgUGFyY2ggPSByYXdfZGF0YSRQYXJjaCwgRmlyc3RDbGFzcyA9IHJhd19kYXRhJEZpcnN0Q2xhc3MsIFNlY29uZENsYXNzID0gcmF3X2RhdGEkU2Vjb25kQ2xhc3MsIFRoaXJkQ2xhc3MgPSByYXdfZGF0YSRUaGlyZENsYXNzICxTb3V0aGhhbXB0b24gPSByYXdfZGF0YSRTb3V0aGhhbXB0b24sIENoZXJib3VyZyA9IHJhd19kYXRhJENoZXJib3VyZywgUXVlZW5zdG93biA9IHJhd19kYXRhJFF1ZWVuc3Rvd24sIEZhcmUgPSByYXdfZGF0YSRGYXJlLCBTdXJ2aXZlZCA9IHJhd19kYXRhJFN1cnZpdmVkLCBFbWJhcmtlZCA9IHJhd19kYXRhJEVtYmFya2VkLCBQY2xhc3MgPSByYXdfZGF0YSRQY2xhc3MpCiAgCiAgcmV0dXJuKGNsZWFuX2RhdGEpCiAgCn0KCnJhd19kYXRhIDwtIHJlYWRfY3N2KHBhdGgpCgpkYXRhIDwtIGNsZWFuX2RhdGEocmF3X2RhdGEpCgpkYXRhCgojIENsZWFuIHVwCnJtKGxpc3QgPSBjKCJyYXdfZGF0YSIpKQpgYGAKCjxicj4KCiMjIEJhc2ljIGNhbGN1bGF0aW9ucyBvZiB0aGUgZGF0YXNldApgYGB7cn0KIyBMZXQncyBpbGx1c3RyYXRlIHRoZSBiYXNpYyBjYWxjdWxhdGlvbnMKdG90YWxfcGFzc2VuZ2VyIDwtIGxlbmd0aChkYXRhJFNleCkKdG90YWxfZmVtYWxlIDwtIHN1bShkYXRhJFNleCA9PSAwKQp0b3RhbF9tYWxlIDwtIHN1bShkYXRhJFNleCA9PSAxKQp0b3RhbF9zdXJ2aXZhbCA8LSBzdW0oZGF0YSRTdXJ2aXZlZCA9PSAxKQp0b3RhbF9mZW1hbGVfc3Vydml2YWwgPC0gc3VtKGRhdGEkU2V4ID09IDAgJiBkYXRhJFN1cnZpdmVkID09IDEpCnRvdGFsX21hbGVfc3Vydml2YWwgPC0gc3VtKGRhdGEkU2V4ID09IDEgJiBkYXRhJFN1cnZpdmVkID09IDEpCgojIyBDbGFzcyBjYWxjdWxhdGlvbnMgIyMKdG90YWxfcGFzc18xX2NsYXNzIDwtIHN1bShkYXRhJFBjbGFzcyA9PSAxKQp0b3RhbF9wYXNzXzFfY2xhc3Nfc3Vydml2ZWQgPC0gc3VtKGRhdGEkUGNsYXNzID09IDEgJiBkYXRhJFN1cnZpdmVkID09IDEpCnRvdGFsX3Bhc3NfMl9jbGFzcyA8LSBzdW0oZGF0YSRQY2xhc3MgPT0gMikKdG90YWxfcGFzc18yX2NsYXNzX3N1cnZpdmVkIDwtIHN1bShkYXRhJFBjbGFzcyA9PSAyICYgZGF0YSRTdXJ2aXZlZCA9PSAxKQp0b3RhbF9wYXNzXzNfY2xhc3MgPC0gc3VtKGRhdGEkUGNsYXNzID09IDMpCnRvdGFsX3Bhc3NfM19jbGFzc19zdXJ2aXZlZCA8LSBzdW0oZGF0YSRQY2xhc3MgPT0gMyAmIGRhdGEkU3Vydml2ZWQgPT0gMSkKIyMgQ2xhc3MgY2FsY3VsYXRpb24gZW5kcyAjIwoKYGBgCgoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoeCA9IGZhY3RvcihTdXJ2aXZlZCksIGZpbGwgPSBmYWN0b3IoU2V4KSkpICsKICBnZW9tX2JhcigpICsKICBsYWJzKAogICAgdGl0bGUgPSAiTnVtYmVyIG9mIGZlbWFsZSBhbmQgbWFsZSBwYXNzZW5nZXJzIHN1cnZpdmVkIiwKICAgIGNhcHRpb24gPSAiU2V4OiAwID0gRmVtYWxlLCAxID0gTWFsZTsgXG4gU3Vydml2ZWQ6IDAgPSBObywgMSA9IFllcyIsCiAgICB4ID0gIk51bWJlciBvZiBwYXNzZW5nZXJzIHN1cnZpdmVkIiwKICAgIHkgPSAiQ291bnQiLAogICAgZmlsbCA9ICJTZXgiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiMCIgPSAib3JjaGlkMyIsICIxIiA9ICJza3libHVlMyIpKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiMwMDAwMDAiKSkKCiMjIEdlbmVyYWwgaW5mb3JtYXRpb24gIyMKc2VjdGlvbjFfZ2VuX2luZm9fMSA8LSBwYXN0ZTAoIlRoZSB0b3RhbCBudW1iZXIgb2YgcGFzc2VuZ2VyczogIiwgdG90YWxfcGFzc2VuZ2VyKQpzZWN0aW9uMV9nZW5faW5mb18yIDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBmZW1hbGUgcGFzc2VuZ2VyczogIiwgdG90YWxfZmVtYWxlKQpzZWN0aW9uMV9nZW5faW5mb18zIDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBtYWxlIHBhc3NlbmdlcnM6ICIsIHRvdGFsX21hbGUpCnNlY3Rpb24xX2dlbl9pbmZvXzQgPC0gcGFzdGUwKCJUaGUgdG90YWwgbnVtYmVyIG9mIHBhc3NlbmdlcnMgd2hvIHN1cnZpdmVkOiAiLCB0b3RhbF9zdXJ2aXZhbCkKc2VjdGlvbjFfZ2VuX2luZm9fNSA8LSBwYXN0ZTAoIlRoZSB0b3RhbCBudW1iZXIgb2YgZmVtYWxlIHBhc3NlbmdlcnMgd2hvIHN1cnZpdmVkOiAiLCB0b3RhbF9mZW1hbGVfc3Vydml2YWwpCnNlY3Rpb24xX2dlbl9pbmZvXzYgPC0gcGFzdGUwKCJUaGUgdG90YWwgbnVtYmVyIG9mIG1hbGUgcGFzc2VuZ2VycyB3aG8gc3Vydml2ZWQ6ICIsIHRvdGFsX21hbGVfc3Vydml2YWwpCiMjIEdlbmVyYWwgaW5mb3JtYXRpb24gZW5kcyAjIwoKZm9yIChpIGluIDE6NikgewogIHZhcmlhYmxlX25hbWUgPC0gcGFzdGUwKCJzZWN0aW9uMV9nZW5faW5mb18iLCBpKQogIHZhcmlhYmxlX3ZhbHVlIDwtIGdldCh2YXJpYWJsZV9uYW1lKQogIHByaW50KHZhcmlhYmxlX3ZhbHVlKQp9CgoKCiMgUGxvdHRpbmcgdGhlIGFnZSBkaXN0cmlidXRpb24gdGhvdWdoIGhpc3RvZ3JhbS4gV2UgYWxzbyB3YW50ZWQgdG8gaWxsdXN0cmF0ZSB0aGUgZXN0aW1hdGUgb2YgdGhlIGRlbnNpdHkuCmdncGxvdChkYXRhID0gZGF0YSwgYWVzKHg9QWdlKSkgKwogIGdlb21faGlzdG9ncmFtKCBhZXMoeT0uLmRlbnNpdHkuLikpICsKICBnZW9tX2RlbnNpdHkoZmlsbD0iI2YxYjE0NyIsIGNvbG9yPSIjZjFiMTQ3IiwgYWxwaGE9MC4xMSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJBZ2UgZGlzdHJpYnV0aW9uIiwKICAgIHggPSAiQWdlIiwKICAgIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCgojIElsbHVzdHJhdGluZyB0aGUgbnVtYmVyIG9mIGFnZSBncm91cCBhbmQgdGhlaXIgZmFyZS4gV2UgYWxzbyB3b3VsZCBsaWtlIHRvIGlsbHVzdHJhdGUgc3Vydml2YWwgcmF0ZSBhcyBhIGNvbG9yIGFuZCBwYXJjaCBhcyBhIHNpemUuCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhLCBhZXMoeCA9IEFnZSwgeSA9IEZhcmUsIGNvbG9yID0gZmFjdG9yKFN1cnZpdmVkKSwgc2l6ZSA9IFBhcmNoKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJkZWVwc2t5Ymx1ZTMiKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJBZ2UgJiBGYXJlICYgRmFtaWxpYWwgcmVsYXRpb25zICYgU3Vydml2YWwiLAogICAgeCA9ICJBZ2UiLAogICAgeSA9ICJGYXJlIiwKICAgIGNvbG9yID0gIlN1cnZpdmFsIikgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiMwMDAwMDAiKSkKCgpzY2F0dGVyM0QoZGF0YSRBZ2UsIGRhdGEkRmFyZSwgZGF0YSRQY2xhc3MsIAogICAgICAgIHRoZXRhPTIyLCBwaGk9MjksIAogICAgICAgIHhsYWI9IkFnZSBvZiBwYXNzZW5nZXJzIiwgeWxhYj0iRmFyZSIsIHpsYWI9IlRpY2tldCBjbGFzc2VzIG9mIHBhc3NlbmdlcnMiLAogICAgICAgIHRpY2t0eXBlPSJkZXRhaWxlZCIsCiAgICAgICAgcGNoID0gMjAsCiAgICAgICAgY2V4LmF4aXM9MC40NSwgY2V4LmxhYj0wLjk1LCBleHBhbmQ9MC43MywgCiAgICAgICAgY29sdmFyID0gZGF0YSRTdXJ2aXZlZCwKICAgICAgICBjb2wgPSBjKCJibGFjayIsICJkZWVwc2t5Ymx1ZTMiKSkKCiMgSWxsdXN0cmF0aW5nIHNjYXR0ZXIgcGxvdCBvZiBTdXJ2aXZhbCB2cy4gQWdlIHdpdGggY29sb3IgYnkgU2V4CmdncGxvdChkYXRhID0gZGF0YSwgYWVzKHggPSBBZ2UsIHkgPSBmYWN0b3IoU3Vydml2ZWQpLCBjb2xvciA9IGZhY3RvcihTZXgpKSkgKwogIGdlb21faml0dGVyKHdpZHRoID0gMC4zLCBoZWlnaHQgPSAwLjMpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHN1cnZpdmFsIGJ5IGFnZSBhbmQgZ2VuZGVyIiwKICAgIGNhcHRpb24gPSAiU2V4OiAwID0gRmVtYWxlLCAxID0gTWFsZTsgXG4gU3Vydml2ZWQ6IDAgPSBObywgMSA9IFllcyIsCiAgICB4ID0gIkFnZSBvZiBwYXNzZW5nZXJzIiwKICAgIHkgPSAiU3Vydml2aW5nIHBhc3NlbmdlcnMiLAogICAgY29sb3IgPSAiU2V4IgogICkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCgpnZ3Bsb3QoZGF0YSA9IGRhdGEsIGFlcyh4ID0gIiIsIHkgPSBQY2xhc3MsIGZpbGwgPSBmYWN0b3IoUGNsYXNzKSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICBsYWJzKAogICAgdGl0bGUgPSAiTnVtYmVyIG9mIHBhc3NlbmdlcnMgZnJvbSBlYWNoIGNsYXNzZXMiLAogICAgZmlsbCA9ICJDbGFzcyIKICApICsKICB0aGVtZV92b2lkKCkKCgojIyBDbGFzcyBpbmZvcm1hdGlvbiAjIwpzZWN0aW9uMV9nZW5fY2xhc3NfaW5mb18xIDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBwYXNzZW5nZXJzIGZyb20gZmlyc3QgY2xhc3M6ICIsIHRvdGFsX3Bhc3NfMV9jbGFzcykKc2VjdGlvbjFfZ2VuX2NsYXNzX2luZm9fMiA8LSBwYXN0ZTAoIlRoZSB0b3RhbCBudW1iZXIgb2YgcGFzc2VuZ2VycyBmcm9tIGZpcnN0IGNsYXNzIHdobyBzdXJ2aXZlZDogIiwgdG90YWxfcGFzc18xX2NsYXNzX3N1cnZpdmVkKQpzZWN0aW9uMV9nZW5fY2xhc3NfaW5mb18zIDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBwYXNzZW5nZXJzIGZyb20gc2Vjb25kIGNsYXNzOiAiLCB0b3RhbF9wYXNzXzJfY2xhc3MpCnNlY3Rpb24xX2dlbl9jbGFzc19pbmZvXzQgPC0gcGFzdGUwKCJUaGUgdG90YWwgbnVtYmVyIG9mIHBhc3NlbmdlcnMgZnJvbSBzZWNvbmQgY2xhc3Mgd2hvIHN1cnZpdmVkOiAiLCB0b3RhbF9wYXNzXzJfY2xhc3Nfc3Vydml2ZWQpCnNlY3Rpb24xX2dlbl9jbGFzc19pbmZvXzUgPC0gcGFzdGUwKCJUaGUgdG90YWwgbnVtYmVyIG9mIHBhc3NlbmdlcnMgZnJvbSB0aGlyZCBjbGFzczogIiwgdG90YWxfcGFzc18zX2NsYXNzKQpzZWN0aW9uMV9nZW5fY2xhc3NfaW5mb182IDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBwYXNzZW5nZXJzIGZyb20gdGhpcmQgY2xhc3Mgd2hvIHN1cnZpdmVkOiAiLCB0b3RhbF9wYXNzXzNfY2xhc3Nfc3Vydml2ZWQpCiMjIENsYXNzIGluZm9ybWF0aW9uIGVuZHMgIyMKCmZvciAoaSBpbiAxOjYpIHsKICB2YXJpYWJsZV9uYW1lIDwtIHBhc3RlMCgic2VjdGlvbjFfZ2VuX2NsYXNzX2luZm9fIiwgaSkKICB2YXJpYWJsZV92YWx1ZSA8LSBnZXQodmFyaWFibGVfbmFtZSkKICBwcmludCh2YXJpYWJsZV92YWx1ZSkKfQoKCiMgU2NhdHRlciBwbG90IG9mIFNleCB2cy4gUGNsYXNzIHdpdGggY29sb3IgYnkgU3Vydml2ZWQKZ2dwbG90KGRhdGEgPSBkYXRhLCBhZXMoeCA9IGZhY3RvcihQY2xhc3MpLCB5ID0gZmFjdG9yKFN1cnZpdmVkKSwgY29sb3IgPSBmYWN0b3IoU2V4KSkpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMywgaGVpZ2h0ID0gMC4zKSArCiAgbGFicygKICAgIHRpdGxlID0gIlNjYXR0ZXIgcGxvdCBvZiBnZW5kZXIgYW5kIGNsYXNzIGNvbG91cmVkIGJ5IHN1cnZpdmFsIiwKICAgIGNhcHRpb24gPSAiU2V4OiAwID0gRmVtYWxlLCAxID0gTWFsZTsgXG4gU3Vydml2ZWQ6IDAgPSBObywgMSA9IFllcyBcbiAxID0gZmlyc3QgY2xhc3MsIDIgPSBzZWNvbmQgY2xhc3MsIDMgPSB0aGlyZCBjbGFzcyIsCiAgICB4ID0gIlRpY2tldCBjbGFzc2VzIG9mIHBhc3NlbmdlcnMiLAogICAgeSA9ICJOdW1iZXIgb2YgcGFzc2VuZ2VycyBzdXJ2aXZlZCIsIAogICAgY29sb3IgPSAiU2V4IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIwIiA9ICJvcmNoaWQzIiwgIjEiID0gInNreWJsdWU0IikpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpKQoKIyBDbGVhbiB1cApybShsaXN0ID0gYygiaSIsICJzZWN0aW9uMV9nZW5fY2xhc3NfaW5mb18xIiwgInNlY3Rpb24xX2dlbl9jbGFzc19pbmZvXzIiLCAic2VjdGlvbjFfZ2VuX2NsYXNzX2luZm9fMyIsICJzZWN0aW9uMV9nZW5fY2xhc3NfaW5mb180IiwgInNlY3Rpb24xX2dlbl9jbGFzc19pbmZvXzUiLCAic2VjdGlvbjFfZ2VuX2NsYXNzX2luZm9fNiIsICJzZWN0aW9uMV9nZW5faW5mb18xIiwgInNlY3Rpb24xX2dlbl9pbmZvXzIiLCAic2VjdGlvbjFfZ2VuX2luZm9fMyIsICJzZWN0aW9uMV9nZW5faW5mb180IiwgInNlY3Rpb24xX2dlbl9pbmZvXzUiLCAic2VjdGlvbjFfZ2VuX2luZm9fNiIsICJ0b3RhbF9mZW1hbGUiLCAidG90YWxfZmVtYWxlX3N1cnZpdmFsIiwgInRvdGFsX21hbGUiLCAidG90YWxfbWFsZV9zdXJ2aXZhbCIsICJ0b3RhbF9wYXNzXzFfY2xhc3MiLCAidG90YWxfcGFzc18yX2NsYXNzIiwgInRvdGFsX3Bhc3NfM19jbGFzcyIsICJ0b3RhbF9wYXNzXzFfY2xhc3Nfc3Vydml2ZWQiLCAidG90YWxfcGFzc18yX2NsYXNzX3N1cnZpdmVkIiwgInRvdGFsX3Bhc3NfM19jbGFzc19zdXJ2aXZlZCIsICJ0b3RhbF9wYXNzZW5nZXIiLCAidG90YWxfc3Vydml2YWwiLCAidmFyaWFibGVfbmFtZSIsICJ2YXJpYWJsZV92YWx1ZSIpKQoKYGBgCgo8YnI+Cjxicj4KCiMgQ2x1c3RlcmluZwpgYGB7cn0KIyBXZSBkZWNpZGVkIHRvIHdvcmsgb24gdHdvIGNvbHVtbnMgdGhhdCBhcmUgQW1vdW50IG9mIEZhcmUgZnJvbSBwYXNzZW5nZXJzIGFuZCBBZ2Ugb2YgcGFzc2VuZ2Vycy4gVGhpcyBpcyBiZWNhdXNlIGFsbCB0aGUgb3RoZXIgY29sdW1ucyBjb250YWluIHNtYWxsIG51bWVyaWMgdmFyaWFibGVzIHN1Y2ggYXMgYm9vbGVhbiAxIG9yIDIsIFRSVUUgb3IgRkFMU0Ugb3IgMSwyIGFuZCAzIGZvciBjbGFzcy4Kc2V0LnNlZWQoMTIzNCkKZHRfa21lYW5zIDwtIGRhdGFbLCBjKCJBZ2UiLCAiRmFyZSIpXQoKIyBGaXJzdCBvZiBhbGwsIGxldCdzIHN0YXJ0IHdpdGggMiBrLW1lYW5zICh0d28gY2VudGVycykgZm9yIG5vdy4KbW9kZWxfa21lYW5zIDwtIGttZWFucyhkdF9rbWVhbnMsIGNlbnRlcnMgPSAyKQoKY2x1c3RlcmluZ18xX2luZm8xIDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZm9yIGZpcnN0IGNsdXN0ZXIgaXMgIiwgbW9kZWxfa21lYW5zJHNpemVbMV0sICIuIikKY2x1c3RlcmluZ18xX2luZm8yIDwtIHBhc3RlMCgiVGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZm9yIHNlY29uZCBjbHVzdGVyIGlzICIsIG1vZGVsX2ttZWFucyRzaXplWzJdLCAiLiIpCnByaW50KGMoY2x1c3RlcmluZ18xX2luZm8xLCBjbHVzdGVyaW5nXzFfaW5mbzIgKSkKCiMgSGVyZSBpcyB0aGUgY2VudGVyIG9mIG91ciBjbHVzdGVyczoKcHJpbnQobW9kZWxfa21lYW5zJGNlbnRlcnMpCgojIExldCB1cyB2aXN1YWxpc2UgaXQgYnkgc2hvd2luZyB0aGUgY2VudGVycyBhcyB3ZWxsCmdncGxvdChkYXRhID0gZHRfa21lYW5zLCBhZXMoeCA9IEFnZSwgeSA9IEZhcmUsIGNvbG9yID0gYXMuZmFjdG9yKG1vZGVsX2ttZWFucyRjbHVzdGVyKSkpICsKICBnZW9tX3BvaW50KCkgKwogICMgTGV0IHVzIGlsbHVzdHJhdGUgdGhlIGNlbnRlcnMgb2YgY2x1c3RlcnMKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhLmZyYW1lKG1vZGVsX2ttZWFucyRjZW50ZXJzKSwKICAgICAgICAgICAgIHNoYXBlID0gImNpcmNsZSBmaWxsZWQiLAogICAgICAgICAgICAgc2l6ZSA9IDcsIAogICAgICAgICAgICAgY29sb3IgPSAiIzAwMDAwMCIsCiAgICAgICAgICAgICBmaWxsID0gYygiZGVlcHNreWJsdWUyIiwgImdvbGQyIiksCiAgICAgICAgICAgICBhbHBoYSA9IDAuNzUpICsKICBndWlkZXMoY29sb3IgPSAibm9uZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZGVlcHNreWJsdWUyIiwiZ29sZDIiKSkgKwogIGxhYnMoCiAgICB4ID0gIkFnZSBvZiBwYXNzZW5nZXJzIiwKICAgIHkgPSAiUGFzc2VuZ2VyIGZhcmUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpKQoKCiMgTGV0IHVzIHN0YW5kYXJkaXNlIGlucHV0cwojIFdlIGhhdmUgc2VlbiB0aGUgc3RhbmRhcmRpemF0aW9uIG9mIHZhcmlhYmxlcyBiZWZvcmU6IEV2ZXJ5IHZhcmlhYmxlIGlzIGNlbnRlcmVkIG9uIGl0cyBtZWFuIGFuZCB0aGVuIHNjYWxlZCBieSBpdHMgc3RhbmRhcmQgZGV2aWF0aW9uLgpkdF9rbWVhbnNfc2NhbGVkIDwtIGRhdGEudGFibGUoc2NhbGUoZHRfa21lYW5zKSkKCm1vZGVsX2ttZWFuc19zY2FsZWQgPC0ga21lYW5zKGR0X2ttZWFuc19zY2FsZWQsIGNlbnRlcnMgPSAyKQoKZ2dwbG90KGRhdGEgPSBkdF9rbWVhbnNfc2NhbGVkLCBhZXMoeCA9IEFnZSwgeSA9IEZhcmUsIGNvbG9yID0gYXMuZmFjdG9yKG1vZGVsX2ttZWFuc19zY2FsZWQkY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhLmZyYW1lKG1vZGVsX2ttZWFuc19zY2FsZWQkY2VudGVycyksCiAgICAgICAgICAgICBzaGFwZSA9ICJjaXJjbGUgZmlsbGVkIiwKICAgICAgICAgICAgIHNpemUgPSA3LCAKICAgICAgICAgICAgIGNvbG9yID0gIiMwMDAwMDAiLAogICAgICAgICAgICAgZmlsbCA9IGMoImRlZXBza3libHVlMiIsICJnb2xkMiIpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjc1KSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJkZWVwc2t5Ymx1ZTIiLCAiZ29sZDIiKSkgKwogIGxhYnMoCiAgICB4PSJBZ2Ugb2YgcGFzc2VuZ2VycyIsCiAgICB5PSAiUGFzc2VuZ2VyIGZhcmUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpKQpgYGAKCjxicj4KCldlIGZpbmQgdHdvIGNsdXN0ZXJzIG1haW5seSBkZXBlbmRpbmcgb24gdGhlIGFnZSB2YXJpYWJsZS4KCiMjIEVsYm93IHBsb3QKV2UgYWxzbyB3b3VsZCBsaWtlIHRvIGlsbHVzdHJhdGUgZWxib3cgcGxvdCB0byBmaW5kIHRoZSBvcHRpbWFsIGsuIFRvIGRvIHNvLCB3ZSB3aWxsIGJlIG5lZWRpbmcgdGhlIHRvdGFsIHdpdGhpbiBzdW0gb2Ygc3F1YXJlcyAoVFdTUykgb2Ygb3VyIHNjYWxlZCBtb2RlbC4gVFdTUyBhbHNvIG1lYW5zIHRoYXQgdGhlIHNxdWFyZWQgZGlzdGFuY2Ugb2YgYWxsIGRhdGEgcG9pbnRzIHRvIHRoZWlyIGNsdXN0ZXIgY2VudGVycy4KRmlyc3Qgb2YgYWxsLCBsZXQgdXMgY3JlYXRlIGEgbG9vcCBmdW5jdGlvbiBhbmQgd2Ugd2lsbCBsb29wIHRocm91Z2ggZGlmZmVyZW50IGsgdmFsdWVzIGZyb20gMSB0byAyMCBpbiB0aGlzIGNhc2UuIE5leHQsIHdlIHdpbGwgaWxsdXN0cmF0ZSBhbiBlbGJvdyBwbG90IHRvIGludmVzdGlnYXRlIHRoZSBzZW5zaXRpdml0eSBvZiBUV1NTIHRvIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgay4KYGBge3J9CmsgPC0gMjUKZWxib3cgPC0gZGF0YS50YWJsZSgiayIgPSAxOmssICJXU1MiID0gTkEpCgojIExldCB1cyBjcmVhdGUgb3VyIGxvb3AgZnVuY3Rpb24KZm9yIChpIGluIDE6aykgewogIGVsYm93JGtbaV0gPC0gaQogICMgTmV4dCwgd2UgYXJlIHNldHRpbmcgdGhlIG5ldyBjZW50ZXIgb2Ygb3VyIGNsdXN0ZXIKICBlbGJvdyRXU1NbaV0gPC0ga21lYW5zKGR0X2ttZWFuc19zY2FsZWQsIGNlbnRlcnMgPSBpLCBuc3RhcnQ9NTApJHRvdC53aXRoaW5zcwp9CgojIElsbHVzdHJhdGluZyBvdXIgZmluZGluZ3Mgd2l0aCB0aGUgaGVscCBvZiBsaW5lIGNoYXJ0CmdncGxvdChkYXRhID0gZWxib3csIGFlcyh4ID0gaywgeSA9IFdTUykpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkVsYm93IHBsb3QiLAogICAgeCA9ICJrIiwKICAgIHkgPSAiVFdTUyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCgpOYkNsdXN0KGRhdGE9ZHRfa21lYW5zX3NjYWxlZCwgbWV0aG9kPSJrbWVhbnMiKQojIEFjY29yZGluZyB0byB0aGUgbWFqb3JpdHkgcnVsZSwgdGhlIGJlc3QgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzICAzIApgYGAKSXQgaXMgY2xlYXIgdGhhdCAzIGNlbnRlcnMgd291bGQgbWFrZSBtb3JlIHN1aXRhYmxlIGZvciBvdXIgd29yaywgc28gbGV0IHVzIHJlcGxpY2F0ZSBvdXIgY2x1c3RlcmluZyB3b3JrIHdpdGggMyBjZW50ZXJzLgoKYGBge3J9CmR0X2ttZWFuc19zY2FsZWQgPC0gZGF0YS50YWJsZShzY2FsZShkdF9rbWVhbnMpKQoKbW9kZWxfa21lYW5zX3NjYWxlZCA8LSBrbWVhbnMoZHRfa21lYW5zX3NjYWxlZCwgY2VudGVycyA9IDMpCgpnZ3Bsb3QoZGF0YSA9IGR0X2ttZWFuc19zY2FsZWQsIGFlcyh4ID0gQWdlLCB5ID0gRmFyZSwgY29sb3IgPSBhcy5mYWN0b3IobW9kZWxfa21lYW5zX3NjYWxlZCRjbHVzdGVyKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEuZnJhbWUobW9kZWxfa21lYW5zX3NjYWxlZCRjZW50ZXJzKSwKICAgICAgICAgICAgIHNoYXBlID0gImNpcmNsZSBmaWxsZWQiLAogICAgICAgICAgICAgc2l6ZSA9IDcsIAogICAgICAgICAgICAgY29sb3IgPSAiIzAwMDAwMCIsCiAgICAgICAgICAgICBmaWxsID0gYygiZGVlcHNreWJsdWUyIiwgImdvbGQyIiwgImZvcmVzdGdyZWVuIiksCiAgICAgICAgICAgICBhbHBoYSA9IDAuNzUpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRlZXBza3libHVlMiIsICJnb2xkMiIsICJmb3Jlc3RncmVlbiIpKSArCiAgbGFicygKICAgIHg9IkFnZSBvZiBwYXNzZW5nZXJzIiwKICAgIHk9ICJQYXNzZW5nZXIgZmFyZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCgojIENsZWFuIHVwCnJtKGxpc3QgPSBjKCJrIiwgImkiLCAiY2x1c3RlcmluZ18xX2luZm8xIiwgImNsdXN0ZXJpbmdfMV9pbmZvMiIsICJkdF9rbWVhbnMiLCAiZHRfa21lYW5zX3NjYWxlZCIsICJlbGJvdyIsICJtb2RlbF9rbWVhbnMiLCAibW9kZWxfa21lYW5zX3NjYWxlZCIpKQoKYGBgCgo8YnI+Cjxicj4KCgojIExvZ2lzdGljIHJlZ3Jlc3Npb24KYGBge3J9CiMgRXN0aW1hdGUgdGhlIGxvZ2lzdGljIG1vZGVsCm1vZGVsX2dsbSA8LSBnbG0oZm9ybXVsYSA9IFN1cnZpdmVkIH4gRmlyc3RDbGFzcyArIFNlY29uZENsYXNzICsgU2V4ICsgQWdlICsgU2lic3AgKyBQYXJjaCArIEZhcmUgKyBDaGVyYm91cmcgKyBRdWVlbnN0b3duLCAKICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSwgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQoKIyBWaWV3IHN1bW1hcnkgc3RhdGlzdGljcwpwcmludChzdW1tYXJ5KG1vZGVsX2dsbSkpCgojIEZpdCBvZiB0aGUgbW9kZWwKd2l0aChtb2RlbF9nbG0sIHBjaGlzcShudWxsLmRldmlhbmNlIC0gZGV2aWFuY2UsIGRmLm51bGwgLSBkZi5yZXNpZHVhbCwgbG93ZXIudGFpbCA9IEZBTFNFKSkKIyBBIHAtdmFsdWUgYmVsb3cgMC4wMDEgaW5kaWNhdGVzIHRoYXQgdGhlIG1vZGVsIHBlcmZvcm1zIGJldHRlciB0aGFuIGFuIGVtcHR5IG1vZGVsCgpgYGAKTG9va2luZyBhdCB0aGUgcmVzdWx0IG9mIHRoZSBtb2RlbCwgaXQgaXMgY2xlYXIgdGhhdCBiZWluZyBpbiBmaXJzdCBjbGFzcywgYmVpbmcgZmVtYWxlIGFuZCBoYXZpbmcgYSB5b3VuZ2VyIGFnZSB3ZXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcHJlZGljdG9ycyBvZiBzdXJ2aXZhbCwgd2hpbGUgdGhlIG51bWJlciBvZiBzaWJsaW5ncyBhbmQgb3RoZXIgdmFyaWFibGVzIGRpZCBub3Qgc2hvdyBzaWduaWZpY2FudCBpbmZsdWVuY2Ugb24gc3Vydml2YWwgaW4gdGhlIGdpdmVuIGRhdGEgc2V0LgoKPGJyPgoKIyBUcmVlCmBgYHtyfQojIyMgQmFzZSB0cmVlIAojIEJ1aWxkIGEgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyBycGFydAptb2RlbF90cmVlIDwtIHJwYXJ0KGRhdGEgPSBkYXRhLCBmb3JtdWxhID0gU3Vydml2ZWQgfiBQY2xhc3MgKyBTZXggKyBBZ2UgKyBTaWJzcCArIFBhcmNoICsgRmFyZSArIEVtYmFya2VkLCBtZXRob2QgPSAiY2xhc3MiKQoKIyBQbG90IHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycCwgd2l0aCBleHRyYSBkZXRhaWxzIChleHRyYSA9IDIpCnBycChtb2RlbF90cmVlLCBleHRyYSA9IDIsIG1haW4gPSAiQmFzZSB0cmVlIGZvciBtb2RlbCIpCgojIFByaW50IGFuIHBsb3QgZGF0YSBvbiB0cmVlCnByaW50Y3AobW9kZWxfdHJlZSkKcGxvdGNwKG1vZGVsX3RyZWUpCiMgVGhlIHRyZWUgc2hvd3MgdGhhdCB0aGUgZmlyc3QgZGVjaWRpbmcgZmFjdG9yIHNlZW1zIHRvIGJlIGdlbmRlci4gQWZ0ZXJ3YXJkcyBtYWlubHkgY2xhc3MgYW5kIGFnZSBzaG93IHNlZW0gcHJvbWluZW50IGRlY2lkaW5nIGZhY3RvcnMgZm9yIGJyYW5jaGVzIHRob3VnaCBpbnRlcmVzdGluZ2x5IGVub3VnaCBmYXJlIHNlZW1zIHRvIHBsYXkgYW4gaW1wb3J0YW50IHJvbGUgYXMgd2VsbC4KCiMgRGV0ZXJtaW5lIHRoZSBtaW5pbXVtIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgZm9yIHBydW5pbmcKI2NwIHBhcmFtZXRlcjogY2FwdHVyZXMgbW9kZWwgY29tcGxleGl0eQptaW5fY3AgPC0gbW9kZWxfdHJlZSRjcHRhYmxlW3doaWNoLm1pbihtb2RlbF90cmVlJGNwdGFibGVbLCAieGVycm9yIl0pLCAiQ1AiXQoKIyBQcnVuZSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyB0aGUgbWluaW11bSBjcAptb2RlbF90cmVlXzIgPC0gcHJ1bmUobW9kZWxfdHJlZSwgY3AgPSBtaW5fY3ApCgojIFBsb3QgdGhlIHBydW5lZCBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycCwgd2l0aCBleHRyYSBkZXRhaWxzIChleHRyYSA9IDIpCnBycChtb2RlbF90cmVlXzIsIGV4dHJhID0gMiwgbWFpbiA9ICJQcnVuZWQgYmFzZSB0cmVlIHdpdGggbGVzcyBjb21wbGV4aXR5IikKCiMgVGhlIGJyYW5jaCBkZXRlcm1pbmVkIGJ5IGJlaW5nIG1hbGUgYW5kIG92ZXIgdGhlIGFnZSBvZiA2IHNob3dzIHRoZSBiaWdnZXN0IGdyb3VwIGFuZCB0aGUgbW9zdCB3cm9uZyBndWVzc2VzIG9mIHRoZSBtb2RlbC4KCiMgQ2FsY3VsYXRlIHRoZSBtZWFuIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCBwcmVkaWN0aW9ucwptZWFuKHByZWRpY3QobW9kZWxfdHJlZSwgZGF0YSwgdHlwZSA9ICJjbGFzcyIpPT1kYXRhJFN1cnZpdmVkKQoKbWVhbihwcmVkaWN0KG1vZGVsX3RyZWVfMiwgZGF0YSwgdHlwZSA9ICJjbGFzcyIpPT1kYXRhJFN1cnZpdmVkKQoKIyBCZXR3ZWVuIHRoZSBhY2N1cmFjeSBmb3IgdGhlIHBydW5lZCBhbmQgbm90LXBydW5lZCBtb2RlbCB0aGVyZSBpcyBvbmx5IGEgMiUgZGlmZmVyZW5jZSBzbyB3ZSB3aWxsIGNob29zZSB0aGUgcHJ1bmVkIG1vZGVsIGFzIHJlcHJlc2VudGF0aXZlIGFzIGl0IGlzIGxlc3MgY29tcGxleAptb2RlbF90cmVlcyA8LSBtb2RlbF90cmVlXzIKCiMgUHJpbnQgYW4gcGxvdCBkYXRhIG9uIHRyZWUKcHJpbnRjcChtb2RlbF90cmVlXzIpCnBsb3RjcChtb2RlbF90cmVlXzIpCgojIyMgVmlzdWFsaXphdGlvbiBmb3IgaW50ZXJlc3RpbmcgdmFyaWFibGUgY29tYmluYXRpb25zCiMgQ29udmVydCAnU3Vydml2ZWQnIHZhcmlhYmxlIHRvIGEgZmFjdG9yCnRyZWVfZGF0YSA8LSBkYXRhCnRyZWVfZGF0YSRTdXJ2aXZlZCA8LSBhcy5mYWN0b3IodHJlZV9kYXRhJFN1cnZpdmVkKSAKCiMgQnVpbGQgYSBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHJwYXJ0Cm1vZGVsX3RyZWVfNCA8LSBycGFydChkYXRhID0gdHJlZV9kYXRhLCBmb3JtdWxhID0gU3Vydml2ZWQgfiBBZ2UgKyBTaWJzcCwgbWV0aG9kID0gImNsYXNzIikKCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyBwcnAKcHJwKG1vZGVsX3RyZWVfNCwgZXh0cmEgPSAyLCBtYWluID0gIlRyZWUgZm9yIEFnZSBhbmQgU2libGluZy9TcG91c2UiKQoKIyBEZXRlcm1pbmUgdGhlIG1pbmltdW0gY29tcGxleGl0eSBwYXJhbWV0ZXIgKGNwKSBmb3IgcHJ1bmluZwptaW5fY3AgPC0gbW9kZWxfdHJlZV80JGNwdGFibGVbd2hpY2gubWluKG1vZGVsX3RyZWVfNCRjcHRhYmxlWywgInhlcnJvciJdKSwgIkNQIl0KCiMgUHJ1bmUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgdGhlIG1pbmltdW0gY3AKbW9kZWxfdHJlZV81IDwtIHBydW5lKG1vZGVsX3RyZWVfNCwgY3AgPSBtaW5fY3ApCgojIFBsb3QgdGhlIHBydW5lZCBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycApwcnAobW9kZWxfdHJlZV81LCBleHRyYSA9IDIsIG1haW4gPSAiUHJ1bmVkIHRyZWUgZm9yIEFnZSBhbmQgU2libGluZy9TcG91c2UiKQoKIyBQcmludCBhbiBwbG90IGRhdGEgb24gdHJlZQpwcmludGNwKG1vZGVsX3RyZWVfNSkKcGxvdGNwKG1vZGVsX3RyZWVfNSkKCgoKIyBDcmVhdGUgYSBzY2F0dGVyIHBsb3Qgd2l0aCBkZWNpc2lvbiB0cmVlIGJvdW5kYXJpZXMgZm9yIEFnZSBhbmQgU2lic3AgdmFyaWFibGVzCnBsb3QxIDwtIGdncGxvdChkYXRhID0gdHJlZV9kYXRhLCBhZXMoeCA9IFNpYnNwLCB5ID0gQWdlKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3Vydml2ZWQpKSArCiAgZ2VvbV9wYXJ0dHJlZShkYXRhID0gbW9kZWxfdHJlZV81LCBhZXMoZmlsbCA9IFN1cnZpdmVkKSwgYWxwaGEgPSAwLjEpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAiZGVlcHNreWJsdWUyIikpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJkZWVwc2t5Ymx1ZTIiKSkgKwogIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogIGxhYnMoCiAgICB4ID0gIlNpYnNwIiwKICAgIHkgPSAiQWdlIiwKICAgIGNvbG9yID0gIlN1cnZpdmVkIiwgCiAgICB0aXRsZSA9ICJTY2F0dGVyIHBsb3Qgb2YgYWdlIGFuZCBmYW1pbGlhbCByZWxhdGlvbnMgc2hvd2luZyBzdXJ2aXZhbCBhbmQgdHJlZSBlc3RpbWF0aW9uIG9mIHN1cnZpdmFsIgogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiMwMDAwMDAiKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBCdWlsZCBhbm90aGVyIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgcnBhcnQKbW9kZWxfdHJlZV80IDwtIHJwYXJ0KGRhdGEgPSB0cmVlX2RhdGEsIGZvcm11bGEgPSBTdXJ2aXZlZCB+IEFnZSArIFBhcmNoLCBtZXRob2QgPSAiY2xhc3MiKQoKIyBQbG90IHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycApwcnAobW9kZWxfdHJlZV80LCBleHRyYSA9IDIsIG1haW4gPSAiVHJlZSBmb3IgQWdlIGFuZCBQYXJlbnQvQ2hpbGQiKQoKIyBEZXRlcm1pbmUgdGhlIG1pbmltdW0gY29tcGxleGl0eSBwYXJhbWV0ZXIgKGNwKSBmb3IgcHJ1bmluZwptaW5fY3AgPC0gbW9kZWxfdHJlZV80JGNwdGFibGVbd2hpY2gubWluKG1vZGVsX3RyZWVfNCRjcHRhYmxlWywgInhlcnJvciJdKSwgIkNQIl0KCiMgUHJ1bmUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgdGhlIG1pbmltdW0gY3AKbW9kZWxfdHJlZV82IDwtIHBydW5lKG1vZGVsX3RyZWVfNCwgY3AgPSBtaW5fY3ApCgojIFBsb3QgdGhlIHBydW5lZCBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycApwcnAobW9kZWxfdHJlZV82LCBleHRyYSA9IDIsIG1haW4gPSAiUHJ1bmVkIHRyZWUgZm9yIEFnZSBhbmQgUGFyZW50L0NoaWxkIikKCiMgUHJpbnQgYW4gcGxvdCBkYXRhIG9uIHRyZWUKcHJpbnRjcChtb2RlbF90cmVlXzYpCnBsb3RjcChtb2RlbF90cmVlXzYpCgoKIyBDcmVhdGUgYSBzY2F0dGVyIHBsb3Qgd2l0aCBkZWNpc2lvbiB0cmVlIGJvdW5kYXJpZXMgZm9yIEFnZSBhbmQgUGFyY2ggdmFyaWFibGVzCnBsb3QyIDwtIGdncGxvdChkYXRhID0gdHJlZV9kYXRhLCBhZXMoeCA9IFBhcmNoLCB5ID0gQWdlKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3Vydml2ZWQpKSArCiAgZ2VvbV9wYXJ0dHJlZShkYXRhID0gbW9kZWxfdHJlZV82LCBhZXMoZmlsbCA9IFN1cnZpdmVkKSwgYWxwaGEgPSAwLjEpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAiZGVlcHNreWJsdWUyIikpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJkZWVwc2t5Ymx1ZTIiKSkgKwogIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogIGxhYnMoCiAgICB4ID0gIlBhcmNoIiwKICAgIHkgPSAiQWdlIiwKICAgIGNvbG9yID0gIlN1cnZpdmVkIgogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQuYm94ID0gImhvcml6b250YWwiCiAgKQoKIyBDb21iaW5lIHRoZSB0d28gcGxvdHMKcGxvdDEgKyBwbG90MgoKIyBWaXN1YWxpemluZyBib3RoIGFnZSBhbmQgZmFyZSBhcyB0aGUgb25seSB2YXJpYWJsZXMgY29udGFpbmluZyB0aHJvdWdob3V0IGRpc3RyaWJ1dGVkIHZhbHVlcy4KIyBCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgcnBhcnQKbW9kZWxfdHJlZV80IDwtIHJwYXJ0KGRhdGEgPSB0cmVlX2RhdGEsIGZvcm11bGEgPSBTdXJ2aXZlZCB+IEFnZSArIEZhcmUsIG1ldGhvZCA9ICJjbGFzcyIpCgojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgcHJwCnBycChtb2RlbF90cmVlXzQsIGV4dHJhID0gMiwgbWFpbiA9ICJUcmVlIGZvciBBZ2UgYW5kIEZhcmUiKQoKIyBEZXRlcm1pbmUgdGhlIG1pbmltdW0gY29tcGxleGl0eSBwYXJhbWV0ZXIgKGNwKSBmb3IgcHJ1bmluZwptaW5fY3AgPC0gbW9kZWxfdHJlZV80JGNwdGFibGVbd2hpY2gubWluKG1vZGVsX3RyZWVfNCRjcHRhYmxlWywgInhlcnJvciJdKSwgIkNQIl0KCiMgUHJ1bmUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgdGhlIG1pbmltdW0gY3AKbW9kZWxfdHJlZV83IDwtIHBydW5lKG1vZGVsX3RyZWVfNCwgY3AgPSBtaW5fY3ApCgojIFBsb3QgdGhlIHBydW5lZCBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycApwcnAobW9kZWxfdHJlZV83LCBleHRyYSA9IDIsIG1haW4gPSAicHJ1bmVkIHRyZWUgZm9yIEFnZSBhbmQgRmFyZSIpCgojIFByaW50IGFuIHBsb3QgZGF0YSBvbiB0cmVlCnByaW50Y3AobW9kZWxfdHJlZV83KQpwbG90Y3AobW9kZWxfdHJlZV83KQoKCiMgQ3JlYXRlIGEgc2NhdHRlciBwbG90IHdpdGggZGVjaXNpb24gdHJlZSBib3VuZGFyaWVzIGZvciBBZ2UgYW5kIGZhcmUgdmFyaWFibGVzCmdncGxvdChkYXRhID0gdHJlZV9kYXRhLCBhZXMoeCA9IEZhcmUsIHkgPSBBZ2UpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTdXJ2aXZlZCkpICsKICBnZW9tX3BhcnR0cmVlKGRhdGEgPSBtb2RlbF90cmVlXzcsIGFlcyhmaWxsID0gU3Vydml2ZWQpLCBhbHBoYSA9IDAuMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJkZWVwc2t5Ymx1ZTIiKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImRlZXBza3libHVlMiIpKSArCiAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArCiAgbGFicygKICAgIHggPSAiRmFyZSIsCiAgICB5ID0gIkFnZSIsCiAgICBjb2xvciA9ICJTdXJ2aXZlZCIsCiAgICB0aXRsZSA9ICJTY2F0dGVyIHBsb3Qgb2YgYWdlIGFuZCBmYXJlIHNob3dpbmcgc3Vydml2YWwgYW5kIHRyZWUgZXN0aW1hdGlvbiBvZiBzdXJ2aXZhbCIKICApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIiksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgVGhpcyB2aXN1YWxpemF0aW9uIHRob3VnaCBsb29raW5nIHF1aXRlIG5pY2UgZG9lcyBub3Qgc2VlbSB0byBpbmRpY2F0ZSBhbnkgbm90YWJsZSBpbXBhY3Qgb2YgZmFyZSBpbiBteSBvcGluaW9uLgoKIyBDbGVhbiB1cApybShsaXN0ID0gYygibWluX2NwIiwgInBsb3QxIiwgInBsb3QyIiwgIm1vZGVsX3RyZWUiLCAibW9kZWxfdHJlZV8yIiwgIm1vZGVsX3RyZWVfNCIsICJtb2RlbF90cmVlXzUiLCAibW9kZWxfdHJlZV82IiwgIm1vZGVsX3RyZWVfNyIsICJ0cmVlX2RhdGEiKSkKCgpgYGAKSXQgaXMgY2xlYXIgdGhhdCB0aGVyZSBpcyBvbmx5IHNtYWxsIGRpZmZlcmVuY2UgYmV0d2VlbiBQcnVuZWQgYmFzZSB0cmVlIGFuZCBjb21wbGV4IGJhc2UgdHJlZS4gQmV0d2VlbiB0aGUgYWNjdXJhY3kgZm9yIHRoZSBwcnVuZWQgYW5kIG5vdC1wcnVuZWQgbW9kZWwgdGhlcmUgaXMgb25seSBhIDIlIGRpZmZlcmVuY2Ugc28gd2Ugd2lsbCBjaG9vc2UgdGhlIHBydW5lZCBtb2RlbCBhcyByZXByZXNlbnRhdGl2ZSBhcyBpdCBpcyBsZXNzIGNvbXBsZXguICBUaGlzIGlzIGJlY2F1c2UsIHdlIGJlbGlldmUgdGhhdCB0aGlzIHdpbGwgc2F2ZSBjb21wdXRhdGlvbmFsIG1lbW9yeS4gTW9yZW92ZXIsIHRoZSBoaWdoZXIgY29tcGxleGl0eSB0cmVlIGRvZXMgb25seSBpbXByb3ZlIG9uIGEgbGVzcyByZWxldmFudCBicmFuY2guIAoKVGhlIG1vcmUgaW1wb3J0YW50IGJyYW5jaCBkZXRlcm1pbmVkIGJ5IGJlaW5nIG1hbGUgYW5kIG92ZXIgdGhlIGFnZSBvZiA2IHNob3dzIHRoZSBiaWdnZXN0IGdyb3VwIGFuZCB0aGUgbW9zdCB3cm9uZyBndWVzc2VzIG9mIHRoZSBtb2RlbC4KCiMjIEludmVzdGlnYXRpbmcgaHlwZXJwYXJhbWV0ZXJzIGZvciB0aGUgdHJlZSBtb2RlbApgYGB7cn0KIyMjIEludmVzdGlnYXRpbmcgaHlwZXJwYXJhbWV0ZXJzCiMgRGVmaW5lIHRoZSB2YWx1ZXMgZm9yIG1pbmltdW0gb2JzZXJ2YXRpb25zLCBtYXhpbXVtIGRlcHRoLCBhbmQgY29tcGxleGl0eSBwYXJhbWV0ZXIgKGNwKQptaW5fb2JzIDwtIHNlcShmcm9tID0gNSwgdG8gPSA1MCwgYnkgPSA1KQptYXhfZGVwdGggPC0gYygxLCA1LCAxMCwgMTUpCmNwX3ZhbCA8LSBjKDAuMDAxLCAwLjAxLCAwLjA1LCAwLjEpCgojIENyZWF0ZSBhbiBlbXB0eSBkYXRhIGZyYW1lIHRvIHN0b3JlIHRoZSByZXN1bHRzCnJlc3VsdHMgPC0gZGF0YS5mcmFtZSgpCgojIEl0ZXJhdGUgb3ZlciB0aGUgY29tYmluYXRpb25zIG9mIG1pbl9vYnMsIG1heF9kZXB0aCwgYW5kIGNwX3ZhbApmb3IgKGkgaW4gMTpsZW5ndGgobWluX29icykpIHsKICBmb3IgKGogaW4gMTpsZW5ndGgobWF4X2RlcHRoKSkgewogICAgZm9yIChrIGluIDE6bGVuZ3RoKGNwX3ZhbCkpIHsKICAgICAgIyBCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgd2l0aCB0aGUgc3BlY2lmaWVkIHBhcmFtZXRlcnMKICAgICAgbW9kZWwgPC0gcnBhcnQoZGF0YSA9IGRhdGEsIGZvcm11bGEgPSBTdXJ2aXZlZCB+IFBjbGFzcyArIFNleCArIEFnZSArIFNpYnNwICsgUGFyY2ggKyBGYXJlICsgRW1iYXJrZWQsIAogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY2xhc3MiLAogICAgICAgICAgICAgICAgICAgICBjcCA9IGNwX3ZhbFtrXSwKICAgICAgICAgICAgICAgICAgICAgbWF4ZGVwdGggPSBtYXhfZGVwdGhbal0sCiAgICAgICAgICAgICAgICAgICAgIG1pbmJ1Y2tldCA9IG1pbl9vYnNbaV0pCiAgICAgIAogICAgICAjIENhbGN1bGF0ZSB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsIHByZWRpY3Rpb25zCiAgICAgIGFjYyA8LSBtZWFuKHByZWRpY3QobW9kZWwsIGRhdGEsIHR5cGUgPSAiY2xhc3MiKSA9PSBkYXRhJFN1cnZpdmVkKQogICAgICAKICAgICAgIyBBcHBlbmQgdGhlIGFjY3VyYWN5IGFuZCBwYXJhbWV0ZXIgdmFsdWVzIHRvIHRoZSByZXN1bHRzIGRhdGEgZnJhbWUKICAgICAgcmVzdWx0cyA8LSByYmluZChyZXN1bHRzLCBjKGFjYywgbWluX29ic1tpXSwgbWF4X2RlcHRoW2pdLCBjcF92YWxba10pKQogICAgfQogIH0KfQoKIyBTZXQgY29sdW1uIG5hbWVzIGZvciB0aGUgcmVzdWx0cyBkYXRhIGZyYW1lCmNvbG5hbWVzKHJlc3VsdHMpIDwtIGMoImFjY3VyYWN5IiwgIm1pbl9vYnMiLCAibWF4X2RlcHRoIiwgImNwIikKCiMgQ3JlYXRlIGEgM0Qgc2NhdHRlciBwbG90IHVzaW5nIHNjYXR0ZXIzRApwbG90M0Q6OnNjYXR0ZXIzRCh4ID0gcmVzdWx0cyRtaW5fb2JzLCB5ID0gcmVzdWx0cyRtYXhfZGVwdGgsIHogPSByZXN1bHRzJGNwLAogICAgICAgICAgICAgICAgICB0aGV0YSA9IDMwLCBwaGkgPSAxMCwKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJNaW4gT2JzIiwgeWxhYiA9ICJNYXggRGVwdGgiLCB6bGFiID0gIkNQIiwKICAgICAgICAgICAgICAgICAgdGlja3R5cGUgPSAiZGV0YWlsZWQiLAogICAgICAgICAgICAgICAgICBwY2ggPSAxOSwKICAgICAgICAgICAgICAgICAgY2V4LmF4aXMgPSAwLjc1LCBjZXgubGFiID0gMC43NSwgZXhwYW5kID0gMC43NSwKICAgICAgICAgICAgICAgICAgY29sdmFyID0gcmVzdWx0cyRhY2N1cmFjeSwgY29sID0gdmlyaWRpcygxMDApKQoKCiMgU2VsZWN0IHRoZSBvcHRpbWFsIHBhcmFtZXRlciBjb21iaW5hdGlvbiB3aXRoIHRoZSBoaWdoZXN0IGFjY3VyYWN5Cm9wdF9wYXJhbXMgPC0gcmVzdWx0c1t3aGljaC5tYXgocmVzdWx0cyRhY2N1cmFjeSksXQoKIyBCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgdGhlIG9wdGltYWwgcGFyYW1ldGVycwptb2RlbF90cmVlXzMgPC0gcnBhcnQoZGF0YT1kYXRhLCBmb3JtdWxhID0gU3Vydml2ZWQgfiBQY2xhc3MgKyBTZXggKyBBZ2UgKyBTaWJzcCArIFBhcmNoICsgRmFyZSArIEVtYmFya2VkLCAKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjbGFzcyIsCiAgICAgICAgICAgICAgICAgICAgICBjcCA9IG9wdF9wYXJhbXMkY3AsIAogICAgICAgICAgICAgICAgICAgICAgbWF4ZGVwdGggPSBvcHRfcGFyYW1zJG1heF9kZXB0aCwgCiAgICAgICAgICAgICAgICAgICAgICBtaW5idWNrZXQgPSBvcHRfcGFyYW1zJG1pbl9vYnMpCgojIENhbGN1bGF0ZSB0aGUgbWVhbiBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgcHJlZGljdGlvbnMKbWVhbihwcmVkaWN0KG1vZGVsX3RyZWVfMywgZGF0YSwgdHlwZSA9ICJjbGFzcyIpID09IGRhdGEkU3Vydml2ZWQpCgojIFBsb3QgdGhlIHBydW5lZCBkZWNpc2lvbiB0cmVlIHdpdGggYWRkaXRpb25hbCBkZXRhaWxzCnBycChtb2RlbF90cmVlXzMsIGV4dHJhID0gMiwgbWFpbiA9ICJUcmVlIHdpdGggb3B0aW1hbCBwYXJhbWV0ZXJzIikKCiMgUHJpbnQgYW4gcGxvdCBkYXRhIG9uIHRyZWUKcHJpbnRjcChtb2RlbF90cmVlXzMpCnBsb3RjcChtb2RlbF90cmVlXzMpCgojIENsZWFuIHVwCnJtKGxpc3QgPSBjKCJhY2MiLCAiY3BfdmFsIiwgImkiLCAiaiIsICJrIiwgIm1heF9kZXB0aCIsICJtaW5fb2JzIiwgInJlc3VsdHMiLCAib3B0X3BhcmFtcyIsICJtb2RlbF90cmVlXzMiLCAibW9kZWwiKSkKYGBgCgpUaGUgdW5jZXJ0YWluIGJyYW5jaCBkaXNjdXNzZWQgYmVmb3JlIChtYWxlLCBhZ2UgYWJvdmUgNikgY2FuIHJlYXNvbmFibHkgYmUgZnVydGhlciBkaXN0aW5ndWlzaGVkIGJ5IGNsYXNzIGFuZCBtYXliZSBhZ2UuIEFmdGVyd2FyZHMgdGhlIGNsYXNzaWZpY2F0aW9uIHNlZW1zIHRvIGJlIG1vc3RseSBkZXBlbmRlbnQgb24gZmFyZSB3aXRoIGRlY2lzaW9ucyBhbG9uZyB0aGUgYnJhbmNoIGxlYWRpbmcgdG8gbm8gcmVhbCBpbnNpZ2h0cy4gVGhlIG9wdGltYWwgbW9kZWwgc2hvd3Mgb25seSBtYXJnaW5hbCBpbXByb3ZlbWVudCBjb21wYXJlZCB0byBhY2N1cmFjeSBvZiBlYXJsaWVyIG1vZGVscy4KCjxicj4KPGJyPgoKIyBOZXVyYWwgTmV0d29ya3MKYGBge3J9CnNldC5zZWVkKDEyMzQ1KSAgICMgU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CgojIERhdGEgc2V0dXAKWCA8LSBhcy5tYXRyaXgoZGF0YVssIGMoIkZpcnN0Q2xhc3MiLCAiU2Vjb25kQ2xhc3MiLCAiVGhpcmRDbGFzcyIsICJTZXgiLCAiQWdlIiwgIlNpYnNwIiwgIlBhcmNoIiwgIkZhcmUiLCAiU291dGhoYW1wdG9uIiwgIkNoZXJib3VyZyIsICJRdWVlbnN0b3duIildKQp5IDwtIGRhdGEkU3Vydml2ZWQKCiMjIE1vZGVsIGFzIGluIGxlY3R1cmUKCm5vcm1hbGl6ZSA8LSBsYXllcl9ub3JtYWxpemF0aW9uKCkgICAjIENyZWF0ZSBhIG5vcm1hbGl6YXRpb24gbGF5ZXIKbm9ybWFsaXplICU+JSBhZGFwdChYKSAgICMgQWRhcHQgdGhlIG5vcm1hbGl6YXRpb24gbGF5ZXIgdG8gdGhlIGRhdGEKCnByaW50KG5vcm1hbGl6ZSRtZWFuKSAgICMgUHJpbnQgdGhlIG1lYW4gdmFsdWVzIHVzZWQgZm9yIG5vcm1hbGl6YXRpb24KCm1vZGVsX25ldXJhbCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgICAjIENyZWF0ZSBhIHNlcXVlbnRpYWwgbW9kZWwKCm1vZGVsX25ldXJhbCAlPiUKICBub3JtYWxpemUoKSAlPiUgICAjIEFwcGx5IG5vcm1hbGl6YXRpb24gIyBMMQoKICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzEiLCB1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUgICAjIEFkZCBhIGhpZGRlbiBsYXllciB3aXRoIDE2IHVuaXRzIGFuZCBSZUxVIGFjdGl2YXRpb24KICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzIiLCB1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUgICAjIEFkZCBhbm90aGVyIGhpZGRlbiBsYXllciB3aXRoIDE2IHVuaXRzIGFuZCBSZUxVIGFjdGl2YXRpb24KICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzMiLCB1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUgICAjIEFkZCBhIHRoaXJkIGhpZGRlbiBsYXllciB3aXRoIDE2IHVuaXRzIGFuZCBSZUxVIGFjdGl2YXRpb24KCiAgIGxheWVyX2RlbnNlKG5hbWUgPSAiT3V0cHV0TGF5ZXIiLCB1bml0cyA9IDEpICAgIyBBZGQgYW4gb3V0cHV0IGxheWVyIHdpdGggMSB1bml0CgpzdW1tYXJ5KG1vZGVsX25ldXJhbCkgICAjIFByaW50IHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbAoKI3Bsb3RfbW9kZWwobW9kZWxfYmFzZSkKCm1vZGVsX25ldXJhbCAlPiUgY29tcGlsZSgKICBsb3NzID0gIk1TRSIsCiAgb3B0aW1pemVyID0gb3B0aW1pemVyX2FkYW0obGVhcm5pbmdfcmF0ZSA9IDAuMDAxKSwKICBtZXRyaWNzID0gIkFjY3VyYWN5IgopICAgIyBDb21waWxlIHRoZSBtb2RlbCB3aXRoIG1lYW4gc3F1YXJlZCBlcnJvciBsb3NzLCBBZGFtIG9wdGltaXplciwgYW5kIGFjY3VyYWN5IGFzIGEgbWV0cmljCgptb2RlbF9uZXVyYWwgJT4lIGZpdCgKICB4ID0gWCwKICB5ID0geSwKICBlcG9jaHMgPSA1MDAsCiAgdmVyYm9zZSA9IDAKKSAgICMgVHJhaW4gdGhlIG1vZGVsIG9uIHRoZSBkYXRhIGZvciA1MDAgZXBvY2hzCgojIG1vZGVsX25ldXJhbCRsYXllcnNbWzJdXSRrZXJuZWwgICAjIFByaW50IHRoZSBrZXJuZWwgKHdlaWdodHMpIG9mIHRoZSBzZWNvbmQgbGF5ZXIKIyBtb2RlbF9uZXVyYWwkbGF5ZXJzW1szXV0ka2VybmVsICAgIyBQcmludCB0aGUga2VybmVsICh3ZWlnaHRzKSBvZiB0aGUgdGhpcmQgbGF5ZXIKIyBtb2RlbF9uZXVyYWwkbGF5ZXJzW1s0XV0ka2VybmVsICAgIyBQcmludCB0aGUga2VybmVsICh3ZWlnaHRzKSBvZiB0aGUgZm91cnRoIGxheWVyCgoKbW9kZWxfbmV1cmFsICU+JSBldmFsdWF0ZShYLCB5LCB2ZXJib3NlID0gMCkgICAjIEV2YWx1YXRlIHRoZSBtb2RlbCBvbiB0aGUgZGF0YQoKcm0obGlzdCA9IGMoIlgiLCAieSIpKQpgYGAKCiMjIEludmVzdGlnYXRpbmcgaHlwZXJwYXJhbWV0ZXJzIGZvciB0aGUgbmV1cmFsIG5ldHdvcmtzCmBgYHtyfQojIyMgSW52ZXN0aWdhdGluZyBoeXBlcnBhcmFtZXRlcnMKIyBEZWZpbmUgdGhlIHZhbHVlcwpsZWFybmluZ19yYXRlIDwtIGMoMC4wMDAxLCAwLjAwMSwgMC4wMSwgMC4xKSAgIyBMZWFybmluZyByYXRlCndpZHRoIDwtIGMoNCwgMTYsIDI0KSAjIEFtb3VudCBvZiBuZXVyb25zIHBlciBsYXllcgpsZW5ndGggPC0gYygxLCAzLCAxMCkgIyBBbW91bnQgb2YgbGF5ZXJzCgojIERhdGEgc2V0dXAKWCA8LSBhcy5tYXRyaXgoZGF0YVssIGMoIkZpcnN0Q2xhc3MiLCAiU2Vjb25kQ2xhc3MiLCAiVGhpcmRDbGFzcyIsICJTZXgiLCAiQWdlIiwgIlNpYnNwIiwgIlBhcmNoIiwgIkZhcmUiLCAiU291dGhoYW1wdG9uIiwgIkNoZXJib3VyZyIsICJRdWVlbnN0b3duIildKQp5IDwtIGRhdGEkU3Vydml2ZWQKCiMgQ3JlYXRlIGFuIGVtcHR5IGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIHJlc3VsdHMKcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkKCiMgSXRlcmF0ZSBvdmVyIHRoZSBjb21iaW5hdGlvbnMKZm9yIChpIGluIDE6bGVuZ3RoKGxlYXJuaW5nX3JhdGUpKSB7CiAgZm9yIChqIGluIDE6bGVuZ3RoKHdpZHRoKSkgewogICAgZm9yIChrIGluIDE6bGVuZ3RoKGxlbmd0aCkpIHsKCiAgICAgICNwcmludChpKQogICAgICAjcHJpbnQoaikKICAgICAgI3ByaW50KGspCgogICAgICBpZiAobGVuZ3RoW2tdID09IDEpIHsgIyBPbmUgbGF5ZXIgbW9kZWwKCiAgICAgICAgbm9ybWFsaXplX2kgPC0gbGF5ZXJfbm9ybWFsaXphdGlvbigpICAgIyBDcmVhdGUgYSBub3JtYWxpemF0aW9uIGxheWVyCiAgICAgICAgbm9ybWFsaXplX2kgJT4lIGFkYXB0KFgpICAgIyBBZGFwdCB0aGUgbm9ybWFsaXphdGlvbiBsYXllciB0byB0aGUgZGF0YQoKICAgICAgICBtb2RlbF9uZXVyYWxfaSA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgICAjIENyZWF0ZSBhIHNlcXVlbnRpYWwgbW9kZWwKCiAgICAgICAgbW9kZWxfbmV1cmFsX2kgJT4lCiAgICAgICAgICBub3JtYWxpemVfaSgpICU+JSAgICMgQXBwbHkgbm9ybWFsaXphdGlvbgoKICAgICAgICAgIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMSIsIHVuaXRzID0gd2lkdGhbal0sIGFjdGl2YXRpb24gPSAncmVsdScpICU+JQoKICAgICAgICAgICBsYXllcl9kZW5zZShuYW1lID0gIk91dHB1dExheWVyIiwgdW5pdHMgPSAxKSAgICMgQWRkIG91dHB1dCBsYXllciB3aXRoIDEgdW5pdAoKICAgICAgICBtb2RlbF9uZXVyYWxfaSAlPiUgY29tcGlsZShsb3NzPSJNU0UiLCBvcHRpbWl6ZXI9IG9wdGltaXplcl9hZGFtKGxlYXJuaW5nX3JhdGUgPSBsZWFybmluZ19yYXRlW2ldKSwgbWV0cmljcz0iQWNjdXJhY3kiKQoKICAgICAgICBtb2RlbF9uZXVyYWxfaSAlPiUgZml0KHggPSBYLCB5ID0geSwgZXBvY2hzID0gMjUwLCB2ZXJib3NlID0gMCkKCiAgICAgIH0gZWxzZSBpZiAobGVuZ3RoW2tdID09IDMpIHsgIyBUaHJlZSBsYXllciBtb2RlbAoKICAgICAgICBub3JtYWxpemVfaSA8LSBsYXllcl9ub3JtYWxpemF0aW9uKCkgICAjIENyZWF0ZSBhIG5vcm1hbGl6YXRpb24gbGF5ZXIKICAgICAgICBub3JtYWxpemVfaSAlPiUgYWRhcHQoWCkgICAjIEFkYXB0IHRoZSBub3JtYWxpemF0aW9uIGxheWVyIHRvIHRoZSBkYXRhCgogICAgICAgIG1vZGVsX25ldXJhbF9pIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAgICMgQ3JlYXRlIGEgc2VxdWVudGlhbCBtb2RlbAoKICAgICAgICBtb2RlbF9uZXVyYWxfaSAlPiUKICAgICAgICAgIG5vcm1hbGl6ZV9pKCkgJT4lICAgIyBBcHBseSBub3JtYWxpemF0aW9uCgogICAgICAgICAgbGF5ZXJfZGVuc2UobmFtZSA9ICJIaWRkZW5MYXllcl8xIiwgdW5pdHMgPSB3aWR0aFtqXSwgYWN0aXZhdGlvbiA9ICdyZWx1JykgJT4lCiAgICAgICAgICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzIiLCB1bml0cyA9IHdpZHRoW2pdLCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUKICAgICAgICAgIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMyIsIHVuaXRzID0gd2lkdGhbal0sIGFjdGl2YXRpb24gPSAncmVsdScpICU+JQoKICAgICAgICAgICBsYXllcl9kZW5zZShuYW1lID0gIk91dHB1dExheWVyIiwgdW5pdHMgPSAxKSAgICMgQWRkIG91dHB1dCBsYXllciB3aXRoIDEgdW5pdAoKICAgICAgICBtb2RlbF9uZXVyYWxfaSAlPiUgY29tcGlsZShsb3NzPSJNU0UiLCBvcHRpbWl6ZXI9b3B0aW1pemVyX2FkYW0obGVhcm5pbmdfcmF0ZSA9IGxlYXJuaW5nX3JhdGVbaV0pLCBtZXRyaWNzPSJBY2N1cmFjeSIpCgogICAgICAgIG1vZGVsX25ldXJhbF9pICU+JSBmaXQoeCA9IFgsIHkgPSB5LCBlcG9jaHMgPSAyNTAsIHZlcmJvc2UgPSAwKQoKICAgICAgfSBlbHNlIGlmIChsZW5ndGhba10gPT0gMTApIHsgIyBUZW4gbGF5ZXIgbW9kZWwKCiAgICAgICAgbm9ybWFsaXplX2kgPC0gbGF5ZXJfbm9ybWFsaXphdGlvbigpICAgIyBDcmVhdGUgYSBub3JtYWxpemF0aW9uIGxheWVyCiAgICAgICAgbm9ybWFsaXplX2kgJT4lIGFkYXB0KFgpICAgIyBBZGFwdCB0aGUgbm9ybWFsaXphdGlvbiBsYXllciB0byB0aGUgZGF0YQoKICAgICAgICBtb2RlbF9uZXVyYWxfaSA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgICAjIENyZWF0ZSBhIHNlcXVlbnRpYWwgbW9kZWwKCiAgICAgICAgbW9kZWxfbmV1cmFsX2kgJT4lCiAgICAgICAgICBub3JtYWxpemVfaSgpICU+JSAgICMgQXBwbHkgbm9ybWFsaXphdGlvbgoKICAgICAgICAgIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMSIsIHVuaXRzID0gd2lkdGhbal0sIGFjdGl2YXRpb24gPSAncmVsdScpICU+JQogICAgICAgICAgbGF5ZXJfZGVuc2UobmFtZSA9ICJIaWRkZW5MYXllcl8yIiwgdW5pdHMgPSB3aWR0aFtqXSwgYWN0aXZhdGlvbiA9ICdyZWx1JykgJT4lCiAgICAgICAgICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzMiLCB1bml0cyA9IHdpZHRoW2pdLCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUKICAgICAgICAgIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfNCIsIHVuaXRzID0gd2lkdGhbal0sIGFjdGl2YXRpb24gPSAncmVsdScpICU+JQogICAgICAgICAgbGF5ZXJfZGVuc2UobmFtZSA9ICJIaWRkZW5MYXllcl81IiwgdW5pdHMgPSB3aWR0aFtqXSwgYWN0aXZhdGlvbiA9ICdyZWx1JykgJT4lCiAgICAgICAgICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzYiLCB1bml0cyA9IHdpZHRoW2pdLCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUKICAgICAgICAgIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfNyIsIHVuaXRzID0gd2lkdGhbal0sIGFjdGl2YXRpb24gPSAncmVsdScpICU+JQogICAgICAgICAgbGF5ZXJfZGVuc2UobmFtZSA9ICJIaWRkZW5MYXllcl84IiwgdW5pdHMgPSB3aWR0aFtqXSwgYWN0aXZhdGlvbiA9ICdyZWx1JykgJT4lCiAgICAgICAgICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzkiLCB1bml0cyA9IHdpZHRoW2pdLCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUKICAgICAgICAgIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMTAiLCB1bml0cyA9IHdpZHRoW2pdLCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUKCiAgICAgICAgICAgbGF5ZXJfZGVuc2UobmFtZSA9ICJPdXRwdXRMYXllciIsIHVuaXRzID0gMSkgICAjIEFkZCBvdXRwdXQgbGF5ZXIgd2l0aCAxIHVuaXQKCiAgICAgICAgbW9kZWxfbmV1cmFsX2kgJT4lIGNvbXBpbGUobG9zcz0iTVNFIiwgb3B0aW1pemVyPW9wdGltaXplcl9hZGFtKGxlYXJuaW5nX3JhdGUgPSBsZWFybmluZ19yYXRlW2ldKSwgbWV0cmljcz0iQWNjdXJhY3kiKQoKICAgICAgICBtb2RlbF9uZXVyYWxfaSAlPiUgZml0KHggPSBYLCB5ID0geSwgZXBvY2hzID0gMjUwLCB2ZXJib3NlID0gMCkKCiAgICAgIH0KCiAgICAgICMgQ2FsY3VsYXRlIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgcHJlZGljdGlvbnMKICAgICAgZXZhbCA8LSBtb2RlbF9uZXVyYWxfaSAlPiUgZXZhbHVhdGUoWCwgeSwgdmVyYm9zZSA9IDApCiAgICAgIGFjYyA8LSBldmFsWzJdCgogICAgICAjIEFwcGVuZCB0aGUgYWNjdXJhY3kgYW5kIHBhcmFtZXRlciB2YWx1ZXMgdG8gdGhlIHJlc3VsdHMgZGF0YSBmcmFtZQogICAgICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGMoYWNjLCBsZWFybmluZ19yYXRlW2ldLCB3aWR0aFtqXSwgbGVuZ3RoW2tdKSkKICAgIH0KICB9Cn0KCiMgU2V0IGNvbHVtbiBuYW1lcyBmb3IgdGhlIHJlc3VsdHMgZGF0YSBmcmFtZQpjb2xuYW1lcyhyZXN1bHRzKSA8LSBjKCJhY2N1cmFjeSIsICJsZWFybmluZ19yYXRlIiwgIndpZHRoIiwgImxlbmd0aCIpCgojIENyZWF0ZSBhIDNEIHNjYXR0ZXIgcGxvdCB1c2luZyBzY2F0dGVyM0QKcGxvdDNEOjpzY2F0dGVyM0QoeCA9IHJlc3VsdHMkbGVhcm5pbmdfcmF0ZSwgeSA9IHJlc3VsdHMkd2lkdGgsIHogPSByZXN1bHRzJGxlbmd0aCwKICAgICAgICAgICAgICAgICAgdGhldGEgPSAzMCwgcGhpID0gMTAsCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiTGVhcm5pbmcgUmF0ZSIsIHlsYWIgPSAiV2lkdGgiLCB6bGFiID0gIkxlbmd0aCIsCiAgICAgICAgICAgICAgICAgIHRpY2t0eXBlID0gImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgcGNoID0gMTksCiAgICAgICAgICAgICAgICAgIGNleC5heGlzID0gMC43NSwgY2V4LmxhYiA9IDAuNzUsIGV4cGFuZCA9IDAuNzUsCiAgICAgICAgICAgICAgICAgIGNvbHZhciA9IHJlc3VsdHMkYWNjdXJhY3ksIGNvbCA9IHZpcmlkaXMoMTAwKSkKCgojIFNlbGVjdCB0aGUgb3B0aW1hbCBwYXJhbWV0ZXIgY29tYmluYXRpb24gd2l0aCB0aGUgaGlnaGVzdCBhY2N1cmFjeQpvcHRfcGFyYW1zIDwtIHJlc3VsdHNbd2hpY2gubWF4KHJlc3VsdHMkYWNjdXJhY3kpLF0KCm9wdF9wYXJhbXMKCiMgQ2xlYW4gdXAKcm0obGlzdCA9IGMoIlgiLCAieSIsICJpIiwgImoiLCAiayIsICJ3aWR0aCIsICJsZW5ndGgiLCAibGVhcm5pbmdfcmF0ZSIsICJyZXN1bHRzIiwgIm9wdF9wYXJhbXMiLCAibm9ybWFsaXplX2kiLCAibW9kZWxfbmV1cmFsX2kiLCAiZXZhbCIsICJhY2MiKSkKYGBgCkxvb2tpbmcgYXQgdGhlIGdyYXBoLCBpdCBpcyBldmlkZW50IHRoYXQgd2UgaW1wcm92ZSBvdXIgbW9kZWwgd2l0aCBtb3JlIG5ldXJvbnMgcGVyIGxheWVyIGFzIHdlbGwgYXMgdGhlIHdpdGggaGlnaGVyIG51bWJlciBvZiBsYXllcnMgYnV0IGluIG9yZGVyIHRvIHNhdmUgY29tcHV0YXRpb25hbCBtZW1vcnkgd2Ugc3RpbGwgZGVjaWRlZCB0byBjaG9vc2UganVzdCB0aHJlZSBsYXllcnMgYW5kIHNpeHRlZW4gbmV1cm9ucyBwZXIgbGF5ZXIgZm9yIG91ciBtb2RlbC4gRnVydGhlcm1vcmUsIG91ciBpbnZlc3RpZ2F0aW9uIHNob3dzIHRoYXQgdGhlIGJlc3QgbGVhcm5pbmcgcmF0ZSBpbiBvdXIgY2FzZSBpcyB0aGUgZGVmYXVsdCBvZiAwLjAwMS4gCgoKIyBFbnNlbWJsZSBtZXRob2RzCmBgYHtyfQpmb3JtdWxhIDwtIFN1cnZpdmVkIH4gRmlyc3RDbGFzcyArIFNlY29uZENsYXNzICsgU2V4ICsgQWdlICsgU2lic3AgKyBQYXJjaCArIEZhcmUgKyBDaGVyYm91cmcgKyBRdWVlbnN0b3duCgojIEJ1aWxkaW5nIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCB1c2luZyB0aGUgcmFuZG9tRm9yZXN0IGZ1bmN0aW9uCiMgbXRyeSBwYXJhbWV0ZXIgaXMgc2V0IHRvIDksIHdoaWNoIGNvbnRyb2xzIGZlYXR1cmUgc3Vic2FtcGxpbmcKIyBudHJlZSBwYXJhbWV0ZXIgaXMgc2V0IHRvIDUwMCwgc3BlY2lmeWluZyB0aGUgbnVtYmVyIG9mIHRyZWVzIGluIHRoZSByYW5kb20gZm9yZXN0Cm1vZGVsX2Vuc2VtYmxlX2JhZ2dpbmcgPC0gcmFuZG9tRm9yZXN0KGZvcm11bGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeT05LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZT01MDApCgojIENyZWF0aW5nIGEgdmFyaWFibGUgaW1wb3J0YW5jZSBwbG90IGZvciB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbAp2YXJJbXBQbG90KG1vZGVsX2Vuc2VtYmxlX2JhZ2dpbmcsIG1haW49IkJhZ2dpbmc6IFZhcmlhYmxlIEltcG9ydGFuY2UiKQoKIyBDcmVhdGluZyBhIGRhdGEudGFibGUgdG8gY29tcGFyZSBhY3R1YWwgc3Vydml2YWwgdmFsdWVzIHdpdGggcHJlZGljdGVkIHZhbHVlcyBmcm9tIHRoZSBiYWdnaW5nIG1vZGVsCm1vZGVsX2JhZ19yZXN1bHRfY29tcGFyaXNvbiA8LSBkYXRhLnRhYmxlKGFjdHVhbCA9IGRhdGEkU3Vydml2ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb24gPSBwcmVkaWN0KG1vZGVsX2Vuc2VtYmxlX2JhZ2dpbmcsIG5ld2RhdGEgPSBkYXRhKSkKCiMgQnVpbGRpbmcgYSBncmFkaWVudCBib29zdGluZyBtb2RlbCB1c2luZyB0aGUgZ2JtIGZ1bmN0aW9uCiMgZGlzdHJpYnV0aW9uIGlzIHNldCB0byAiZ2F1c3NpYW4iIGZvciByZWdyZXNzaW9uLCBidXQgY291bGQgYmUgbW9kaWZpZWQgZm9yIGNsYXNzaWZpY2F0aW9uCiMgaW50ZXJhY3Rpb24uZGVwdGggc2V0cyB0aGUgbWF4aW11bSBkZXB0aCBvZiB0aGUgaW5kaXZpZHVhbCB0cmVlcwojIG4udHJlZXMgaXMgc2V0IHRvIDUwMCwgaW5kaWNhdGluZyB0aGUgbnVtYmVyIG9mIGJvb3N0aW5nIGl0ZXJhdGlvbnMKIyBzaHJpbmthZ2UgY29udHJvbHMgdGhlIHN0ZXAgc2l6ZSB3aGVuIHVwZGF0aW5nIHRoZSB3ZWlnaHRzIG9mIGVhY2ggdHJlZQojIGN2LmZvbGRzIGlzIHNldCB0byA1LCBzcGVjaWZ5aW5nIHRoZSBudW1iZXIgb2YgY3Jvc3MtdmFsaWRhdGlvbiBmb2xkcyBmb3IgbW9kZWwgZXZhbHVhdGlvbgptb2RlbF9lbnNlbWJsZV9ib29zdGluZyA8LSBnYm0oZm9ybXVsYSA9IGZvcm11bGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb249ImdhdXNzaWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyYWN0aW9uLmRlcHRoPTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLnRyZWVzPTUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNocmlua2FnZT0wLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdi5mb2xkcyA9IDUpCgojIENhbGN1bGF0aW5nIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgYm9vc3RpbmcgbW9kZWwgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbgpwZXJmb3JtYW5jZV9lbnNlbWJsZSA8LSBnYm0ucGVyZihtb2RlbF9lbnNlbWJsZV9ib29zdGluZywgbWV0aG9kID0gImN2IikKCiMgR2VuZXJhdGluZyBhIHN1bW1hcnkgb2YgdGhlIGJvb3N0aW5nIG1vZGVsLCBidXQgcGxvdGl0IHBhcmFtZXRlciBpcyBzZXQgdG8gRkFMU0UsIHNvIG5vIHBsb3Qgd2lsbCBiZSBnZW5lcmF0ZWQKc3VtbWFyeShtb2RlbF9lbnNlbWJsZV9ib29zdGluZywgcGxvdGl0ID0gRkFMU0UpCgojIE1lcmdpbmcgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgZGF0YSBmcm9tIHRoZSBiYWdnaW5nIG1vZGVsIHdpdGggdGhlIHN1bW1hcnkgZGF0YSBmcm9tIHRoZSBib29zdGluZyBtb2RlbAojIFByZXBhcmluZyB0aGUgZGF0YSBmb3IgcGxvdHRpbmcgYnkgdHJhbnNmb3JtaW5nIHBlcmNlbnRhZ2VzIGFuZCBjb252ZXJ0aW5nIHRvIGxvbmcgZm9ybWF0IHVzaW5nIG1lbHQgZnVuY3Rpb24KZHRfdmFyaW1wIDwtIG1lcmdlKGRhdGEudGFibGUoaW1wb3J0YW5jZShtb2RlbF9lbnNlbWJsZV9iYWdnaW5nLCB0eXBlID0gMiksIGtlZXAucm93bmFtZXMgPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgIGRhdGEudGFibGUoc3VtbWFyeShtb2RlbF9lbnNlbWJsZV9ib29zdGluZywgcGxvdGl0ID0gRkFMU0UpKSwKICAgICAgICAgICAgICAgICAgIGJ5LnggPSAicm4iLCBieS55ID0gInZhciIpCmR0X3ZhcmltcCA8LSBzZXROYW1lcyhkdF92YXJpbXAsIGMoIlZhcmlhYmxlIiwgIkJhZ2dpbmciLCAiQm9vc3RpbmciKSkKZHRfdmFyaW1wWywgQmFnZ2luZyA6PSAxMDAqQmFnZ2luZy9zdW0oQmFnZ2luZyldCmR0X3ZhcmltcCA8LSBtZWx0KGR0X3ZhcmltcCwgaWQudmFycyA9ICJWYXJpYWJsZSIsCiAgICAgICAgICAgICAgICAgIG1lYXN1cmUudmFycyA9IGMoIkJhZ2dpbmciLCJCb29zdGluZyIpLAogICAgICAgICAgICAgICAgICB2YXJpYWJsZS5uYW1lID0gInkiLAogICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIngiKQpkdF92YXJpbXAgPC0gZHRfdmFyaW1wW29yZGVyKHksLXgpXQoKIyBQbG90dGluZyB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBjb21wYXJpc29uIHVzaW5nIGdncGxvdDIKZ2dwbG90KGRhdGEgPSBkdF92YXJpbXApICsKICBnZW9tX2NvbChhZXMoeT1WYXJpYWJsZSwgeD14LCBmaWxsPXkpKSArCiAgbGFicygKICAgIHggPSAiUmVsYXRpdmUgSW1wb3J0YW5jZSIsCiAgICB5ID0gIlZhcmlhYmxlcyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjZjFiMTQ3IiwiIzRDQTdERSIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpLAogICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGZhY2V0X3dyYXAofnkpCgpybShsaXN0ID0gYygiZHRfdmFyaW1wIiwgIm1vZGVsX2JhZ19yZXN1bHRfY29tcGFyaXNvbiIsICJwZXJmb3JtYW5jZV9lbnNlbWJsZSIpKQpgYGAKSW4gYW5hbHlzaXMgb2YgdHdvIGVuc2VtYmxlIG1ldGhvZHMgd2hpY2ggYXJlIEJhZ2dpbmcgYW5kIEJvb3N0aW5nLCBpdCBpcyBjbGVhciB0aGF0IGFmdGVyIFNleCBhbmQgQWdlLCBQYXNzZW5nZXIgRmFyZSBpcyB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGUuIFRoaXMgc3VwcG9ydHMgb3VyIGFzc3VtcHRpb24gdGhhdCB0aGUgd2VhbHRoIG9mIHBhc3NlbmdlcnMgbWF5IGFjdHVhbGx5IGluZmx1ZW5jZSBjaGFuY2VzIG9mIHN1cnZpdmFsIGhlYXZpbHkuIFRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIGRhdGEgc3VwcG9ydHMgdGhlIGFzc3VtcHRpb24gdGhhdCBGYXJlIGRvZXMgbm90IGRlcGVuZCBvbiB0aGUgZmFtaWx5IHNpemUgYW5kIHRoZSBjbGFzcyBvZiB0aGUgcGFzc2VuZ2VyIGFsc28gZG9lcyBub3QgaGF2ZSBhbiBpbXBhY3Qgb24gc3Vydml2YWwgY2hhbmNlcy5Db21wYXJpbmcgdGhlIHR3byBtb2RlbHMgc2hvd3MgdGhhdCBib3RoIHRhcmdldCB0aGUgc2FtZSB2YXJpYWJsZXMuCgoKIyMgSW52ZXN0aWdhdGluZyBoeXBlcnBhcmFtZXRlcnMgZm9yIHRoZSBib29zdGluZwpgYGB7cn0KIyMjIEludmVzdGlnYXRpbmcgaHlwZXJwYXJhbWV0ZXJzCiMgRGVmaW5lIHRoZSB2YWx1ZXMKbl90cmVlcyA8LSBzZXEoZnJvbSA9IDI1MCwgdG8gPSA3NTAsIGJ5ID0gMjUwKQpzaHJpbmtpbmcgPC0gc2VxKGZyb20gPSAwLjA1LCB0byA9IDAuMiwgYnkgPSAwLjA1KQpjdl9mb2xkcyA8LSBzZXEoZnJvbSA9IDIsIHRvID0gOCwgYnkgPSAzKQoKIyBDcmVhdGUgYW4gZW1wdHkgZGF0YSBmcmFtZSB0byBzdG9yZSB0aGUgcmVzdWx0cwpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoKQoKIyBJdGVyYXRlIG92ZXIgdGhlIGNvbWJpbmF0aW9ucwpmb3IgKGkgaW4gMTpsZW5ndGgobl90cmVlcykpIHsKICBmb3IgKGogaW4gMTpsZW5ndGgoc2hyaW5raW5nKSkgewogICAgZm9yIChrIGluIDE6bGVuZ3RoKGN2X2ZvbGRzKSkgewogICAgICAjIEJ1aWxkIGEgIG1vZGVsIHdpdGggdGhlIHNwZWNpZmllZCBwYXJhbWV0ZXJzCiAgICAgIG1vZGVsICA8LSBnYm0oZm9ybXVsYSA9IGZvcm11bGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb249ImdhdXNzaWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyYWN0aW9uLmRlcHRoPTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLnRyZWVzPW5fdHJlZXNbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaHJpbmthZ2U9c2hyaW5raW5nW2pdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3YuZm9sZHMgPSBjdl9mb2xkc1trXSkKCiAgICAgICMgQ2FsY3VsYXRlIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgcHJlZGljdGlvbnMKICAgICAgYWNjIDwtIGdibS5wZXJmKG1vZGVsLCBtZXRob2QgPSAiY3YiLCBwbG90Lml0ID0gRkFMU0UpCgogICAgICAjIEFwcGVuZCB0aGUgYWNjdXJhY3kgYW5kIHBhcmFtZXRlciB2YWx1ZXMgdG8gdGhlIHJlc3VsdHMgZGF0YSBmcmFtZQogICAgICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGMoYWNjLCBuX3RyZWVzW2ldLCBzaHJpbmtpbmdbal0sIGN2X2ZvbGRzW2tdKSkKICAgIH0KICB9Cn0KCiMgU2V0IGNvbHVtbiBuYW1lcyBmb3IgdGhlIHJlc3VsdHMgZGF0YSBmcmFtZQpjb2xuYW1lcyhyZXN1bHRzKSA8LSBjKCJhY2N1cmFjeSIsICJuX3RyZWVzIiwgInNocmlua2luZyIsICJjdl9mb2xkcyIpCgojIENyZWF0ZSBhIDNEIHNjYXR0ZXIgcGxvdCB1c2luZyBzY2F0dGVyM0QKcGxvdDNEOjpzY2F0dGVyM0QoeCA9IHJlc3VsdHMkbl90cmVlcywgeSA9IHJlc3VsdHMkc2hyaW5raW5nLCB6ID0gcmVzdWx0cyRjdl9mb2xkcywKICAgICAgICAgICAgICAgICAgdGhldGEgPSAzMCwgcGhpID0gMTAsCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiTnVtYmVyIG9mIHRyZWVzIiwgeWxhYiA9ICJTaHJpbmthZ2UiLCB6bGFiID0gIkNWIGZvbGRzIiwKICAgICAgICAgICAgICAgICAgdGlja3R5cGUgPSAiZGV0YWlsZWQiLAogICAgICAgICAgICAgICAgICBwY2ggPSAxOSwKICAgICAgICAgICAgICAgICAgY2V4LmF4aXMgPSAwLjc1LCBjZXgubGFiID0gMC43NSwgZXhwYW5kID0gMC43NSwKICAgICAgICAgICAgICAgICAgY29sdmFyID0gcmVzdWx0cyRhY2N1cmFjeSwgY29sID0gdmlyaWRpcygxMDApKQoKCiMgU2VsZWN0IHRoZSBvcHRpbWFsIHBhcmFtZXRlciBjb21iaW5hdGlvbiB3aXRoIHRoZSBoaWdoZXN0IGFjY3VyYWN5Cm9wdF9wYXJhbXMgPC0gcmVzdWx0c1t3aGljaC5tYXgocmVzdWx0cyRhY2N1cmFjeSksXQoKcHJpbnQob3B0X3BhcmFtcykKCiMgQnVpbGQgYSBtb2RlbCB1c2luZyB0aGUgb3B0aW1hbCBwYXJhbWV0ZXJzCm1vZGVsX29wdCAgPC0gZ2JtKGZvcm11bGEgPSBmb3JtdWxhLAogICAgICAgICAgICAgICBkYXRhPWRhdGEsCiAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbj0iZ2F1c3NpYW4iLAogICAgICAgICAgICAgICBpbnRlcmFjdGlvbi5kZXB0aD0xLAogICAgICAgICAgICAgICBuLnRyZWVzID0gb3B0X3BhcmFtcyRuX3RyZWVzLAogICAgICAgICAgICAgICBzaHJpbmthZ2UgPSBvcHRfcGFyYW1zJHNocmlua2luZywKICAgICAgICAgICAgICAgY3YuZm9sZHMgPSBvcHRfcGFyYW1zJGN2X2ZvbGRzKQoKIyBDYWxjdWxhdGUgdGhlIG1lYW4gYWNjdXJhY3kgb2YgdGhlIG1vZGVsIHByZWRpY3Rpb25zCm1lYW4ocHJlZGljdChtb2RlbF9vcHQsIGRhdGEpID09IGRhdGEkU3Vydml2ZWQpCgojIENsZWFuIHVwCnJtKGxpc3QgPSBjKCJhY2MiLCAiY3ZfZm9sZHMiLCAiaSIsICJqIiwgImsiLCAibl90cmVlcyIsICJzaHJpbmtpbmciLCAicmVzdWx0cyIsICJvcHRfcGFyYW1zIiwgIm1vZGVsX29wdCIpKQpgYGAKCkFzIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgYm9vc3RpbmcgbW9kZWwgc2VlbXMgdG8gdmFyeSwgd2UgZGlkIG5vdCBjaGFuZ2Ugb3VyIGh5cGVycGFyYW1ldGVycyBmcm9tIG91ciBkZWZhdWx0IG9wdGlvbnMuIFRob3VnaCBzaW5jZSBiYWdnaW5nIGRvZXMgbGVhZCB0byB0aGUgYmVzdCByZXN1bHRzLCB3ZSBhcmUgY29uZmlkZW50IGluIG91ciBoeXBlcnBhcmFtZXRlciBjaG9pY2UuCgo8YnI+Cjxicj4KCiMgUENBCgpDb3ZhcmlhbmNlIG1hdHJpeCBkZXNjcmliZSB0aGUgZGF0YSB0byBmaW5kaW5nIHRoZSBiZXN0IGRpcmVjdGlvbiB3aGVyZSB0aGUgZGF0YSBtb3N0IHNwcmVhZC4KU28gbGV0IHVzIGZpbmQgdGhlIGNvdmFyaWFuY2UgbWF0cml4IG9mIHRoZSBkYXRhOgpjZW50ZXIgdGhlIGRhdGEgcG9pbnRzIGFuZCBjYWxjdWxhdGUgdGhlIHZhcmlhbmNlcyBhbmQgY292YXJpYW5jZXMKTmV4dCwgZmluZCB0aGUgZWlnZW52ZWN0b3JzIGFuZCB0aGUgY29ycmVzcG9uZGluZyBlaWdlbnZhbHVlcyBvZiB0aGUgY292YXJpYW5jZSBtYXRyaXguClNvcnQgdGhlIGVpZ2VudmVjdG9ycyB2IGJ5IHRoZSBhYnNvbHV0ZSBtYWduaXR1ZGUgb2YgdGhlaXIgZWlnZW52YWx1ZXM6IHRoZXNlIGFyZSBvdXIgcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDcykgYW5kIHRoZSBiYXNpcyB2ZWN0b3JzIG9mIG91ciBuZXcgY29vcmRpbmF0ZSBzeXN0ZW0uCgojIyBSZWR1Y2luZyBwcmluY2lwbGUgY29tcG9uZW50cwpXZSBhcmUgYmFjayB0byBkcm9wcGluZyBkaW1lbnNpb25zIGJ1dCB0aGlzIHRpbWUgd2UgZHJvcCBwcmluY2lwYWwgY29tcG9uZW50cyBpbnN0ZWFkIG9mIF9fdmFyaWFibGVzX18uIFdlIGhvcGUgdG8gZ3JlYXRseSByZWR1Y2UgdGhlIFBDcyB3aGlsZSBjYXB0dXJpbmcgbW9zdCB2YXJpb2F0aW9uIGFsb25nIHRoZSBmaXJzdCBQQ3MuCgpXZSBtYXkgd2FudCB0byBjYXB0dXJlIGEgbWluaW11bSBhbW91bnQgb2YgdmFyaWFuY2Ugb2YgdGhlIGRhdGEgaW4gdG90YWwgb3Iga2VlcCBhbGwgdGhlIHByaW5jaXBsZSBjb21wb25lbnRzIHRoYXQgLS1pbmRpdmlkdWFsbHktLSBleHBsYWluIGEgbWluaW11bSBhbW91bnQgb2YgdmFyaWFuY2UuClNpbWlsYXIgdG8gay1tZWFucywgd2UgY2FuIHByb2R1Y2UgYSBzY3JlZSBwbG90IG9mIG91ciBwcmluY2lwYWwgY29tcG9uZW50cyBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIHRoZWlyIGVpZ2VudmFsdWVzLgoKV2hhdCBpcyBtb3JlIGlzIGluc3RlYWQgb2YgZHJvcHBpbmcgUENzIHdlIG1heSBiZSBpbnRlcmVzdGVkIGluIGV4cGxvcmluZyBhbmQgcG9zc2libHkgaW50ZXJwcmV0aW5nIHRoZW0uCkNlcnRhaW4gcmVhbCBlc3RhdGUgdmFyaWFibGVzIG1heSBkcml2ZSB0aGUgUEMxIGNvb3JkaW5hdGUgKGFtZW5pdGllcykgd2hpbGUgb3RoZXJzIGRyaXZlIHRoZSBQQzIgY29vcmRpbmF0ZSAobG9jYXRpb24pLgpXZSBoYXZlIGFscmVhZHkgc2VlbiB0aGF0IHByaW5jaXBsZSBjb21wb25lbnRzIG5vIGxvbmdlciByZXByZXNlbnQgc2luZ2xlIHZhcmlhYmxlcyBhcyBvdXIgb3JpZ2luYWwgYmFzaXMgdmVjdG9ycyBkaWQuIEVhY2ggUEMgY29vcmRpbmF0ZSBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0aGUgb3JpZ2luYWwgdmFyaWFibGVzLiBUaGUgZWlnZW4gdmVjdG9ycyB2IGRldGVybWluZSBob3cgc2luZ2xlIHZhcmlhYmxlcyBvZiB4IGNvbnRyaWJ1dGUgdG8gZWFjaCBQQyBjb29yZGluYXRlIHZpYSB2XlR4LgpgYGB7cn0KIyBXZSBleGNsdWRlIHRoaXJkQ2xhc3MgYmVjYXVzZSBpdCBpcyBvdXIgYmFzZWxpbmUgY2xhc3MuIApkdF9wY2EgPC0gZGF0YS50YWJsZShzY2FsZShkYXRhWywgYygiQWdlIiwgIkZhcmUiLCAiU2V4IiwgIlBhcmNoIiwgIlNpYnNwIiwgIlF1ZWVuc3Rvd24iLCAiQ2hlcmJvdXJnIiwgIkZpcnN0Q2xhc3MiLCAiU2Vjb25kQ2xhc3MiKV0pKQojIFdpbGwgYWx3YXlzIHVzZSBhIGxpYnJhcnkgd2hpY2ggZGVjb21wb3NlcyB0aGUgY292YXJpYW5jZSBtYXRyaXggZm9yIHVzIGFuZCBzdXBwbGllcyBvdGhlciBmdW5kYW1lbnRhbCBvdXRwdXRzLgptb2RlbF9wY2EgPC0gcHJjb21wKGR0X3BjYSkKc3VtbWFyeShtb2RlbF9wY2EpCgpgYGAKSXQgaXMgY2xlYXIgdGhhdCB0aGUgbW9kZWwgZW5hYmxlcyB1cyB0byBkaXZlIGludG8gb3VyIDkgcHJpbmNpcGxlIGNvbXBvbmV0cy4gSW4gdGhlIHN1bW1hcnksIHdlIGFsc28gZmluZCB0aGUgc2hhcmUgdGhhdCBlYWNoIFBDIGNvbnRyaWJ1dGVzIHRvIGV4cGxhaW5nIHRoZSB2YXJpYW5jZSBvZiBvdXIgZGF0YSBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIHRoZSBlaWdlbnZhbHVlcy4KCmBgYHtyfQpmdml6X2VpZyhtb2RlbF9wY2EsIGFkZGxhYmVscyA9IFRSVUUpICsgCiAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKTG9va2luZyBhdCB0aGUgU2NyZWUgcGxvdCwgaXQgaXMgZXZpZGVuY2UgdGhhdCB3ZSBleHBsYWluIH42NiUgb2YgdGhlIHZhcmlhbmNlIHdpdGggZmlyc3QgNCBQQ3MuIAoKVG8gaW52ZXN0aWdhdGUgaG93IG91ciBvcmlnaW5hbCB2YXJpYWJsZXMgY29udHJpYnV0ZSB0byB0aGUgUENzIGFuZCB0byBwb3NzaWJseSBpbnRlcnByZXQgdGhlc2UgY29udHJpYnV0aW9ucywgd2UgY2FuIGxvb2sgYXQgdGhlIGVpZ2VudmVjdG9ycyB2LiBUaGV5IGVzc2VudGlhbGx5IGNvbnJpYnV0ZSB0aGUgY29lZmZpY2llbnRzIHRvIHRoZSBsaW5lYXIgY29tYmluYXRpb24gb2Ygb3VyIHZhcmlhYmxlcyB0aGF0IHByb2R1Y2UgdGhlIFBDcy4gV2UgZmluZCB0aGUgZWlnZW52ZWN0b3JzIGF0IGBtb2RlbF9wY2Ekcm90YXRpb25gLgoKSGVyZSwgYCRyb3RhdGlvbmAgZ2l2ZXMgdGhlIGVpZ2VudmVjdG9ycyBzY2FsZWQgdG8gYSBzcGVjaWZpYyBsZW5ndGggd2hpY2ggYXJlIGFsc28gY2FsbGVkIGxvYWRpbmdzOiBlaWdlbnZlY3RvcnMg4oiXIOKImmVpZ2VudmFsdWVzCiAKIFRoZSBhYm92ZSBlaWdlbnZlY3RvcnMgdGFrZSBlYWNoIGRhdGEgcG9pbnQgdG8gdGhlIFBDIGNvb3JkaW5hdGUgc3lzdGVtIGJ5IG11bHRpcGx5aW5nIHZlY3RvciB4LiBWOiB4Vi4KIApMZXQncyBpbGx1c3RyYXRlIFNjb3JlIFBsb3QuIEl0IHBsb3RvcyBvdXIgcGFzc2VuZ2VycyBpbiBvbmx5IHRoZSBmaXJzdCB0d28gZGltZW5zaW9ucyAoUEMxIGFuZCBQQzIpIHdoaWNoIGNhcHR1cmUgNTUlIG9mIHZhcmlhbmNlLiAKIApGaW5hbGx5LCBsZXQgdXMgaWxsdXN0cmF0ZSBvdXIgcHJpbmNpcGxlIGNvbXBvbmVudHMgaW4gYSBsb2FkaW5ncyBwbG90LiBCYXNpY2FsbHksIHRoZSBkaXJlY3Rpb24gb2YgYW4gYXJyb3cgc2hvd3Mgd2hpY2ggUENzIGEgdmFyaWFibGUgY29udHJpYnV0ZXMgdG8gYW5kIHRoZSBhbmdsZXMgYmV0d2VlbiBhcnJvd3MgaW5kaWNhdGUgdGhlIGRpZmZlcmVuY2UgaW4gdGhlc2UgZGlyZWN0aW9ucy4gCmBgYHtyfQojIE1hdHJpeCBvZiAoc2NhbGVkKSBlaWdlbnZlY3RvcnM6IFYKcm91bmQobW9kZWxfcGNhJHJvdGF0aW9uLCAyKQoKZnZpel9wY2FfaW5kKG1vZGVsX3BjYSwgCiAgICAgICAgICAgICBnZW9tLmluZCA9ICJwb2ludCIsIAogICAgICAgICAgICAgY29sLmluZCA9IGFzLmZhY3RvcihkYXRhJFN1cnZpdmVkKSwgCiAgICAgICAgICAgICBwYWxldHRlID0gYygiI2YxYjE0NyIsICIjNENBN0RFIikpICsKICBnZ3RpdGxlKCJTY29yZSBQbG90IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKZnZpel9wY2FfdmFyKG1vZGVsX3BjYSwgCiAgICAgICAgICAgICBjb2wudmFyPSJjb250cmliIiwgCiAgICAgICAgICAgICByZXBlbCA9IFRSVUUpICsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCldlIGZpbmQgaGVyZSB0aGF0IEZhcmUgYW5kIEZpcnN0IENsYXNzIHNlZW0gdG8gaW5kaWNhdGUgc3Ryb25nbHkgaW4gb25lIGRpcmVjdGlvbiBwb3NzaWJseSBzaWduaWZ5aW5nIHdlYWx0aC4gU2Vjb25kbHksIFNpYnNwIGFuZCBQYXJjaCBpbmRpYWN0ZSBjbG9zZWx5IHRvZ2V0aGVyIHBvaW50aW5nIGF0IGFuIG92ZXJhcmNoaW5nIGNvbmNlcHQgb2YgZmFtaWx5IHNpemUuCgoKIyMgUmVjb25zdHJ1Y3RpbmcgVmFyaWFibGVzCk91ciBtYXRyaXggb2YgKHNjYWxlZCkgZWlnZW52ZWN0b3JzIFYgYWJvdmUgdHJhbnNmb3JtZWQgb3VyIG9yaWdpbmFsIDkgY29vcmRpbmF0ZXMgKHZhcmlhYmxlcykgaW50byA5IFBDIGNvb3JkaW5hdGVzLgpgYGB7cn0KIyBJbnNlcnQgUENBIGludG8gb3VyIGRhdGEgZm9yIGNvbnRpbnVlZCB1c2UKcGNhIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KGR0X3BjYSkgJSolCiAgYXMubWF0cml4KG1vZGVsX3BjYSRyb3RhdGlvbikpCgpkYXRhIDwtIGNiaW5kKGRhdGEsIHBjYSkKCiMgRGVmaW5lIGEgZnVuY3Rpb24gdG8gcGVyZm9ybSBQQ0EgdHJhbnNmb3JtYXRpb25zIG9uIHRoZSB0ZXN0IGRhdGEKYXBwbHlfcGNhIDwtIGZ1bmN0aW9uKGRhdGEsIG1vZGVsKSB7CiAgZHRfcGNhIDwtIGRhdGEudGFibGUoc2NhbGUoZGF0YVssIGMoIkFnZSIsICJGYXJlIiwgIlNleCIsICJQYXJjaCIsICJTaWJzcCIsICJRdWVlbnN0b3duIiwgIkNoZXJib3VyZyIsICJGaXJzdENsYXNzIiwgIlNlY29uZENsYXNzIildKSkKICAKICBwY2EgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoZHRfcGNhKSAlKiUKICAgIGFzLm1hdHJpeChtb2RlbCRyb3RhdGlvbikpCiAgCiAgcmV0dXJuKGNiaW5kKGRhdGEsIHBjYSkpCn0KCmBgYAoKVG8gcmVjb25zdHJ1Y3QgdGhlIG9yaWdpbmFsIHZhcmlhYmxlcyB3ZSBjYW4gbXVsdGlwbHkgdGhlIFBDIGNvb3JkaW5hdGVzIGJ5IFZeX1RfXjoKYGBge3J9CiMgUGFzc2VuZ2VyIDE6IE9yaWdpbmFsIHZhcmlhYmxlcwojIHgKcm91bmQoYXMubWF0cml4KGR0X3BjYSlbMSxdLDIpCgojIFBhc3NlbmdlciAxOiBQQyBjb29yZGluYXRlcwojIHggKiBWCnJvdW5kKGFzLm1hdHJpeChkdF9wY2EpWzEsXSAlKiUKICAgICAgICBhcy5tYXRyaXgobW9kZWxfcGNhJHJvdGF0aW9uKSwgMikKCiMgUGFzc2VuZ2VyIDE6IFJlY29uc3RydWN0ZWQgb3JpZ2luYWwgdmFyaWFibGVzCiMgeCAqIFYgKiBWVApyb3VuZChhcy5tYXRyaXgoZHRfcGNhKVsxLF0gJSolCiAgICAgICAgYXMubWF0cml4KG1vZGVsX3BjYSRyb3RhdGlvbikgJSolCiAgICAgICAgdChhcy5tYXRyaXgobW9kZWxfcGNhJHJvdGF0aW9uKSksIDIpCmBgYAoKSG93ZXZlciwgbm90IG11Y2ggaXMgZ2FpbmVkIGluIHRlcm1zIG9mIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBpZiB3ZSBzaW1wbHkga2VlcCA3IFBDIGNvb3JkaW5hdGVzIGluc3RlYWQgb2YgMTAgb3JpZ2luYWwgdmFyaWFibGVzLiBXZSBoYXZlIHNlZW4gYXQgdGhlIHZlcnkgYmVnaW5uaW5nIHRoYXQgdGhlIGZpcnN0IDQgUENzIGNhcHR1cmUgPjY1JSBvZiB0aGUgdmFyaWFuY2Ugb2Ygb3VyIGRhdGEsIHNvIGxldCB1cyByZWR1Y2UgdGhlIHByaW5jaXBsZSBjb21wb25lbnRzIHRvIDQuIFdlIHdpbGwgY2FsbCBvdXIgZWlnZW52ZWN0b3IgbWF0cml4IFYgd2l0aCBvbmx5IHRoZSBmaXJzdCA0IGNvbHVtbnMgVn5yZWR1Y2VkfiAgSWYgd2UgcHJvamVjdGVkIG91ciBwYXNzZW5nZXIgMSBvbnRvIG9ubHkgNCBQQ3MsIHdlIGhhZCBvbmx5IDQgdmFsdWVzIHRvIHN0b3JlOiB4Vn5yZWR1Y2VkfgoKYGBge3J9CiMgeCAqIFZfcmVkdWNlZAphcy5tYXRyaXgoZHRfcGNhKVsxLF0gJSolCiAgYXMubWF0cml4KG1vZGVsX3BjYSRyb3RhdGlvblssMTo0XSkKCiMgeCAqIFZfcmVkdWNlZCAqIFZfcmVkdWNlZFQKYXMubWF0cml4KGR0X3BjYSlbMSxdICUqJQogIGFzLm1hdHJpeChtb2RlbF9wY2Ekcm90YXRpb25bLDE6NF0pICUqJQogIHQoYXMubWF0cml4KG1vZGVsX3BjYSRyb3RhdGlvblssMTo0XSkpCmBgYAoKSWYgd2UgaGF2ZSBWfnJlZHVjZWR+ICB3ZSBjYW4gcmVjb25zdHJ1Y3QgYW55IHBhc3NlbmdlciBmcm9tIGl0cyA0IFBDIHZhbHVlcyB2aWEgeFZ+cmVkdWNlZH4gVl5UXn5yZWR1Y2VkfgpJbiBvdGhlciB3b3Jkcywgd2Ugb25seSBuZWVkIHRoZSBQQyBjb29yZGluYXRlcyB4Vn5yZWR1Y2VkfiBhbmQgY2FuIHJlY29uc3RydWN0IHRoZSBvcmlnaW5hbCBkYXRhLiBUaGlzIHJlY29uc3RydWN0aW9uIGlzIGFwcHJveGltYXRlIGFuZCBkZXBlbmRzIG9uIHRoZSBudW1iZXIgb2YgUENzIHdlIHVzZS4gQ29tcGFyZSB0aGUgb3JpZ2luYWwgdmFyaWFibGUgdmFsdWVzIGFib3ZlIHRvIHRoZSByZWNvbnN0cnVjdGlvbiBiZWxvdy4KCkhvdyBtYW55IGRpbWVuc2lvbnMgd2UgY2FuL3NoYWxsIGRyb3AgZGVwZW5kcyBvbiBvdXIgcHVycG9zZS4gTGV0IHVzIGludmVzdGlnYXRlIHRoZSB0b3RhbCByZWNvbnN0cnVjdGlvbiBlcnJvciBmb3Igb3VyIG9uZSBwYXNzZW5nZXIgZGVwZW5kaW5nIG9uIHRoZSBudW1iZXIgb2YgZWlnZW52ZWN0b3JzIHdlIHJldGFpbiBpbiBWfnJlZHVjZWR+LiBXZSBsb29wIHRocm91Z2ggdGhlIHJlY29uc3RydWN0aW9uIG9mIHBhc3NlbmdlciAxIGZvciAxIHRvIDkgUENzOgoKYGBge3J9CnJlc3VsdHMgPC0gbGlzdCgpCgpmb3IgKHBjcyBpbiAxOjkpewogIHBhc3Nlbmdlcl9yZWNvbnN0cnVjdGVkIDwtIGFzLm1hdHJpeChkdF9wY2EpWzEsXSAlKiUgCiAgICBhcy5tYXRyaXgobW9kZWxfcGNhJHJvdGF0aW9uWywxOnBjc10pICUqJSAKICAgIHQoYXMubWF0cml4KG1vZGVsX3BjYSRyb3RhdGlvblssMTpwY3NdKSkgCiAgCiAgcmVzdWx0c1tbcGNzXV0gPC0gZGF0YS50YWJsZSgiUENzIHVzZWQiID0gcGNzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1BRSI9IHN1bShhYnMoYXMubWF0cml4KGR0X3BjYSlbMSxdLXBhc3Nlbmdlcl9yZWNvbnN0cnVjdGVkKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzZW5nZXJfcmVjb25zdHJ1Y3RlZCkKICB9CgpkdF9yZWNvbnN0cnVjdGlvbiA8LSByYmluZGxpc3QocmVzdWx0cykKcm91bmQoZHRfcmVjb25zdHJ1Y3Rpb24sMikKYGBgCgpgYGB7cn0KIyBDbGVhbiB1cApybShsaXN0ID0gYygiZHRfcGNhIiwgImR0X3JlY29uc3RydWN0aW9uIiwgInBjYSIsICJwYXNzZW5nZXJfcmVjb25zdHJ1Y3RlZCIsICJwY3MiLCAicmVzdWx0cyIpKQoKYGBgCgojIE1vZGVscyB3aXRoIFBDQQojIyBQQ0E6IExvZ2lzdGljIHJlZ3Jlc3Npb24KYGBge3J9CiMgRGVmaW5lIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIHVzaW5nIFBDQXMKbW9kZWxfcGNhX2dsbSA8LSBnbG0oZm9ybXVsYSA9IFN1cnZpdmVkIH4gUEMxICsgUEMyICsgUEMzICsgUEM0LCAKICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSwgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQoKIyBQcmludCBzdW1tYXJ5IHN0YXRpc3RpYwpwcmludChzdW1tYXJ5KG1vZGVsX3BjYV9nbG0pKQoKIyBGaXQgb2YgdGhlIG1vZGVsCndpdGgobW9kZWxfcGNhX2dsbSwgcGNoaXNxKG51bGwuZGV2aWFuY2UgLSBkZXZpYW5jZSwgZGYubnVsbCAtIGRmLnJlc2lkdWFsLCBsb3dlci50YWlsID0gRkFMU0UpKQojIEEgcC12YWx1ZSBiZWxvdyAwLjAwMSBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgcGVyZm9ybXMgYmV0dGVyIHRoYW4gYW4gZW1wdHkgbW9kZWwKCmBgYApBcyBhbGwgaW5wdXRzIHNlZW0gdG8gYmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhdCB0aGUgMC4xJSBsZXZlbC4KCiMjIFBDQTogVHJlZQpgYGB7cn0KIyMjIEJhc2UgdHJlZSAKIyBCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgcnBhcnQKbW9kZWxfdHJlZSA8LSBycGFydChkYXRhID0gZGF0YSwgZm9ybXVsYSA9IFN1cnZpdmVkIH4gUEMxICsgUEMyICsgUEMzICsgUEM0LCBtZXRob2QgPSAiY2xhc3MiKQoKIyBQbG90IHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycCwgd2l0aCBleHRyYSBkZXRhaWxzIChleHRyYSA9IDIpCnBycChtb2RlbF90cmVlLCBleHRyYSA9IDIsIG1haW4gPSAiQmFzZSB0cmVlIGZvciBtb2RlbCIpCgojIFByaW50IGFuIHBsb3QgZGF0YSBvbiB0cmVlCnByaW50Y3AobW9kZWxfdHJlZSkKcGxvdGNwKG1vZGVsX3RyZWUpCiMgVGhlIHRyZWUgc2hvd3MgdGhhdCB0aGUgZmlyc3QgZGVjaWRpbmcgZmFjdG9yIHNlZW1zIHRvIGJlIGdlbmRlci4gQWZ0ZXJ3YXJkcyBtYWlubHkgY2xhc3MgYW5kIGFnZSBzaG93IHNlZW0gcHJvbWluZW50IGRlY2lkaW5nIGZhY3RvcnMgZm9yIGJyYW5jaGVzIHRob3VnaCBpbnRlcmVzdGluZ2x5IGVub3VnaCBmYXJlIHNlZW1zIHRvIHBsYXkgYW4gaW1wb3J0YW50IHJvbGUgYXMgd2VsbC4KCiMgRGV0ZXJtaW5lIHRoZSBtaW5pbXVtIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgZm9yIHBydW5pbmcKI2NwIHBhcmFtZXRlcjogY2FwdHVyZXMgbW9kZWwgY29tcGxleGl0eQptaW5fY3AgPC0gbW9kZWxfdHJlZSRjcHRhYmxlW3doaWNoLm1pbihtb2RlbF90cmVlJGNwdGFibGVbLCAieGVycm9yIl0pLCAiQ1AiXQoKIyBQcnVuZSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyB0aGUgbWluaW11bSBjcAptb2RlbF90cmVlXzIgPC0gcHJ1bmUobW9kZWxfdHJlZSwgY3AgPSBtaW5fY3ApCgojIFBsb3QgdGhlIHBydW5lZCBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHBycCwgd2l0aCBleHRyYSBkZXRhaWxzIChleHRyYSA9IDIpCnBycChtb2RlbF90cmVlXzIsIGV4dHJhID0gMiwgbWFpbiA9ICJQcnVuZWQgYmFzZSB0cmVlIHdpdGggbGVzcyBjb21wbGV4aXR5IikKCiMgVGhlIGJyYW5jaCBkZXRlcm1pbmVkIGJ5IGJlaW5nIG1hbGUgYW5kIG92ZXIgdGhlIGFnZSBvZiA2IHNob3dzIHRoZSBiaWdnZXN0IGdyb3VwIGFuZCB0aGUgbW9zdCB3cm9uZyBndWVzc2VzIG9mIHRoZSBtb2RlbC4KCiMgQ2FsY3VsYXRlIHRoZSBtZWFuIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCBwcmVkaWN0aW9ucwptZWFuKHByZWRpY3QobW9kZWxfdHJlZSwgZGF0YSwgdHlwZSA9ICJjbGFzcyIpPT1kYXRhJFN1cnZpdmVkKQoKbWVhbihwcmVkaWN0KG1vZGVsX3RyZWVfMiwgZGF0YSwgdHlwZSA9ICJjbGFzcyIpPT1kYXRhJFN1cnZpdmVkKQoKIyBCZXR3ZWVuIHRoZSBhY2N1cmFjeSBmb3IgdGhlIHBydW5lZCBhbmQgbm90LXBydW5lZCBtb2RlbCB0aGVyZSBpcyBvbmx5IGEgMiUgZGlmZmVyZW5jZSBzbyB3ZSB3aWxsIGNob29zZSB0aGUgcHJ1bmVkIG1vZGVsIGFzIHJlcHJlc2VudGF0aXZlIGFzIGl0IGlzIGxlc3MgY29tcGxleAptb2RlbF9wY2FfdHJlZXMgPC0gbW9kZWxfdHJlZQoKIyBQcmludCBhbiBwbG90IGRhdGEgb24gdHJlZQpwcmludGNwKG1vZGVsX3RyZWVfMikKcGxvdGNwKG1vZGVsX3RyZWVfMikKCnJtKGxpc3QgPSBjKCJtb2RlbF90cmVlIiwgIm1vZGVsX3RyZWVfMiIpKQpgYGAKCkFzIHdlIGZpbmQgYSBub3RhYmxlIGRpc3Bhcml0eSBpbiBhY2N1cmFjeSBiZXR3ZWVuIHRoZSBiYXNlIHRyZWUgYW5kIHRoZSBwcnVuZWQgdHJlZSBmYXZvcmluZyB0aGUgdW4tcHJ1bmVkIGJhc2UgdHJlZSBpbiB0aGUgY2FzZSBvZiB1c2luZyBQQ0EsIHdlIGNob29zZSB0aGUgdW4tcHJ1bmVkIG1vZGVsIGZvciBmdXJ0aGVyIGFwcGxpY2F0aW9uLgoKIyMgUENBOiBOZXVyYWwgTmV0d29ya3MKYGBge3J9CnNldC5zZWVkKDEyMzQ1KSAgICMgU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CgojIERhdGEgc2V0dXAKWCA8LSBhcy5tYXRyaXgoZGF0YVssIGMoIlBDMSIsICJQQzIiLCAiUEMzIiwgIlBDNCIpXSkKeSA8LSBkYXRhJFN1cnZpdmVkCgojIyBNb2RlbCBhcyBpbiBsZWN0dXJlCgpub3JtYWxpemVfcGNhIDwtIGxheWVyX25vcm1hbGl6YXRpb24oKSAgICMgQ3JlYXRlIGEgbm9ybWFsaXphdGlvbiBsYXllcgpub3JtYWxpemVfcGNhICU+JSBhZGFwdChYKSAgICMgQWRhcHQgdGhlIG5vcm1hbGl6YXRpb24gbGF5ZXIgdG8gdGhlIGRhdGEKCnByaW50KG5vcm1hbGl6ZV9wY2EkbWVhbikgICAjIFByaW50IHRoZSBtZWFuIHZhbHVlcyB1c2VkIGZvciBub3JtYWxpemF0aW9uCgptb2RlbF9wY2FfbmV1cmFsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAgICMgQ3JlYXRlIGEgc2VxdWVudGlhbCBtb2RlbAoKbW9kZWxfcGNhX25ldXJhbCAlPiUgCiAgbm9ybWFsaXplX3BjYSgpICU+JSAgICMgQXBwbHkgbm9ybWFsaXphdGlvbgogIAogIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMSIsIHVuaXRzID0gMTYsIGFjdGl2YXRpb24gPSAncmVsdScpICU+JSAgICMgQWRkIGEgaGlkZGVuIGxheWVyIHdpdGggMTYgdW5pdHMgYW5kIFJlTFUgYWN0aXZhdGlvbgogIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMiIsIHVuaXRzID0gMTYsIGFjdGl2YXRpb24gPSAncmVsdScpICU+JSAgICMgQWRkIGFub3RoZXIgaGlkZGVuIGxheWVyIHdpdGggMTYgdW5pdHMgYW5kIFJlTFUgYWN0aXZhdGlvbgogIGxheWVyX2RlbnNlKG5hbWUgPSAiSGlkZGVuTGF5ZXJfMyIsIHVuaXRzID0gMTYsIGFjdGl2YXRpb24gPSAncmVsdScpICU+JSAgICMgQWRkIGEgdGhpcmQgaGlkZGVuIGxheWVyIHdpdGggMTYgdW5pdHMgYW5kIFJlTFUgYWN0aXZhdGlvbgogCiAgIGxheWVyX2RlbnNlKG5hbWUgPSAiT3V0cHV0TGF5ZXIiLCB1bml0cyA9IDEpICAgIyBBZGQgYW4gb3V0cHV0IGxheWVyIHdpdGggMSB1bml0CgpzdW1tYXJ5KG1vZGVsX3BjYV9uZXVyYWwpICAgIyBQcmludCB0aGUgc3VtbWFyeSBvZiB0aGUgbW9kZWwKCiNwbG90X21vZGVsKG1vZGVsX2Jhc2UpCgptb2RlbF9wY2FfbmV1cmFsICU+JSBjb21waWxlKAogIGxvc3MgPSAiTVNFIiwKICBvcHRpbWl6ZXIgPSBvcHRpbWl6ZXJfYWRhbShsZWFybmluZ19yYXRlID0gMC4wMDEpLAogIG1ldHJpY3MgPSAiQWNjdXJhY3kiCikgICAjIENvbXBpbGUgdGhlIG1vZGVsIHdpdGggbWVhbiBzcXVhcmVkIGVycm9yIGxvc3MsIEFkYW0gb3B0aW1pemVyLCBhbmQgYWNjdXJhY3kgYXMgYSBtZXRyaWMKCm1vZGVsX3BjYV9uZXVyYWwgJT4lIGZpdCgKICB4ID0gWCwKICB5ID0geSwKICBlcG9jaHMgPSA1MDAsCiAgdmVyYm9zZSA9IDAKKSAgICMgVHJhaW4gdGhlIG1vZGVsIG9uIHRoZSBkYXRhIGZvciA1MDAgZXBvY2hzCgojbW9kZWxfcGNhX25ldXJhbCRsYXllcnNbWzJdXSRrZXJuZWwgICAjIFByaW50IHRoZSBrZXJuZWwgKHdlaWdodHMpIG9mIHRoZSBzZWNvbmQgbGF5ZXIKI21vZGVsX3BjYV9uZXVyYWwkbGF5ZXJzW1szXV0ka2VybmVsICAgIyBQcmludCB0aGUga2VybmVsICh3ZWlnaHRzKSBvZiB0aGUgdGhpcmQgbGF5ZXIKI21vZGVsX3BjYV9uZXVyYWwkbGF5ZXJzW1s0XV0ka2VybmVsICAgIyBQcmludCB0aGUga2VybmVsICh3ZWlnaHRzKSBvZiB0aGUgZm91cnRoIGxheWVyCgoKbW9kZWxfcGNhX25ldXJhbCAlPiUgZXZhbHVhdGUoWCwgeSwgdmVyYm9zZSA9IDApICAgIyBFdmFsdWF0ZSB0aGUgbW9kZWwgb24gdGhlIGRhdGEKCnJtKGxpc3QgPSBjKCJYIiwgInkiKSkKYGBgCgojIyBQQ0E6IEVuc2VtYmxlIG1ldGhvZHMKYGBge3J9CmZvcm11bGFfcGNhIDwtIFN1cnZpdmVkIH4gUEMxICsgUEMyICsgUEMzICsgUEM0CgojIEJ1aWxkaW5nIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCB1c2luZyB0aGUgcmFuZG9tRm9yZXN0IGZ1bmN0aW9uCiMgbXRyeSBwYXJhbWV0ZXIgaXMgc2V0IHRvIDksIHdoaWNoIGNvbnRyb2xzIGZlYXR1cmUgc3Vic2FtcGxpbmcKIyBudHJlZSBwYXJhbWV0ZXIgaXMgc2V0IHRvIDUwMCwgc3BlY2lmeWluZyB0aGUgbnVtYmVyIG9mIHRyZWVzIGluIHRoZSByYW5kb20gZm9yZXN0Cm1vZGVsX3BjYV9lbnNlbWJsZV9iYWdnaW5nIDwtIHJhbmRvbUZvcmVzdChmb3JtdWxhX3BjYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnk9OSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlPTUwMCkKCiMgQ3JlYXRpbmcgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgZm9yIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsCnZhckltcFBsb3QobW9kZWxfcGNhX2Vuc2VtYmxlX2JhZ2dpbmcsIG1haW49IlZhcmlhYmxlIEltcG9ydGFuY2UiKQoKIyBDcmVhdGluZyBhIGRhdGEudGFibGUgdG8gY29tcGFyZSBhY3R1YWwgc3Vydml2YWwgdmFsdWVzIHdpdGggcHJlZGljdGVkIHZhbHVlcyBmcm9tIHRoZSBiYWdnaW5nIG1vZGVsCm1vZGVsX2JhZ19yZXN1bHRfY29tcGFyaXNvbiA8LSBkYXRhLnRhYmxlKGFjdHVhbCA9IGRhdGEkU3Vydml2ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb24gPSBwcmVkaWN0KG1vZGVsX3BjYV9lbnNlbWJsZV9iYWdnaW5nLCBuZXdkYXRhID0gZGF0YSkpCgojIEJ1aWxkaW5nIGEgZ3JhZGllbnQgYm9vc3RpbmcgbW9kZWwgdXNpbmcgdGhlIGdibSBmdW5jdGlvbgojIGRpc3RyaWJ1dGlvbiBpcyBzZXQgdG8gImdhdXNzaWFuIiBmb3IgcmVncmVzc2lvbiwgYnV0IGNvdWxkIGJlIG1vZGlmaWVkIGZvciBjbGFzc2lmaWNhdGlvbgojIGludGVyYWN0aW9uLmRlcHRoIHNldHMgdGhlIG1heGltdW0gZGVwdGggb2YgdGhlIGluZGl2aWR1YWwgdHJlZXMKIyBuLnRyZWVzIGlzIHNldCB0byA1MDAsIGluZGljYXRpbmcgdGhlIG51bWJlciBvZiBib29zdGluZyBpdGVyYXRpb25zCiMgc2hyaW5rYWdlIGNvbnRyb2xzIHRoZSBzdGVwIHNpemUgd2hlbiB1cGRhdGluZyB0aGUgd2VpZ2h0cyBvZiBlYWNoIHRyZWUKIyBjdi5mb2xkcyBpcyBzZXQgdG8gNSwgc3BlY2lmeWluZyB0aGUgbnVtYmVyIG9mIGNyb3NzLXZhbGlkYXRpb24gZm9sZHMgZm9yIG1vZGVsIGV2YWx1YXRpb24KbW9kZWxfcGNhX2Vuc2VtYmxlX2Jvb3N0aW5nIDwtIGdibShmb3JtdWxhID0gZm9ybXVsYV9wY2EsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbj0iZ2F1c3NpYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJhY3Rpb24uZGVwdGg9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXM9NTAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNocmlua2FnZT0wLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdi5mb2xkcyA9IDUpCgojIENhbGN1bGF0aW5nIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgYm9vc3RpbmcgbW9kZWwgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbgpwZXJmb3JtYW5jZV9lbnNlbWJsZSA8LSBnYm0ucGVyZihtb2RlbF9wY2FfZW5zZW1ibGVfYm9vc3RpbmcsIG1ldGhvZCA9ICJjdiIpCgojIEdlbmVyYXRpbmcgYSBzdW1tYXJ5IG9mIHRoZSBib29zdGluZyBtb2RlbCwgYnV0IHBsb3RpdCBwYXJhbWV0ZXIgaXMgc2V0IHRvIEZBTFNFLCBzbyBubyBwbG90IHdpbGwgYmUgZ2VuZXJhdGVkCnN1bW1hcnkobW9kZWxfcGNhX2Vuc2VtYmxlX2Jvb3N0aW5nLCBwbG90aXQgPSBGQUxTRSkKCiMgTWVyZ2luZyB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBkYXRhIGZyb20gdGhlIGJhZ2dpbmcgbW9kZWwgd2l0aCB0aGUgc3VtbWFyeSBkYXRhIGZyb20gdGhlIGJvb3N0aW5nIG1vZGVsCiMgUHJlcGFyaW5nIHRoZSBkYXRhIGZvciBwbG90dGluZyBieSB0cmFuc2Zvcm1pbmcgcGVyY2VudGFnZXMgYW5kIGNvbnZlcnRpbmcgdG8gbG9uZyBmb3JtYXQgdXNpbmcgbWVsdCBmdW5jdGlvbgpkdF92YXJpbXAgPC0gbWVyZ2UoZGF0YS50YWJsZShpbXBvcnRhbmNlKG1vZGVsX3BjYV9lbnNlbWJsZV9iYWdnaW5nLCB0eXBlID0gMiksIGtlZXAucm93bmFtZXMgPSBUUlVFKSwgCiAgICAgICAgICAgICAgICAgICBkYXRhLnRhYmxlKHN1bW1hcnkobW9kZWxfcGNhX2Vuc2VtYmxlX2Jvb3N0aW5nLCBwbG90aXQgPSBGQUxTRSkpLCAKICAgICAgICAgICAgICAgICAgIGJ5LnggPSAicm4iLCBieS55ID0gInZhciIpCmR0X3ZhcmltcCA8LSBzZXROYW1lcyhkdF92YXJpbXAsIGMoIlZhcmlhYmxlIiwgIkJhZ2dpbmciLCAiQm9vc3RpbmciKSkKZHRfdmFyaW1wWywgQmFnZ2luZyA6PSAxMDAqQmFnZ2luZy9zdW0oQmFnZ2luZyldCmR0X3ZhcmltcCA8LSBtZWx0KGR0X3ZhcmltcCwgaWQudmFycyA9ICJWYXJpYWJsZSIsIAogICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSBjKCJCYWdnaW5nIiwiQm9vc3RpbmciKSwgCiAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAieSIsIAogICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIngiKQpkdF92YXJpbXAgPC0gZHRfdmFyaW1wW29yZGVyKHksLXgpXQoKIyBQbG90dGluZyB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBjb21wYXJpc29uIHVzaW5nIGdncGxvdDIKZ2dwbG90KGRhdGEgPSBkdF92YXJpbXApICsKICBnZW9tX2NvbChhZXMoeT1WYXJpYWJsZSwgeD14LCBmaWxsPXkpKSArCiAgbGFicygKICAgIHggPSAiUmVsYXRpdmUgSW1wb3J0YW5jZSIsCiAgICB5ID0gIlZhcmlhYmxlcyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjZjFiMTQ3IiwiIzRDQTdERSIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpLAogICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBmYWNldF93cmFwKH55KQoKcm0obGlzdCA9IGMoImR0X3ZhcmltcCIsICJtb2RlbF9iYWdfcmVzdWx0X2NvbXBhcmlzb24iLCAicGVyZm9ybWFuY2VfZW5zZW1ibGUiKSkKYGBgCgojIE1vZGVscyB3aXRoIGZ1bGwgUENBCgojIyBGdWxsIFBDQTogTG9naXN0aWMgcmVncmVzc2lvbgpgYGB7cn0KIyBEZWZpbmUgYSBsb2dpc3RpYyBtb2RlbCB1c2luZyBhbGwgUENBcwptb2RlbF9wY2FfZnVsbF9nbG0gPC0gZ2xtKGZvcm11bGEgPSBTdXJ2aXZlZCB+IFBDMSArIFBDMiArIFBDMyArIFBDNCArIFBDNSArIFBDNiArIFBDNyArIFBDOCwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkKCiMgUHJpbnQgc3VtbWFyeSBzdGF0aXN0aWMKcHJpbnQoc3VtbWFyeShtb2RlbF9wY2FfZnVsbF9nbG0pKQoKIyBGaXQgb2YgdGhlIG1vZGVsCndpdGgobW9kZWxfcGNhX2Z1bGxfZ2xtLCBwY2hpc3EobnVsbC5kZXZpYW5jZSAtIGRldmlhbmNlLCBkZi5udWxsIC0gZGYucmVzaWR1YWwsIGxvd2VyLnRhaWwgPSBGQUxTRSkpCiMgQSBwLXZhbHVlIGJlbG93IDAuMDAxIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCBwZXJmb3JtcyBiZXR0ZXIgdGhhbiBhbiBlbXB0eSBtb2RlbAoKYGBgCkFzIGFsbCBpbnB1dHMgc2VlbSB0byBiZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IHRoZSAwLjElIGxldmVsLgoKIyMgRnVsbCBQQ0E6IFRyZWUKYGBge3J9CiMjIyBCYXNlIHRyZWUgCiMgQnVpbGQgYSBkZWNpc2lvbiB0cmVlIG1vZGVsIHVzaW5nIHJwYXJ0Cm1vZGVsX3RyZWUgPC0gcnBhcnQoZGF0YSA9IGRhdGEsIGZvcm11bGEgPSBTdXJ2aXZlZCB+IFBDMSArIFBDMiArIFBDMyArIFBDNCArIFBDNSArIFBDNiArIFBDNyArIFBDOCwgbWV0aG9kID0gImNsYXNzIikKCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyBwcnAsIHdpdGggZXh0cmEgZGV0YWlscyAoZXh0cmEgPSAyKQpwcnAobW9kZWxfdHJlZSwgZXh0cmEgPSAyLCBtYWluID0gIkJhc2UgdHJlZSBmb3IgbW9kZWwiKQoKIyBQcmludCBhbiBwbG90IGRhdGEgb24gdHJlZQpwcmludGNwKG1vZGVsX3RyZWUpCnBsb3RjcChtb2RlbF90cmVlKQojIFRoZSB0cmVlIHNob3dzIHRoYXQgdGhlIGZpcnN0IGRlY2lkaW5nIGZhY3RvciBzZWVtcyB0byBiZSBnZW5kZXIuIEFmdGVyd2FyZHMgbWFpbmx5IGNsYXNzIGFuZCBhZ2Ugc2hvdyBzZWVtIHByb21pbmVudCBkZWNpZGluZyBmYWN0b3JzIGZvciBicmFuY2hlcyB0aG91Z2ggaW50ZXJlc3RpbmdseSBlbm91Z2ggZmFyZSBzZWVtcyB0byBwbGF5IGFuIGltcG9ydGFudCByb2xlIGFzIHdlbGwuCgojIERldGVybWluZSB0aGUgbWluaW11bSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIGZvciBwcnVuaW5nCiNjcCBwYXJhbWV0ZXI6IGNhcHR1cmVzIG1vZGVsIGNvbXBsZXhpdHkKbWluX2NwIDwtIG1vZGVsX3RyZWUkY3B0YWJsZVt3aGljaC5taW4obW9kZWxfdHJlZSRjcHRhYmxlWywgInhlcnJvciJdKSwgIkNQIl0KCiMgUHJ1bmUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgdXNpbmcgdGhlIG1pbmltdW0gY3AKbW9kZWxfdHJlZV8yIDwtIHBydW5lKG1vZGVsX3RyZWUsIGNwID0gbWluX2NwKQoKIyBQbG90IHRoZSBwcnVuZWQgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyBwcnAsIHdpdGggZXh0cmEgZGV0YWlscyAoZXh0cmEgPSAyKQpwcnAobW9kZWxfdHJlZV8yLCBleHRyYSA9IDIsIG1haW4gPSAiUHJ1bmVkIGJhc2UgdHJlZSB3aXRoIGxlc3MgY29tcGxleGl0eSIpCgojIFRoZSBicmFuY2ggZGV0ZXJtaW5lZCBieSBiZWluZyBtYWxlIGFuZCBvdmVyIHRoZSBhZ2Ugb2YgNiBzaG93cyB0aGUgYmlnZ2VzdCBncm91cCBhbmQgdGhlIG1vc3Qgd3JvbmcgZ3Vlc3NlcyBvZiB0aGUgbW9kZWwuCgojIENhbGN1bGF0ZSB0aGUgbWVhbiBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgcHJlZGljdGlvbnMKbWVhbihwcmVkaWN0KG1vZGVsX3RyZWUsIGRhdGEsIHR5cGUgPSAiY2xhc3MiKT09ZGF0YSRTdXJ2aXZlZCkKCm1lYW4ocHJlZGljdChtb2RlbF90cmVlXzIsIGRhdGEsIHR5cGUgPSAiY2xhc3MiKT09ZGF0YSRTdXJ2aXZlZCkKCiMgQmV0d2VlbiB0aGUgYWNjdXJhY3kgZm9yIHRoZSBwcnVuZWQgYW5kIG5vdC1wcnVuZWQgbW9kZWwgdGhlcmUgaXMgb25seSBhIDIlIGRpZmZlcmVuY2Ugc28gd2Ugd2lsbCBjaG9vc2UgdGhlIHBydW5lZCBtb2RlbCBhcyByZXByZXNlbnRhdGl2ZSBhcyBpdCBpcyBsZXNzIGNvbXBsZXgKbW9kZWxfcGNhX2Z1bGxfdHJlZXMgPC0gbW9kZWxfdHJlZV8yCgojIFByaW50IGFuIHBsb3QgZGF0YSBvbiB0cmVlCnByaW50Y3AobW9kZWxfdHJlZV8yKQpwbG90Y3AobW9kZWxfdHJlZV8yKQoKcm0obGlzdCA9IGMoIm1vZGVsX3RyZWUiLCAibW9kZWxfdHJlZV8yIikpCgpgYGAKCkluIHRoaXMgY2FzZSB0aGUgcHJ1bmVkIGFuZCB1bi1wcnVuZWQgbW9kZWwgYXJlIGlkZW50aWNhbCB3aGljaCBtYWtlcyBzZW5zZSBnaXZlbiB0aGUgbmF0dXJlIG9mIFBDQS4KCiMjIEZ1bGwgUENBOiBOZXVyYWwgTmV0d29ya3MKYGBge3J9CnNldC5zZWVkKDEyMzQ1KSAgICMgU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CgojIERhdGEgc2V0dXAKWCA8LSBhcy5tYXRyaXgoZGF0YVssIGMoIlBDMSIsICJQQzIiLCAiUEMzIiwgIlBDNCIsICJQQzUiLCAiUEM2IiwgIlBDNyIsICJQQzgiKV0pCnkgPC0gZGF0YSRTdXJ2aXZlZAoKIyMgTW9kZWwgYXMgaW4gbGVjdHVyZQoKbm9ybWFsaXplX3BjYV9mdWxsIDwtIGxheWVyX25vcm1hbGl6YXRpb24oKSAgICMgQ3JlYXRlIGEgbm9ybWFsaXphdGlvbiBsYXllcgpub3JtYWxpemVfcGNhX2Z1bGwgJT4lIGFkYXB0KFgpICAgIyBBZGFwdCB0aGUgbm9ybWFsaXphdGlvbiBsYXllciB0byB0aGUgZGF0YQoKcHJpbnQobm9ybWFsaXplX3BjYV9mdWxsJG1lYW4pICAgIyBQcmludCB0aGUgbWVhbiB2YWx1ZXMgdXNlZCBmb3Igbm9ybWFsaXphdGlvbgoKbW9kZWxfcGNhX2Z1bGxfbmV1cmFsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAgICMgQ3JlYXRlIGEgc2VxdWVudGlhbCBtb2RlbAoKbW9kZWxfcGNhX2Z1bGxfbmV1cmFsICU+JSAKICBub3JtYWxpemVfcGNhX2Z1bGwoKSAlPiUgICAjIEFwcGx5IG5vcm1hbGl6YXRpb24KICAKICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzEiLCB1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUgICAjIEFkZCBhIGhpZGRlbiBsYXllciB3aXRoIDE2IHVuaXRzIGFuZCBSZUxVIGFjdGl2YXRpb24KICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzIiLCB1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUgICAjIEFkZCBhbm90aGVyIGhpZGRlbiBsYXllciB3aXRoIDE2IHVuaXRzIGFuZCBSZUxVIGFjdGl2YXRpb24KICBsYXllcl9kZW5zZShuYW1lID0gIkhpZGRlbkxheWVyXzMiLCB1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gJ3JlbHUnKSAlPiUgICAjIEFkZCBhIHRoaXJkIGhpZGRlbiBsYXllciB3aXRoIDE2IHVuaXRzIGFuZCBSZUxVIGFjdGl2YXRpb24KIAogICBsYXllcl9kZW5zZShuYW1lID0gIk91dHB1dExheWVyIiwgdW5pdHMgPSAxKSAgICMgQWRkIGFuIG91dHB1dCBsYXllciB3aXRoIDEgdW5pdAoKc3VtbWFyeShtb2RlbF9wY2FfZnVsbF9uZXVyYWwpICAgIyBQcmludCB0aGUgc3VtbWFyeSBvZiB0aGUgbW9kZWwKCiNwbG90X21vZGVsKG1vZGVsX2Jhc2UpCgptb2RlbF9wY2FfZnVsbF9uZXVyYWwgJT4lIGNvbXBpbGUoCiAgbG9zcyA9ICJNU0UiLAogIG9wdGltaXplciA9IG9wdGltaXplcl9hZGFtKGxlYXJuaW5nX3JhdGUgPSAwLjAwMSksCiAgbWV0cmljcyA9ICJBY2N1cmFjeSIKKSAgICMgQ29tcGlsZSB0aGUgbW9kZWwgd2l0aCBtZWFuIHNxdWFyZWQgZXJyb3IgbG9zcywgQWRhbSBvcHRpbWl6ZXIsIGFuZCBhY2N1cmFjeSBhcyBhIG1ldHJpYwoKbW9kZWxfcGNhX2Z1bGxfbmV1cmFsICU+JSBmaXQoCiAgeCA9IFgsCiAgeSA9IHksCiAgZXBvY2hzID0gNTAwLAogIHZlcmJvc2UgPSAwCikgICAjIFRyYWluIHRoZSBtb2RlbCBvbiB0aGUgZGF0YSBmb3IgNTAwIGVwb2NocwoKI21vZGVsX3BjYV9mdWxsX25ldXJhbCRsYXllcnNbWzJdXSRrZXJuZWwgICAjIFByaW50IHRoZSBrZXJuZWwgKHdlaWdodHMpIG9mIHRoZSBzZWNvbmQgbGF5ZXIKI21vZGVsX3BjYV9mdWxsX25ldXJhbCRsYXllcnNbWzNdXSRrZXJuZWwgICAjIFByaW50IHRoZSBrZXJuZWwgKHdlaWdodHMpIG9mIHRoZSB0aGlyZCBsYXllcgojbW9kZWxfcGNhX2Z1bGxfbmV1cmFsJGxheWVyc1tbNF1dJGtlcm5lbCAgICMgUHJpbnQgdGhlIGtlcm5lbCAod2VpZ2h0cykgb2YgdGhlIGZvdXJ0aCBsYXllcgoKCm1vZGVsX3BjYV9mdWxsX25ldXJhbCAlPiUgZXZhbHVhdGUoWCwgeSwgdmVyYm9zZSA9IDApICAgIyBFdmFsdWF0ZSB0aGUgbW9kZWwgb24gdGhlIGRhdGEKCnJtKGxpc3QgPSBjKCJYIiwgInkiKSkKYGBgCgojIyBGdWxsIFBDQTogRW5zZW1ibGUgbWV0aG9kcwpgYGB7cn0KZm9ybXVsYV9wY2FfZnVsbCA8LSBTdXJ2aXZlZCB+IFBDMSArIFBDMiArIFBDMyArIFBDNCArIFBDNSArIFBDNiArIFBDNyArIFBDOAoKIyBCdWlsZGluZyBhIHJhbmRvbSBmb3Jlc3QgbW9kZWwgdXNpbmcgdGhlIHJhbmRvbUZvcmVzdCBmdW5jdGlvbgojIG10cnkgcGFyYW1ldGVyIGlzIHNldCB0byA5LCB3aGljaCBjb250cm9scyBmZWF0dXJlIHN1YnNhbXBsaW5nCiMgbnRyZWUgcGFyYW1ldGVyIGlzIHNldCB0byA1MDAsIHNwZWNpZnlpbmcgdGhlIG51bWJlciBvZiB0cmVlcyBpbiB0aGUgcmFuZG9tIGZvcmVzdAptb2RlbF9wY2FfZnVsbF9lbnNlbWJsZV9iYWdnaW5nIDwtIHJhbmRvbUZvcmVzdChmb3JtdWxhX3BjYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnk9OSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlPTUwMCkKCiMgQ3JlYXRpbmcgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgZm9yIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsCnZhckltcFBsb3QobW9kZWxfcGNhX2Z1bGxfZW5zZW1ibGVfYmFnZ2luZywgbWFpbj0iVmFyaWFibGUgSW1wb3J0YW5jZSIpCgojIENyZWF0aW5nIGEgZGF0YS50YWJsZSB0byBjb21wYXJlIGFjdHVhbCBzdXJ2aXZhbCB2YWx1ZXMgd2l0aCBwcmVkaWN0ZWQgdmFsdWVzIGZyb20gdGhlIGJhZ2dpbmcgbW9kZWwKbW9kZWxfYmFnX3Jlc3VsdF9jb21wYXJpc29uIDwtIGRhdGEudGFibGUoYWN0dWFsID0gZGF0YSRTdXJ2aXZlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGlvbiA9IHByZWRpY3QobW9kZWxfcGNhX2Z1bGxfZW5zZW1ibGVfYmFnZ2luZywgbmV3ZGF0YSA9IGRhdGEpKSAKCiMgQnVpbGRpbmcgYSBncmFkaWVudCBib29zdGluZyBtb2RlbCB1c2luZyB0aGUgZ2JtIGZ1bmN0aW9uCiMgZGlzdHJpYnV0aW9uIGlzIHNldCB0byAiZ2F1c3NpYW4iIGZvciByZWdyZXNzaW9uLCBidXQgY291bGQgYmUgbW9kaWZpZWQgZm9yIGNsYXNzaWZpY2F0aW9uCiMgaW50ZXJhY3Rpb24uZGVwdGggc2V0cyB0aGUgbWF4aW11bSBkZXB0aCBvZiB0aGUgaW5kaXZpZHVhbCB0cmVlcwojIG4udHJlZXMgaXMgc2V0IHRvIDUwMCwgaW5kaWNhdGluZyB0aGUgbnVtYmVyIG9mIGJvb3N0aW5nIGl0ZXJhdGlvbnMKIyBzaHJpbmthZ2UgY29udHJvbHMgdGhlIHN0ZXAgc2l6ZSB3aGVuIHVwZGF0aW5nIHRoZSB3ZWlnaHRzIG9mIGVhY2ggdHJlZQojIGN2LmZvbGRzIGlzIHNldCB0byA1LCBzcGVjaWZ5aW5nIHRoZSBudW1iZXIgb2YgY3Jvc3MtdmFsaWRhdGlvbiBmb2xkcyBmb3IgbW9kZWwgZXZhbHVhdGlvbgptb2RlbF9wY2FfZnVsbF9lbnNlbWJsZV9ib29zdGluZyA8LSBnYm0oZm9ybXVsYSA9IGZvcm11bGFfcGNhX2Z1bGwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbj0iZ2F1c3NpYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJhY3Rpb24uZGVwdGg9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXM9NTAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNocmlua2FnZT0wLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdi5mb2xkcyA9IDUpCgojIENhbGN1bGF0aW5nIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgYm9vc3RpbmcgbW9kZWwgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbgpwZXJmb3JtYW5jZV9lbnNlbWJsZSA8LSBnYm0ucGVyZihtb2RlbF9wY2FfZnVsbF9lbnNlbWJsZV9ib29zdGluZywgbWV0aG9kID0gImN2IikKCiMgR2VuZXJhdGluZyBhIHN1bW1hcnkgb2YgdGhlIGJvb3N0aW5nIG1vZGVsLCBidXQgcGxvdGl0IHBhcmFtZXRlciBpcyBzZXQgdG8gRkFMU0UsIHNvIG5vIHBsb3Qgd2lsbCBiZSBnZW5lcmF0ZWQKc3VtbWFyeShtb2RlbF9wY2FfZnVsbF9lbnNlbWJsZV9ib29zdGluZywgcGxvdGl0ID0gRkFMU0UpCgojIE1lcmdpbmcgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgZGF0YSBmcm9tIHRoZSBiYWdnaW5nIG1vZGVsIHdpdGggdGhlIHN1bW1hcnkgZGF0YSBmcm9tIHRoZSBib29zdGluZyBtb2RlbAojIFByZXBhcmluZyB0aGUgZGF0YSBmb3IgcGxvdHRpbmcgYnkgdHJhbnNmb3JtaW5nIHBlcmNlbnRhZ2VzIGFuZCBjb252ZXJ0aW5nIHRvIGxvbmcgZm9ybWF0IHVzaW5nIG1lbHQgZnVuY3Rpb24KZHRfdmFyaW1wIDwtIG1lcmdlKGRhdGEudGFibGUoaW1wb3J0YW5jZShtb2RlbF9wY2FfZnVsbF9lbnNlbWJsZV9iYWdnaW5nLCB0eXBlID0gMiksIGtlZXAucm93bmFtZXMgPSBUUlVFKSwgCiAgICAgICAgICAgICAgICAgICBkYXRhLnRhYmxlKHN1bW1hcnkobW9kZWxfcGNhX2Z1bGxfZW5zZW1ibGVfYm9vc3RpbmcsIHBsb3RpdCA9IEZBTFNFKSksIAogICAgICAgICAgICAgICAgICAgYnkueCA9ICJybiIsIGJ5LnkgPSAidmFyIikKZHRfdmFyaW1wIDwtIHNldE5hbWVzKGR0X3ZhcmltcCwgYygiVmFyaWFibGUiLCAiQmFnZ2luZyIsICJCb29zdGluZyIpKQpkdF92YXJpbXBbLCBCYWdnaW5nIDo9IDEwMCpCYWdnaW5nL3N1bShCYWdnaW5nKV0KZHRfdmFyaW1wIDwtIG1lbHQoZHRfdmFyaW1wLCBpZC52YXJzID0gIlZhcmlhYmxlIiwgCiAgICAgICAgICAgICAgICAgIG1lYXN1cmUudmFycyA9IGMoIkJhZ2dpbmciLCJCb29zdGluZyIpLCAKICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJ5IiwgCiAgICAgICAgICAgICAgICAgIHZhbHVlLm5hbWUgPSAieCIpCmR0X3ZhcmltcCA8LSBkdF92YXJpbXBbb3JkZXIoeSwteCldCgojIFBsb3R0aW5nIHRoZSB2YXJpYWJsZSBpbXBvcnRhbmNlIGNvbXBhcmlzb24gdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGR0X3ZhcmltcCkgKwogIGdlb21fY29sKGFlcyh5PVZhcmlhYmxlLCB4PXgsIGZpbGw9eSkpICsKICBsYWJzKAogICAgeCA9ICJSZWxhdGl2ZSBJbXBvcnRhbmNlIiwKICAgIHkgPSAiVmFyaWFibGVzIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNmMWIxNDciLCIjNENBN0RFIikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIiksCiAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksIAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGZhY2V0X3dyYXAofnkpCgpybShsaXN0ID0gYygiZHRfdmFyaW1wIiwgIm1vZGVsX2JhZ19yZXN1bHRfY29tcGFyaXNvbiIsICJwZXJmb3JtYW5jZV9lbnNlbWJsZSIpKQpgYGAKCk5vdGFibHkgYWdhaW4gaW4gdGhpcyBjYXNlIG91ciBiYWdnaW5nIG1vZGVsIHBlcmZvcm1zIHRoZSBzYW1lIGFzIHRoZSBiYWdnaW5nIG1vZGVsIHVzaW5nIG9ubHkgdGhlIGZpcnN0IGZvdXIgUENzLiBUaGlzIG9jY3VycyBhcyB0aGUgcmVsYXRpdmVseSB1bmltcG9ydGFudCBsYXN0IGZvdXIgUENzIHdpbGwgbmV2ZXIgYmUgY2hvc2VuIGJ5IHRoZSB0cmVlcyBpbiB0aGlzIGNhc2UuCgojIERhdGEgZm9yIFZhbGlkYXRpb24KYGBge3J9CnRlc3RfZGF0YSA8LSByZWFkX2NzdihwYXRoMSkgICMgUmVhZCB0aGUgQ1NWIGZpbGUgbG9jYXRlZCBhdCB0aGUgc3BlY2lmaWVkIHBhdGggYW5kIHN0b3JlIHRoZSB0ZXN0X2RhdGEgaW4gdGhlICd0ZXN0X2RhdGEnIHZhcmlhYmxlCgp0ZXN0X3NvbHV0aW9uIDwtIHJlYWRfY3N2KHBhdGgyKQoKdGVzdF9kYXRhJFN1cnZpdmVkIDwtIHRlc3Rfc29sdXRpb24kU3Vydml2ZWQKCnRlc3RfZGF0YSA8LSBjbGVhbl9kYXRhKHRlc3RfZGF0YSkKCnRlc3RfZGF0YSA8LSBhcHBseV9wY2EodGVzdF9kYXRhLCBtb2RlbF9wY2EpCgp0ZXN0X3NvbHV0aW9uIDwtIGFzLmRhdGEuZnJhbWUodGVzdF9kYXRhJFN1cnZpdmVkKQpjb2xuYW1lcyh0ZXN0X3NvbHV0aW9uKSA8LSAiU3Vydml2ZWQiCgpybShsaXN0ID0gYygicGF0aDEiLCAicGF0aDIiKSkKCmBgYAoKIyMgUHJlZGljdGlvbnMgZm9yIHZhbGlkYXRpb24KTGV0IHVzIHBlcmZvcm0gcHJlZGljdGlvbnMgZm9yIGRpZmZlcmVudCBtb2RlbHMgb24gdGhlIHZhbGlkYXRpb24gZGF0YS4KYGBge3J9CiMgUHJlZGljdGlvbiBmb3IgbG9naXN0aWMgcmVncmVzc2lvbgp0ZXN0X3NvbHV0aW9uJGxvZ2lzdGljX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9nbG0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhKQojIENvbnZlcnRpbmcgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHRvIGJpbmFyeSBwcmVkaWN0aW9ucyAoMCBvciAxKSB1c2luZyBhIHRocmVzaG9sZCBvZiAwLjUKdGVzdF9zb2x1dGlvbiRsb2dpc3RpY19wcmVkaWN0aW9uIDwtIGlmZWxzZSh0ZXN0X3NvbHV0aW9uJGxvZ2lzdGljX3ByZWRpY3Rpb24gPiAwLCAxLCAwKQoKIyBQcmVkaWN0aW9uIGZvciB0cmVlIHJlZ3Jlc3Npb24KdGVzdF9zb2x1dGlvbiR0cmVlX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF90cmVlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHRlc3RfZGF0YSlbLCIxIl0KIyBDb252ZXJ0aW5nIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyB0byBiaW5hcnkgcHJlZGljdGlvbnMgKDAgb3IgMSkgdXNpbmcgYSB0aHJlc2hvbGQgb2YgMC41CnRlc3Rfc29sdXRpb24kdHJlZV9wcmVkaWN0aW9uIDwtIGlmZWxzZSh0ZXN0X3NvbHV0aW9uJHRyZWVfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKCiMgUHJlZGljdGlvbiBmb3IgbmV1cmFsIG5ldHdvcmsKdGVzdF9zb2x1dGlvbiRuZXVyYWxfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX25ldXJhbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gYXMubWF0cml4KHRlc3RfZGF0YVssIGMoIkZpcnN0Q2xhc3MiLCAiU2Vjb25kQ2xhc3MiLCAiVGhpcmRDbGFzcyIsICJTZXgiLCAiQWdlIiwgIlNpYnNwIiwgIlBhcmNoIiwgIkZhcmUiLCAiU291dGhoYW1wdG9uIiwgIkNoZXJib3VyZyIsICJRdWVlbnN0b3duIildKSwgdmVyYm9zZSA9IDApCgojIENvbnZlcnRpbmcgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHRvIGJpbmFyeSBwcmVkaWN0aW9ucyAoMCBvciAxKSB1c2luZyBhIHRocmVzaG9sZCBvZiAwLjUKdGVzdF9zb2x1dGlvbiRuZXVyYWxfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRuZXVyYWxfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKCiMgUHJlZGljdGlvbiBmb3IgYmFnZ2luZwp0ZXN0X3NvbHV0aW9uJGJhZ2dpbmdfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX2Vuc2VtYmxlX2JhZ2dpbmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGEpCiMgQ29udmVydGluZyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgdG8gYmluYXJ5IHByZWRpY3Rpb25zICgwIG9yIDEpIHVzaW5nIGEgdGhyZXNob2xkIG9mIDAuNQp0ZXN0X3NvbHV0aW9uJGJhZ2dpbmdfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRiYWdnaW5nX3ByZWRpY3Rpb24gPiAwLjUsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIGJvb3N0aW5nCnRlc3Rfc29sdXRpb24kYm9vc3RpbmdfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX2Vuc2VtYmxlX2Jvb3N0aW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKIyBDb252ZXJ0aW5nIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyB0byBiaW5hcnkgcHJlZGljdGlvbnMgKDAgb3IgMSkgdXNpbmcgYSB0aHJlc2hvbGQgb2YgMC41CnRlc3Rfc29sdXRpb24kYm9vc3RpbmdfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRib29zdGluZ19wcmVkaWN0aW9uID4gMC41LCAxLCAwKQoKIyNQQ0EKCiMgUHJlZGljdGlvbiBmb3IgbG9naXN0aWMgcmVncmVzc2lvbgp0ZXN0X3NvbHV0aW9uJGxvZ2lzdGljX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX2dsbSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGEpCnRlc3Rfc29sdXRpb24kbG9naXN0aWNfcGNhX3ByZWRpY3Rpb24gPC0gaWZlbHNlKHRlc3Rfc29sdXRpb24kbG9naXN0aWNfcGNhX3ByZWRpY3Rpb24gPiAwLCAxLCAwKQoKIyBQcmVkaWN0aW9uIGZvciB0cmVlIHJlZ3Jlc3Npb24KdGVzdF9zb2x1dGlvbiR0cmVlX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX3RyZWVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhKVssIjEiXQoKdGVzdF9zb2x1dGlvbiR0cmVlX3BjYV9wcmVkaWN0aW9uIDwtIGlmZWxzZSh0ZXN0X3NvbHV0aW9uJHRyZWVfcGNhX3ByZWRpY3Rpb24gPiAwLjUsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIG5ldXJhbCBuZXR3b3JrCnRlc3Rfc29sdXRpb24kbmV1cmFsX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX25ldXJhbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gYXMubWF0cml4KHRlc3RfZGF0YVssIGMoIlBDMSIsICJQQzIiLCAiUEMzIiwgIlBDNCIpXSksIHZlcmJvc2UgPSAwKQp0ZXN0X3NvbHV0aW9uJG5ldXJhbF9wY2FfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRuZXVyYWxfcGNhX3ByZWRpY3Rpb24gPiAwLjUsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIGJhZ2dpbmcKdGVzdF9zb2x1dGlvbiRiYWdnaW5nX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX2Vuc2VtYmxlX2JhZ2dpbmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGEpCnRlc3Rfc29sdXRpb24kYmFnZ2luZ19wY2FfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRiYWdnaW5nX3BjYV9wcmVkaWN0aW9uID4gMC41LCAxLCAwKQoKIyBQcmVkaWN0aW9uIGZvciBib29zdGluZwp0ZXN0X3NvbHV0aW9uJGJvb3N0aW5nX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX2Vuc2VtYmxlX2Jvb3N0aW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKdGVzdF9zb2x1dGlvbiRib29zdGluZ19wY2FfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRib29zdGluZ19wY2FfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKCiMjRnVsbCBQQ0EKCiMgUHJlZGljdGlvbiBmb3IgbG9naXN0aWMgcmVncmVzc2lvbgp0ZXN0X3NvbHV0aW9uJGxvZ2lzdGljX3BjYV9mdWxsX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9wY2FfZnVsbF9nbG0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhKQp0ZXN0X3NvbHV0aW9uJGxvZ2lzdGljX3BjYV9mdWxsX3ByZWRpY3Rpb24gPC0gaWZlbHNlKHRlc3Rfc29sdXRpb24kbG9naXN0aWNfcGNhX2Z1bGxfcHJlZGljdGlvbiA+IDAsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIHRyZWUgcmVncmVzc2lvbgp0ZXN0X3NvbHV0aW9uJHRyZWVfcGNhX2Z1bGxfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX3BjYV9mdWxsX3RyZWVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhKVssIjEiXQp0ZXN0X3NvbHV0aW9uJHRyZWVfcGNhX2Z1bGxfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiR0cmVlX3BjYV9mdWxsX3ByZWRpY3Rpb24gPiAwLjUsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIG5ldXJhbCBuZXR3b3JrCnRlc3Rfc29sdXRpb24kbmV1cmFsX3BjYV9mdWxsX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9wY2FfZnVsbF9uZXVyYWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGFzLm1hdHJpeCh0ZXN0X2RhdGFbLCBjKCJQQzEiLCAiUEMyIiwgIlBDMyIsICJQQzQiLCAiUEM1IiwgIlBDNiIsICJQQzciLCAiUEM4IildKSwgdmVyYm9zZSA9IDApCnRlc3Rfc29sdXRpb24kbmV1cmFsX3BjYV9mdWxsX3ByZWRpY3Rpb24gPC0gaWZlbHNlKHRlc3Rfc29sdXRpb24kbmV1cmFsX3BjYV9mdWxsX3ByZWRpY3Rpb24gPiAwLjUsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIGJhZ2dpbmcKdGVzdF9zb2x1dGlvbiRiYWdnaW5nX3BjYV9mdWxsX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9wY2FfZnVsbF9lbnNlbWJsZV9iYWdnaW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhKQp0ZXN0X3NvbHV0aW9uJGJhZ2dpbmdfcGNhX2Z1bGxfcHJlZGljdGlvbiA8LSBpZmVsc2UodGVzdF9zb2x1dGlvbiRiYWdnaW5nX3BjYV9mdWxsX3ByZWRpY3Rpb24gPiAwLjUsIDEsIDApCgojIFByZWRpY3Rpb24gZm9yIGJvb3N0aW5nCnRlc3Rfc29sdXRpb24kYm9vc3RpbmdfcGNhX2Z1bGxfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX3BjYV9mdWxsX2Vuc2VtYmxlX2Jvb3N0aW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKCiMgQ29udmVydGluZyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgdG8gYmluYXJ5IHByZWRpY3Rpb25zICgwIG9yIDEpIHVzaW5nIGEgdGhyZXNob2xkIG9mIDAuNQp0ZXN0X3NvbHV0aW9uJGJvb3N0aW5nX3BjYV9mdWxsX3ByZWRpY3Rpb24gPC0gaWZlbHNlKHRlc3Rfc29sdXRpb24kYm9vc3RpbmdfcGNhX2Z1bGxfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKCgoKYGBgCgojIFZhbGlkYXRpb24KYGBge3J9CiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIGNvcnJlY3QgZ3Vlc3NlcyBmb3IgYSBjb2x1bW4KIyBUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIG92ZXJhbGwgYWNjdXJhY3kgb2YgcHJlZGljdGlvbnMgZm9yIGEgZ2l2ZW4gY29sdW1uIGJ5IGNvbXBhcmluZyBpdCB3aXRoIHRoZSB0cnVlICdTdXJ2aXZlZCcgdmFsdWVzLgpjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzIDwtIGZ1bmN0aW9uKGNvbHVtbiwgc3Vydml2ZWQpIHsKICBzdW0oY29sdW1uID09IHN1cnZpdmVkKSAvIGxlbmd0aChjb2x1bW4pCn0KCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIGNvcnJlY3QgZ3Vlc3NlcyBmb3Igc3Vydml2ZWQgY2FzZXMgaW4gYSBjb2x1bW4KIyBUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIGFjY3VyYWN5IG9mIHByZWRpY3Rpb25zIGZvciB0aGUgJ1N1cnZpdmVkJyBjYXNlcyAod2hlcmUgJ1N1cnZpdmVkJyA9IDEpIGluIGEgZ2l2ZW4gY29sdW1uLgpjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzX3N1cnZpdmVkIDwtIGZ1bmN0aW9uKGNvbHVtbiwgc3Vydml2ZWQpIHsKICBzdW0oY29sdW1uID09IDEgJiBzdXJ2aXZlZCA9PSAxKSAvIHN1bShzdXJ2aXZlZCA9PSAxKQp9CgojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBjb3JyZWN0IGd1ZXNzZXMgZm9yIGRlYWQgY2FzZXMgaW4gYSBjb2x1bW4KIyBUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIGFjY3VyYWN5IG9mIHByZWRpY3Rpb25zIGZvciB0aGUgJ0RlYWQnIGNhc2VzICh3aGVyZSAnU3Vydml2ZWQnID0gMCkgaW4gYSBnaXZlbiBjb2x1bW4uCmNhbGN1bGF0ZV9jb3JyZWN0X2d1ZXNzZXNfZGVhZCA8LSBmdW5jdGlvbihjb2x1bW4sIHN1cnZpdmVkKSB7CiAgc3VtKGNvbHVtbiA9PSAwICYgc3Vydml2ZWQgPT0gMCkgLyBzdW0oc3Vydml2ZWQgPT0gMCkKfQoKIyBDYWxjdWxhdGUgY29ycmVjdCBndWVzc2VzIGZvciBlYWNoIGNvbHVtbgojIFRoZSAnc2FwcGx5KCknIGZ1bmN0aW9uIGFwcGxpZXMgdGhlICdjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzKCknIGZ1bmN0aW9uIHRvIGVhY2ggY29sdW1uIG9mICd0ZXN0X3NvbHV0aW9uJyAoZXhjbHVkaW5nIHRoZSBmaXJzdCBjb2x1bW4sIHdoaWNoIGlzICdTdXJ2aXZlZCcpLgojIFRoZSByZXN1bHQgaXMgdGhlIG92ZXJhbGwgYWNjdXJhY3kgb2YgcHJlZGljdGlvbnMgZm9yIGVhY2ggY29sdW1uLgpjb3JyZWN0X2d1ZXNzZXMgPC0gc2FwcGx5KHRlc3Rfc29sdXRpb25bLTFdLCBjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzLCB0ZXN0X3NvbHV0aW9uJFN1cnZpdmVkKQpjb3JyZWN0X2d1ZXNzZXMgPC0gYygxLCBjb3JyZWN0X2d1ZXNzZXMpCgoKIyBDYWxjdWxhdGUgY29ycmVjdCBndWVzc2VzIGZvciBzdXJ2aXZlZCBjYXNlcyBpbiBlYWNoIGNvbHVtbgpjb3JyZWN0X2d1ZXNzZXNfc3Vydml2ZWQgPC0gc2FwcGx5KHRlc3Rfc29sdXRpb25bLTFdLCBjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzX3N1cnZpdmVkLCB0ZXN0X3NvbHV0aW9uJFN1cnZpdmVkKQpjb3JyZWN0X2d1ZXNzZXNfc3Vydml2ZWQgPC0gYygxLCBjb3JyZWN0X2d1ZXNzZXNfc3Vydml2ZWQpCgojIENhbGN1bGF0ZSBjb3JyZWN0IGd1ZXNzZXMgZm9yIGRlYWQgY2FzZXMgaW4gZWFjaCBjb2x1bW4KY29ycmVjdF9ndWVzc2VzX2RlYWQgPC0gc2FwcGx5KHRlc3Rfc29sdXRpb25bLTFdLCBjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzX2RlYWQsIHRlc3Rfc29sdXRpb24kU3Vydml2ZWQpCmNvcnJlY3RfZ3Vlc3Nlc19kZWFkIDwtIGMoMSwgY29ycmVjdF9ndWVzc2VzX2RlYWQpCgojIEFkZCB0aGUgdGhyZWUgcm93cyB0byB0aGUgb3JpZ2luYWwgZGF0YWZyYW1lCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgJ3NvbHV0aW9uJyB3aXRoIHRoZSBjYWxjdWxhdGVkIGFjY3VyYWN5IHZhbHVlcyBhbmQgY29ycmVzcG9uZGluZyBjb2x1bW4gbmFtZXMuCnNvbHV0aW9uIDwtIGRhdGEuZnJhbWUoIkFjY3VyYWN5IiA9IGNvcnJlY3RfZ3Vlc3NlcywgCiAgICAgICAgICAgICAgICAgIkFjY3VyYWN5OiBTdXJ2aXZhbCIgPSBjb3JyZWN0X2d1ZXNzZXNfc3Vydml2ZWQsCiAgICAgICAgICAgICAgICAgIkFjY3VyYWN5OiBEZWFkIiA9IGNvcnJlY3RfZ3Vlc3Nlc19kZWFkKQojIFJlbW92ZSB0aGUgZmlyc3Qgcm93ICh3aXRoIHRoZSB2YWx1ZSAxKSBhcyBpdCByZXByZXNlbnRzIHRoZSBvdmVyYWxsIGFjY3VyYWN5LCBub3QgcmVsYXRlZCB0byBhbnkgc3BlY2lmaWMgY29sdW1uLgpzb2x1dGlvbiA8LSBzb2x1dGlvblstMSxdCgpgYGAKCmBgYHtyfQpzb2x1dGlvbgpgYGAKVGhlIHRhYmxlIGFib3ZlIGluZGljYXRlcyB0aGUgb3V0cHV0IG9mIHZhcmlvdXMgbW9kZWxzLiBUaGUgIkFjY3VyYWN5IiBjb2x1bW4gc2hvd3MgdGhlIG92ZXJhbGwgYWNjdXJhY3kgb2YgdGhlIG1vZGVsIHdoaWxlICJBY2N1cmFjeSBTdXJ2aXZhbCIgaW5kaWNhdGVzIHRoZSBhY2N1cmFjeSBvZiB0aGUgc3Vydml2YWwgcmF0ZSBpbiB0aGUgc3BlY2lmaWMgbW9kZWwuCgpPdmVyYWxsIHRoZSBib29zdGluZyB3aXRob3V0IFBDQSBkb2VzIGJlc3QgYXQgcHJlZGljdGluZyBvdXIgdGVzdCBkYXRhLiBJdCBkb2VzIHdoZW4gY29uc2lkZXJpbmcgYWNjdXJhY3kgb24gYWxsIGNhc2VzIGJ1dCBhbHNvIHdoZW4ganVzdCB0cnlpbmcgdG8gcHJlZGljdCBvbmx5IHRoZSBzdXJ2aXZhbCAgb2YgdGhlIG9ic2VydmF0aW9uLiBQcmVkaWN0aW9ucyBvZiBkZWF0aCBhcmUgc3VycHJpc2luZ2x5IGVub3VnaCBiZXR0ZXIgcGVyZm9ybWVkIGJ5IGluY2x1ZGluZyBhbGwgUENBcyBpbiBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFRoaXMgdmFyaWFudCBhbHNvIHByZWRpY3RzIHRoZSBzZWNvbmQgYmVzdCBvdmVyYWxsIGNsb3NlbHkgZm9sbG93ZWQgYnkgdGhlIGJhc2ljIHRyZWUgY2xhc3NpZmljYXRpb24uCk5vdGFibHkgYmFnZ2luZyBhbmQgYm9vc3RpbmcgdW5kZXIgUENBIHBlcmZvcm0gdGhlIHdvcnN0IGV2ZW4gdGhvdWdoIGJvb3N0aW5nIHdpdGhvdXQgUENBIHBlcmZvcm1lZCBzZWNvbmQgYmVzdC4gQXMgYSBjb21wYXJpc29uIHdlIGFsc28gcmFuIHZhbGlkYXRpb24gb24gbW9kZWxzIHdoaWNoIGluY2x1ZGUgYWxsIFBDQSB2YXJpYWJsZXMgdG8gY2hlY2sgaWYgd2UgY291bGQgYWNoaWV2ZSBtb3JlIGFjY3VyYWN5IHRoYXQgd2F5LiBVbmZvcnR1bmF0ZWx5LCB0aGlzIGRpZCBub3QgeWllbGQgYmV0dGVyIHByZWRpY3Rpb25zIGV4Y2VwdCBmb3IgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24uCgpJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGVycm9ycyBtYXkgb2NjdXIgYmVjYXVzZSBvZiBjaGFuZ2luZyB0aGUgc2VlZC4gCgojIENyb3NzLVZhbGlkYXRpb24KCmBgYHtyfQojT3B0aW9uYWw6IE1lcmdlIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgZm9yIGNyb3NzLXZhbGlkYXRpb24gCgpmdWxsX2RhdGEgPC0gcmJpbmQoZGF0YSwgdGVzdF9kYXRhKQoKYGBgCgpgYGB7cn0KIyBDaGVjayBpZiBkYXRhIHdhcyBtZXJnZWQgYmVmb3JlCmlmIChleGlzdHMoImZ1bGxfZGF0YSIpKSB7CiAgY3ZfZGF0YSA8LSBmdWxsX2RhdGEKfSBlbHNlIHsKICBjdl9kYXRhIDwtIGRhdGEKfQoKIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBzcGxpdCBkYXRhIGludG8gSyBmb2xkcwojIEFzc2lnbiBhIGZvbGQgbnVtYmVyIHRvIGVhY2ggcm93IHJhbmRvbWx5IHVzaW5nICdjZWlsaW5nJyBhbmQgJ3NhbXBsZScuCnNwbGl0X0sgPC0gZnVuY3Rpb24oZHQsIGZvbGRzKXsKICBkdCRpZCA8LSBjZWlsaW5nKHNhbXBsZSgxOm5yb3coZHQpLCByZXBsYWNlID0gRkFMU0UsIG5yb3coZHQpKSAvIAogICAgICAgICAgICAgICAgICAgICAobnJvdyhkdCkgLyBmb2xkcykpCiAgcmV0dXJuKGR0KQp9CgojIFNldCB0aGUgbnVtYmVyIG9mIGZvbGRzIChLKSBmb3IgY3Jvc3MtdmFsaWRhdGlvbgpLIDwtIDEwCgpjdl9kYXRhIDwtIHNwbGl0X0soY3ZfZGF0YSwgSykKIyBDcmVhdGUgYSBkYXRhIGZyYW1lICdjcm9zc192YWxpZGF0aW9uJyB0byBzdG9yZSBhY2N1cmFjeSB2YWx1ZXMgZm9yIGVhY2ggbW9kZWwgYW5kIGZvbGQKY3Jvc3NfdmFsaWRhdGlvbiA8LSBkYXRhLmZyYW1lKG1hdHJpeChOQSwgbnJvdyA9IDEwLCBuY29sID0gSykpCnJvd25hbWVzKGNyb3NzX3ZhbGlkYXRpb24pIDwtIGMoIkxvZ2lzdGljIiwgIlRyZWUgTW9kZWwiLCAiTmV1cmFsIE5ldHdvcmsiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmFnZ2luZyIsICJCb29zdGluZyIsICJQQ0EgTG9naXN0aWMiLCAiUENBIFRyZWUgTW9kZWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQQ0EgTmV1cmFsIE5ldHdvcmsiLCAiUENBIEJhZ2dpbmciLCAiUENBIEJvb3N0aW5nIikKCmZvciAoaSBpbiAxOkspIHsKICAjIFNlbGVjdCB0aGUgY3VycmVudCBmb2xkIGFzIHRoZSB2YWxpZGF0aW9uIHNldCBhbmQgdGhlIHJlc3QgYXMgdGhlIHRyYWluaW5nIHNldAogIGR0IDwtIGN2X2RhdGFbY3ZfZGF0YSRpZCA9PSBpLCBdCiAgCiAgIyBQcmVkaWN0aW9uIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uCiAgZHQkbG9naXN0aWNfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX2dsbSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGR0KQogIGR0JGxvZ2lzdGljX3ByZWRpY3Rpb24gPC0gaWZlbHNlKGR0JGxvZ2lzdGljX3ByZWRpY3Rpb24gPiAwLCAxLCAwKQogIAogIGNyb3NzX3ZhbGlkYXRpb25bMSwgaV0gPC0gY2FsY3VsYXRlX2NvcnJlY3RfZ3Vlc3NlcyhkdCRsb2dpc3RpY19wcmVkaWN0aW9uLCBkdCRTdXJ2aXZlZCkKICAKICAjIFByZWRpY3Rpb24gZm9yIHRyZWUgcmVncmVzc2lvbgogIGR0JHRyZWVfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX3RyZWVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkdClbLCIxIl0KICBkdCR0cmVlX3ByZWRpY3Rpb24gPC0gaWZlbHNlKGR0JHRyZWVfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKICAKICBjcm9zc192YWxpZGF0aW9uWzIsIGldIDwtIGNhbGN1bGF0ZV9jb3JyZWN0X2d1ZXNzZXMoZHQkdHJlZV9wcmVkaWN0aW9uLCBkdCRTdXJ2aXZlZCkKICAKICAjIFByZWRpY3Rpb24gZm9yIG5ldXJhbCBuZXR3b3JrCiAgZHQkbmV1cmFsX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9uZXVyYWwsIHggPSBhcy5tYXRyaXgoZHRbLCBjKCJGaXJzdENsYXNzIiwgIlNlY29uZENsYXNzIiwgIlRoaXJkQ2xhc3MiLCAiU2V4IiwgIkFnZSIsICJTaWJzcCIsICJQYXJjaCIsICJGYXJlIiwgIlNvdXRoaGFtcHRvbiIsICJDaGVyYm91cmciLCAiUXVlZW5zdG93biIpXSksIHZlcmJvc2UgPSAwKQogIAogIGR0JG5ldXJhbF9wcmVkaWN0aW9uIDwtIGlmZWxzZShkdCRuZXVyYWxfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKCiAgY3Jvc3NfdmFsaWRhdGlvblszLCBpXSA8LSBjYWxjdWxhdGVfY29ycmVjdF9ndWVzc2VzKGR0JG5ldXJhbF9wcmVkaWN0aW9uLCBkdCRTdXJ2aXZlZCkKICAKICAjIFByZWRpY3Rpb24gZm9yIGJhZ2dpbmcKICBkdCRiYWdnaW5nX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9lbnNlbWJsZV9iYWdnaW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkdCkKICBkdCRiYWdnaW5nX3ByZWRpY3Rpb24gPC0gaWZlbHNlKGR0JGJhZ2dpbmdfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKICAKICBjcm9zc192YWxpZGF0aW9uWzQsIGldIDwtIGNhbGN1bGF0ZV9jb3JyZWN0X2d1ZXNzZXMoZHQkYmFnZ2luZ19wcmVkaWN0aW9uLCBkdCRTdXJ2aXZlZCkKICAKICAjIFByZWRpY3Rpb24gZm9yIGJvb3N0aW5nCiAgZHQkYm9vc3RpbmdfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX2Vuc2VtYmxlX2Jvb3N0aW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZHQpCiAgZHQkYm9vc3RpbmdfcHJlZGljdGlvbiA8LSBpZmVsc2UoZHQkYm9vc3RpbmdfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKICAKICBjcm9zc192YWxpZGF0aW9uWzUsIGldIDwtIGNhbGN1bGF0ZV9jb3JyZWN0X2d1ZXNzZXMoZHQkYm9vc3RpbmdfcHJlZGljdGlvbiwgZHQkU3Vydml2ZWQpCiAgCiAgIyNQQ0EKICAKICAjIFByZWRpY3Rpb24gZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24KICBkdCRsb2dpc3RpY19wY2FfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsX3BjYV9nbG0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkdCkKICBkdCRsb2dpc3RpY19wY2FfcHJlZGljdGlvbiA8LSBpZmVsc2UoZHQkbG9naXN0aWNfcGNhX3ByZWRpY3Rpb24gPiAwLCAxLCAwKQogIAogIGNyb3NzX3ZhbGlkYXRpb25bNiwgaV0gPC0gY2FsY3VsYXRlX2NvcnJlY3RfZ3Vlc3NlcyhkdCRsb2dpc3RpY19wY2FfcHJlZGljdGlvbiwgZHQkU3Vydml2ZWQpCiAgCiAgIyBQcmVkaWN0aW9uIGZvciB0cmVlIHJlZ3Jlc3Npb24KICBkdCR0cmVlX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX3RyZWVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkdClbLCIxIl0KICBkdCR0cmVlX3BjYV9wcmVkaWN0aW9uIDwtIGlmZWxzZShkdCR0cmVlX3BjYV9wcmVkaWN0aW9uID4gMC41LCAxLCAwKQogIAogIGNyb3NzX3ZhbGlkYXRpb25bNywgaV0gPC0gY2FsY3VsYXRlX2NvcnJlY3RfZ3Vlc3NlcyhkdCR0cmVlX3BjYV9wcmVkaWN0aW9uLCBkdCRTdXJ2aXZlZCkKICAKICAjIFByZWRpY3Rpb24gZm9yIG5ldXJhbCBuZXR3b3JrCiAgZHQkbmV1cmFsX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX25ldXJhbCwgeCA9IGFzLm1hdHJpeChkdFssIGMoIlBDMSIsICJQQzIiLCAiUEMzIiwgIlBDNCIpXSksIHZlcmJvc2UgPSAwKQogIGR0JG5ldXJhbF9wY2FfcHJlZGljdGlvbiA8LSBpZmVsc2UoZHQkbmV1cmFsX3BjYV9wcmVkaWN0aW9uID4gMC41LCAxLCAwKQogIAogIGNyb3NzX3ZhbGlkYXRpb25bOCwgaV0gPC0gY2FsY3VsYXRlX2NvcnJlY3RfZ3Vlc3NlcyhkdCRuZXVyYWxfcGNhX3ByZWRpY3Rpb24sIGR0JFN1cnZpdmVkKQogIAogICMgUHJlZGljdGlvbiBmb3IgYmFnZ2luZwogIGR0JGJhZ2dpbmdfcGNhX3ByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbF9wY2FfZW5zZW1ibGVfYmFnZ2luZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZHQpCiAgZHQkYmFnZ2luZ19wY2FfcHJlZGljdGlvbiA8LSBpZmVsc2UoZHQkYmFnZ2luZ19wY2FfcHJlZGljdGlvbiA+IDAuNSwgMSwgMCkKICAKICBjcm9zc192YWxpZGF0aW9uWzksIGldIDwtIGNhbGN1bGF0ZV9jb3JyZWN0X2d1ZXNzZXMoZHQkYmFnZ2luZ19wY2FfcHJlZGljdGlvbiwgZHQkU3Vydml2ZWQpCiAgCiAgIyBQcmVkaWN0aW9uIGZvciBib29zdGluZwogIGR0JGJvb3N0aW5nX3BjYV9wcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWxfcGNhX2Vuc2VtYmxlX2Jvb3N0aW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZHQpCiAgZHQkYm9vc3RpbmdfcGNhX3ByZWRpY3Rpb24gPC0gaWZlbHNlKGR0JGJvb3N0aW5nX3BjYV9wcmVkaWN0aW9uID4gMC41LCAxLCAwKQogIAogIGNyb3NzX3ZhbGlkYXRpb25bMTAsIGldIDwtIGNhbGN1bGF0ZV9jb3JyZWN0X2d1ZXNzZXMoZHQkYm9vc3RpbmdfcGNhX3ByZWRpY3Rpb24sIGR0JFN1cnZpdmVkKQogICAgCiAgCn0KCiMgQ2FsY3VsYXRlIG1lYW4gYWNjdXJhY3kgb3ZlciBmb2xkcwpjcm9zc192YWxpZGF0aW9uJE1lYW5fYWNjdXJhY3kgPC0gcm93TWVhbnMoY3Jvc3NfdmFsaWRhdGlvbikKIyBDYWxjdWxhdGUgZXhwZWN0ZWQgbG9zcwpjcm9zc192YWxpZGF0aW9uJEV4cGVjdGVkX0xvc3MgPC0gKDEgLSBjcm9zc192YWxpZGF0aW9uJE1lYW5fYWNjdXJhY3kpCgpgYGAKCmBgYHtyfQpjcm9zc192YWxpZGF0aW9uWywgYygxMSwgMTIpXQpgYGAKClRoZSB0YWJsZSBpbmRpY2F0ZXMgYE1lYW5fYWNjdXJhY3lgIGFuZCBgRXhwZWN0ZWRfTG9zc2AgZm9yIGVhY2ggbW9kZWwuIGBNZWFuX2FjY3VyYWN5YCBpcyBnb29kIGV4YW1wbGUgb2YgcmVwcmVzZW50aW5nIHRoZSBhdmVyYWdlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCBhY3Jvc3MgYWxsIHRoZSAxMCBmb2xkcyBpbiB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZXNzLiBJbiBvdGhlciB3b3JkcywgaXQgc2hvd3MgaG93IHdlbGwgdGhlIG1vZGVsIHBlcmZvcm1zIG9uIGF2ZXJhZ2UgaW4gcHJlZGljdGluZyB0aGUgc3Vydml2YWwgcmF0ZS4gT24gdGhlIG90aGVyIGhhbmQsIGBFeHBlY3RlZF9Mb3NzYCBpbmRpY2F0ZXMgdGhlIHByb3BvcnRpb24gb2YgbWlzY2xhc3NpZmllZCBpbnN0YW5jZXMuIEluIHNob3J0LCB0aGUgbG93ZXIgdGhlIGV4cGVjdGVkIGxvc3MsIHRoZSBiZXR0ZXIgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbC4KSXQgaXMgY2xlYXIgdGhhdCwgYWZ0ZXIgY3Jvc3MtdmFsaWRhdGlvbiB3ZSBmaW5kIHRoYXQgYmFnZ2luZyB3aXRob3V0IFBDQSBzZWVtcyB0byBwZXJmb3JtIGJlc3QsIGNsb3NlbHkgZm9sbG93ZWQgYnkgYmFnZ2luZyB3aXRoIFBDQS4gCgo=