Statistical Model for Predicting Newborn Weight

Company Context Company: Neonatal Health Solutions

Objective: Create a statistical model capable of accurately predicting newborn birth weight, based on clinical variables collected from three hospitals.

The project aims to improve the management of high-risk pregnancies, optimizing hospital resources and ensuring better outcomes for neonatal health.

The variables collected include:

Maternal Age: Measure of age in years.

kable(summary(dataset), caption="Summary of the variables in the dataset") %>%
  kable_styling()
Summary of the variables in the dataset
Anni.madre N.gravidanze Fumatrici Gestazione Peso Lunghezza Cranio Tipo.parto Ospedale Sesso
Min. : 0.00 Min. : 0.0000 0:2396 Min. :25.00 Min. : 830 Min. :310.0 Min. :235 Length:2500 Length:2500 Length:2500
1st Qu.:25.00 1st Qu.: 0.0000 1: 104 1st Qu.:38.00 1st Qu.:2990 1st Qu.:480.0 1st Qu.:330 Class :character Class :character Class :character
Median :28.00 Median : 1.0000 NA Median :39.00 Median :3300 Median :500.0 Median :340 Mode :character Mode :character Mode :character
Mean :28.16 Mean : 0.9812 NA Mean :38.98 Mean :3284 Mean :494.7 Mean :340 NA NA NA
3rd Qu.:32.00 3rd Qu.: 1.0000 NA 3rd Qu.:40.00 3rd Qu.:3620 3rd Qu.:510.0 3rd Qu.:350 NA NA NA
Max. :46.00 Max. :12.0000 NA Max. :43.00 Max. :4930 Max. :565.0 Max. :390 NA NA NA

We note that the variable Age of the mother has a minimum value equal to 0 meaning that there are some wrong data. So we first display the distribution of the age of the mothers.

ggplot(data = subset(dataset))+
  geom_bar(aes(x=Anni.madre),
           stat="count",
           col="khaki",
           fill="khaki")+
  labs(title="Distribution of the mother age",
       x="Age",
       y="absolute frequency")+
  scale_y_continuous(breaks = seq(0,1200,200))+
  scale_x_continuous(breaks = seq(0,46,2))+
  theme_classic()+
  theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
        axis.title.y = element_text(size = 14, margin = margin(r = 10)))

We can assume that the minimum meaningfull age of a mother is 12 years. So we filter the dataset to remove the dirty data corresponding to mothers with an age smaller than 12.

dataset <- dataset %>% 
  filter(Anni.madre >=12)

Next, we start analyzing the anthropometrics data (Weigth, Length and Cranial Circumference) of the newborns in the dataset.

First we report a table with the summary of the statistics of the anthropometrics variables

Indices of weight, length and cranial diameter
quartile_1 median quartile_3 mean sd cv skewness kurtosis std_err
Cranial circumference (mm) 330 340 350 340.03 16.43 4.83 -0.79 2.94 0.33
Weight (gr) 2990 3300 3620 3284.18 525.23 15.99 -0.65 2.03 10.51
Length (mm) 480 500 510 494.70 26.33 5.32 -1.51 6.48 0.53

Second we display the distribution of the anthropometric variables again a normal distribution

# the function normalize takes in input a variable in string form and returns its values normalized by its mean and standard deviation
normalize <- function(x){
  return ((x-mean(x))/sd(x))
}

# we generate a standard normal distribution
gaussian_distribution <- rnorm(1000000,0,1)


# we plot the density of the different variables against a standard normal distribution
ggplot()+
  geom_density(aes(x=gaussian_distribution, fill="N01"), color=NA,  alpha=.8)+
  geom_density(aes(x=normalize(Peso), color="Weight"), linewidth=1.5)+
  geom_density(aes(x=normalize(Cranio), color="Cranial circumference"), linewidth=1.5)+
  geom_density(aes(x=normalize(Lunghezza), color="Length"), linewidth=1.5)+
  labs(title="Distribution of the variables",
       x="Normalized variable ",
       y="density")+
  scale_fill_manual(values = c(N01 = "grey"),   
                    labels = c(N01 = "N(0,1)")) +
  theme_classic()+
  theme(plot.title.position = "panel",
        plot.title=element_text(hjust=0.5))+
  guides(fill = guide_legend(override.aes = list(color = NA)))+
  labs(color = "Variabile",
      fill = "Distribuzione normale")

The plots as also the values of skeweness and Kurtosis show that:

  • All the anthropometric variables are more peaked than a normal distribution, so the probability of outliers is smaller that for the normal distribution with same mean and standard deviation.

  • Values in the left tails of the distributions are more likely than values in the right tail. This could be due to the data relative to premature births.

To check if the asymmetry is due to the premature births we plot the distribution of births by gestational weeks.

ggplot(data=dataset)+
  geom_bar(aes(x=Gestazione),
           stat="count",
           col="pink",
           fill="pink")+
  labs(title="Distribution of gestational age at childbirth",
       x="gestational age (weeks)",
       y="absolute frequency")+
  scale_y_continuous(breaks = seq(0,750,50))+
  scale_x_continuous(breaks = seq(25,43,1))+
  theme_classic()+
  theme(axis.text.x = element_text(angle = 15, hjust = 1),
        axis.title.x = element_text(size = 14, margin = margin(t = 10)),
        axis.title.y = element_text(size = 14, margin = margin(r = 10)))

A delivery is considered premature if it occurs before 37 weeks of gestation. By looking at the barplot we note there exist different many premature birthds in the dataset. We expect the anthropometric measurements of premature newborns to be significantly smaller.

So we filter the data relative only to the non-premature births and study their statistics.

regular_deliveries_data <- dataset %>% filter(Gestazione >= 37)

statistics_regular_deliveries <- data.frame(
        
  quartile_1 = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, function(x) quantile(x,probs=0.25, names=FALSE)),2),
                          
  median = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, median),2),
                            
  quartile_3 = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, function(x) quantile(x,probs=0.75, names=FALSE)),2),
                          
  mean = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, mean),2),
                          
  sd = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, sd),2),
                          
  cv = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, cv_funct),2),
                          
  skewness = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, skewness),2),
                          
  kurtosis = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, kurtosis_index),2),
  
  std_err = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")],2,std.err),2)
)

rownames(statistics_regular_deliveries) <- c("Cranial circumference (mm)", "Weight (gr)", "Length (mm)")

kable(statistics_regular_deliveries, caption="Indices of weight, length and cranial diameter of the non-premature births") %>%
  kable_styling()
Indices of weight, length and cranial diameter of the non-premature births
quartile_1 median quartile_3 mean sd cv skewness kurtosis std_err
Cranial circumference (mm) 332 340.5 350 341.71 14.21 4.16 -0.01 0.30 0.29
Weight (gr) 3050 3320.0 3640 3349.56 443.69 13.25 0.13 0.21 9.18
Length (mm) 485 500.0 510 498.11 21.16 4.25 -0.44 2.74 0.44



# we plot the density of the different variables against a standard normal distribution
ggplot()+
  geom_density(aes(x=gaussian_distribution, fill="N01"), color=NA,  alpha=.8)+
  geom_density(aes(x=normalize(regular_deliveries_data$Peso), color="Weight"), linewidth=1.5)+
  geom_density(aes(x=normalize(regular_deliveries_data$Cranio), color="Cranial circumference"), linewidth=1.5)+
  geom_density(aes(x=normalize(regular_deliveries_data$Lunghezza), color="Length"), linewidth=1.5)+
  labs(title="Distribution of the anthropometric variables \n relative to the non-premature deliveries",
       x="Normalized variable ",
       y="density")+
  scale_fill_manual(values = c(N01 = "grey"),   
                    labels = c(N01 = "N(0,1)")) +
  theme_classic()+
  theme(plot.title.position = "panel",
        plot.title=element_text(hjust=0.5))+
  guides(fill = guide_legend(override.aes = list(color = NA)))+
  labs(color = "Variabile",
      fill = "Distribuzione normale")

The significantly smaller values of skeweness of all the anthropometric variables corresponding only to the non-premature births confirm that the asymmetry that we had previously observed was mainly due to the presence of premature births. Also the values of the kurtosis have significanlty decreased suggesting a much better normal approximation of the distribution of the anthropometric variables when filtering the data corresponding to the only non-premature deliveries. The length is the only variables that is not really normally distributed. The non-normality of the variable Lenght could be due to the presenze of some corrupted data. To this end we study the correlation between the anthropometric variables.



cat("The correlation coefficiente of the variable Length with the varibles Weight and Cranial circumference in non premature newborns is equal to: \n", 
    "-cor(Length, Weight) = ", cor(regular_deliveries_data$Lunghezza, regular_deliveries_data$Peso)
 , "\n",
    "-cor(Length, Cranial circumference) = ", cor(regular_deliveries_data$Lunghezza, regular_deliveries_data$Cranio)
)
The correlation coefficiente of the variable Length with the varibles Weight and Cranial circumference in non premature newborns is equal to: 
 -cor(Length, Weight) =  0.7118152 
 -cor(Length, Cranial circumference) =  0.4566794
scatt1 = ggplot(regular_deliveries_data, aes(x = Lunghezza, y = Peso)) + 
                geom_point()+
                theme_classic()+
                theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                      axis.title.y = element_text(size = 14, margin = margin(r = 10)))+
                labs(title="Scatterplot of Lenght against Weight",
                                        x=" Lenght(mm)",
                                        y=" Weight(gr)")+
                scale_y_continuous(breaks = seq(2000,5000,500))+
                scale_x_continuous(breaks = seq(300,600,50))


scatt2 = ggplot(regular_deliveries_data, aes(x = Lunghezza, y = Cranio)) + 
                geom_point()+
                theme_classic()+
                theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                      axis.title.y = element_text(size = 14, margin = margin(r = 10)))+
                labs(title="Scatterplot of Lenght against Cranial circumference",
                                        x=" Lenght(mm)",
                                        y=" Cranial circumference(mm)")+
                scale_x_continuous(breaks = seq(300,600,50))

comb_fig_scatt <- scatt1 + scatt2 + plot_layout(ncol=2, heights = c(5), widths= c(5,5))
print(comb_fig_scatt)

From the scatterplots, it can be seen that there is one outlier measurement reporting a length smaller than 350 mm, which could be due to an error in the dataset, such as a typo in the annotation of the newborn’s length. So we decide to clean the dataset of regular newborns from this datapoint.

regular_deliveries_data <- regular_deliveries_data %>% filter(Lunghezza >= 350)

statistics_regular_deliveries <- data.frame(
        
  quartile_1 = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, function(x) quantile(x,probs=0.25, names=FALSE)),2),
                          
  median = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, median),2),
                            
  quartile_3 = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, function(x) quantile(x,probs=0.75, names=FALSE)),2),
                          
  mean = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, mean),2),
                          
  sd = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, sd),2),
                          
  cv = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, cv_funct),2),
                          
  skewness = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, skewness),2),
                          
  kurtosis = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")], 2, kurtosis_index),2),
  
  std_err = round(apply(regular_deliveries_data[c("Cranio","Peso","Lunghezza")],2,std.err),2)
)

rownames(statistics_regular_deliveries) <- c("Cranial circumference (mm)", "Weight (gr)", "Length (mm)")

kable(statistics_regular_deliveries, caption="Indices of weight, length and cranial diameter of the non-premature births") %>%
  kable_styling()
Indices of weight, length and cranial diameter of the non-premature births
quartile_1 median quartile_3 mean sd cv skewness kurtosis std_err
Cranial circumference (mm) 332 340 350 341.69 14.20 4.15 -0.01 0.30 0.29
Weight (gr) 3050 3320 3640 3349.12 443.28 13.24 0.13 0.21 9.17
Length (mm) 485 500 510 498.19 20.82 4.18 -0.18 0.56 0.43

We observe that now, in the cleaned dataset, also the variable Length follows approximately a normal distribution, with both kurtosis and skewness coefficients close to 0.

Analysis of weight, length and cranial diameter of the sample with respect to the population statistics.

According to the study International standards for newborn weight, length, and head circumference by gestational age and sex, the birthweight, birthlength and birth head circumference of babies born in Italy from the 37th week of gestation on, have the following mean and standard deviation:

  • birthweight: Mean = 3.3, SD = 0.4,

  • birthlength: Mean = 49.4, SD = 1.7,

  • birth head circumference: Mean = 34.0, SD = 1.2

From the central limit theorem we know that given a random variable X with mean \(\mu_0\) and standard deviation \(\sigma\), the mean over a random sample of N people distributes as a random variable following a normal distribution with mean \(\mu_0\) and standard deviation \(\sigma/N\).

So for the three variables weight, length and cranial circumference we assume as null hypothesis (H0) that they have mean \(\mu_0=\) 3300 gr, 49.4 cm and 34 cm and standard deviation \(\sigma=\) 400gr, 1.7cm, 1.2cm, respectively. Then we test this hypotheses using a Z-test. To this end we consider the sub-dataset corresponding to the births after the 37th week of gestation, having length \(N\).

Then we compute the Z-test value \[Z = \frac{mean(Variable)-\mu_0}{\sigma/\sqrt{N}}.\]

We compute the corresponfing p-values, i.e. the probability of getting a value equal or smaller than mean(Variable) by randomly sampling N births. If the p-value is smaller than the level of significance that we set to 0.05, we reject the hypothesis H0.

z_test = function(x, mu0, stdev, alfa){

  x = na.omit(x)
  mu_cap = mean(x)
  n = length(x)
  
  Z = (mu_cap - mu0) / (stdev / sqrt(n))
  valori.soglia = qnorm(c(alfa/2, 1-alfa/2))
  
  CI <- c(mu_cap+qnorm(alfa/2)*(stdev/sqrt(n)),
          mu_cap-qnorm(alfa/2)*(stdev/sqrt(n)))
    
  return(
    list(mean.sample= mu_cap,
         stat.test = Z,
         threshold.val = valori.soglia,
         pvalue = 2*pnorm(-abs(Z)),
         Int.Conf. = CI
         ))
}
z_test_weight = z_test(regular_deliveries_data$Peso, 3300, 400, 0.05)

z_test_length = z_test(regular_deliveries_data$Lunghezza, 494, 17, 0.05)

z_test_cranial_diam = z_test(regular_deliveries_data$Cranio, 340, 12, 0.05)


cat("The p-values of length, weight and cranial circumference are all smaller than 0.05, so we reject the hypothesis H0 that the three variables are distributed according to the distribution of weight, length and cranial diameter of the italian population. \n", 
    "-p_value(Weight) = ", z_test_weight$pvalue , "\n",
    "-p_value(Length) = ", z_test_length$pvalue , "\n",
    "-p_value(Cranial diameter) = ", z_test_cranial_diam$pvalue )
The p-values of length, weight and cranial circumference are all smaller than 0.05, so we reject the hypothesis H0 that the three variables are distributed according to the distribution of weight, length and cranial diameter of the italian population. 
 -p_value(Weight) =  2.960815e-09 
 -p_value(Length) =  1.124683e-32 
 -p_value(Cranial diameter) =  9.627454e-12

In particular we display the Z-test values of the anthropometric variables against the level of significance of 0.05 of the Z-test.

ggplot()+
  geom_density(aes(x=gaussian_distribution, fill="N01"), color=NA,  alpha=.8) +
  geom_vline(aes(xintercept=z_test_weight$stat.test, color="Weight"), linewidth=1.5) +
  geom_vline(aes(xintercept=z_test_length$stat.test, color="Length"), linewidth=1.5)+
  geom_vline(aes(xintercept=z_test_cranial_diam$stat.test, color="Cranial circumference"), linewidth=1.5)+
  geom_vline(aes(xintercept=z_test_length$threshold.val, color="Threshold p=0.05"),linewidth=1, linetype="dashed")+
  scale_color_manual(values= c("Weight"= "gold1", "Length"="gold4", "Cranial circumference"="gold3", "Threshold p=0.05"="red"))+
  scale_fill_manual(values = c(N01 = "grey"),   
                    labels = c(N01 = "N(0,1)")) +
  labs(title="Z values of weight, length and cranial circumference",
       x=" ",
       y=" ")+
  theme_classic()+
  theme(axis.text.x = element_text(angle = 15, hjust = 1),
        axis.title.x = element_text(size = 14, margin = margin(t = 10)),
        axis.title.y = element_text(size = 14, margin = margin(r = 10)),
        legend.position ="bottom")

Analysis of the differences in the antrhopometric variables across the different between males and females.

First we display the distribution of newborns between the two sexes, which result quite uniform.

sex_colors <- c("M" = "dodgerblue", "F" = "hotpink")  


ggplot(data=dataset)+
  geom_bar(aes(x=Sesso,
               fill=Sesso,
               color=Sesso),
           stat="count")+
  labs(title="Sex distribution of newborns",
       x="sex",
       y="absolute frequency",
       color="SEX:",
       fill="SEX:")+
  scale_fill_manual(values= sex_colors)+
  scale_y_continuous(breaks = seq(0,1500,100))+
  theme_classic()+
  theme(axis.text.x = element_text(angle = 15, hjust = 1),
        axis.title.x = element_text(size = 14, margin = margin(t = 10)),
        axis.title.y = element_text(size = 14, margin = margin(r = 10)))

Second we study the statistics of the anthropometric variables across the two sexes and display them via a boxplot. In order to test later differences across the sexes using a t.test it is important to compare normally distrubuted quantities. For this reason, we restrict our study only to the not-premature deliveries.

cv_funct <- function(x) {
  return(sd(x) / mean(x) * 100)
}


conditional_dataset_sex <- regular_deliveries_data %>% 
  group_by(Sesso) %>% 
  summarise(mean_peso = round(mean(Peso),2),
            sd_peso = round(sd(Peso),2),
            cv_peso = round(cv_funct(Peso), 2),
            skew_peso = round(skewness(Peso), 2),
            kurt_peso = round(kurtosis_index(Peso), 2),
            
            mean_lunghezza = round(mean(Lunghezza),2),
            sd_lunghezza = round(sd(Lunghezza),2),
            cv_lunghezza = round(cv_funct(Lunghezza), 2),
            skew_lunghezza = round(skewness(Lunghezza), 2),
            kurt_lunghezza = round(kurtosis_index(Lunghezza), 2),
            
            mean_cranio = round(mean(Cranio),2),
            sd_cranio = round(sd(Cranio),2),
            cv_cranio = round(cv_funct(Cranio), 2),
            skew_cranio = round(skewness(Cranio), 2),
            kurt_cranio = round(kurtosis_index(Cranio), 2))


colnames(conditional_dataset_sex) <- c("sex", "mean", "sd", "cv", "Skew", "kurt" , "mean", "sd", "cv" , "Skew", "kurt",  "mean", "sd", "cv", "Skew", "kurt")

kable(conditional_dataset_sex, caption="Indices of weight, length and cranial diameter of non-premature newborns, divided by sex") %>%
  kable_styling(bootstrap_options = "condensed", full_width = TRUE) %>%
  add_header_above(c(" "=1, "Weight" = 5, "Length" = 5, "Cranial circumference" = 5)) %>%
  row_spec(1, background = "pink")%>%
  row_spec(2, background = "lightblue") %>%
  column_spec(c(1,6,11), border_right = TRUE)
Indices of weight, length and cranial diameter of non-premature newborns, divided by sex
Weight
Length
Cranial circumference
sex mean sd cv Skew kurt mean sd cv Skew kurt mean sd cv Skew kurt
F 3237.00 433.6 13.4 0.35 0.63 493.95 21.12 4.28 -0.17 0.74 339.56 14.08 4.15 0.06 0.33
M 3458.11 425.2 12.3 -0.04 0.26 502.31 19.67 3.92 -0.13 0.34 343.77 14.00 4.07 -0.07 0.38

box_length_sex <- ggplot(data=regular_deliveries_data,
                     aes(x=Sesso,
                        y=Lunghezza,
                        fill=Sesso),
                        color=Sesso)+
                     geom_boxplot()+
                     labs(title="Length ditribution according to sex\n of non premature newborns",
                          x=" ",
                          y="length (cm)")+
                     scale_fill_manual(values= sex_colors)+
                     theme_classic()+
                     theme(plot.title.position = "panel",
                           plot.title = element_text(size=16, hjust=0.5),
                           axis.title.x = element_text(size=14),
                           axis.title.y = element_text(size=14),
                           axis.text.x = element_text(size=14),
                           axis.text.y = element_text(size=10),
                           legend.position = "none")+
                      scale_y_continuous(breaks = seq(310, 560, by = 25))+
                      scale_x_discrete(labels=c("M"="Male", "F"="Female"))


box_weight_sex <- ggplot(data=regular_deliveries_data,
                     aes(x=Sesso,
                        y=Peso,
                        fill=Sesso),
                        color=Sesso)+
                     geom_boxplot()+
                     labs(title="Weight ditribution according to sex\n of non premature newborns",
                          x=" ",
                          y="weight (Kg)")+
                     scale_fill_manual(values= sex_colors)+
                     theme_classic()+
                     theme(plot.title.position = "panel",
                           plot.title = element_text(size=16, hjust=0.5),
                           axis.title.x = element_text(size=14),
                           axis.title.y = element_text(size=14),
                           axis.text.x = element_text(size=14),
                           axis.text.y = element_text(size=10),
                           legend.position = "none")+
                      scale_y_continuous(breaks = seq(1000, 5000, by = 400),
                                        labels = seq(1.0, 5.0, by = 0.4))+
                      scale_x_discrete(labels=c("M"="Male", "F"="Female"))


box_head_sex <- ggplot(data=regular_deliveries_data,
                     aes(x=Sesso,
                        y=Cranio,
                        fill=Sesso),
                        color=Sesso)+
                     geom_boxplot()+
                     labs(title="Cranial diameter ditribution according to sex\n of non premature newborns",
                          x=" ",
                          y="diameter (cm)")+
                     scale_fill_manual(values= c("M"="dodgerblue", "F"="hotpink"))+
                     theme_classic()+
                     theme(plot.title.position = "panel",
                           plot.title = element_text(size=16, hjust=0.5),
                           axis.title.x = element_text(size=14),
                           axis.title.y = element_text(size=14),
                           axis.text.x = element_text(size=14),
                           axis.text.y = element_text(size=10),
                           legend.position = "none")+
                      scale_y_continuous(breaks = seq(250, 400, by = 25))+
                      scale_x_discrete(labels=c("M"="Male", "F"="Female"))


comb_fig_antropometrics <- box_head_sex + box_weight_sex + box_length_sex + plot_layout(ncol=3, heights = c(5), widths= c(5,5,5))
print(comb_fig_antropometrics)

The data suggest a difference in the anthropometric variables across the two sexes, with higher values for all the variables in case of males rather than females.

We test the hypothesis that the antropometric values of males and females are significantly different

In order to perform a meaningfull t-test that compares the distributions of anthropometric measurements of males and females we first have to check that the distributions of the antrhopometric measurements of males and females are approximatelly normal and have similar variances. The previous table suggest that this should be true if we restrict our study only to the non-premature deliveries. We perform Q-Q tests and Shapiro Wilk tests to verify that the three variables distribute according to normal distributions for both sexes. We recall the Q-Q plots display the observed quantiles of the variables against the quantiles of a Gaussian distribution. So, if the variables come from a normal distribution, the points in the Q-Q plot will approximately lie along a straight line.


males <- regular_deliveries_data %>%
  filter(Sesso=="M")

females <- regular_deliveries_data %>%
  filter(Sesso=="F" & Lunghezza>400)


## QQ-plots of the length
QQ_length_males = ggplot(males, aes(sample = Lunghezza)) +
  labs(title="Q-Q Plot of the length\n of male newborns",
            x="Theoretical Quantiles",
            y="Observed Quantiles")+
  stat_qq(color = "dodgerblue") +
  stat_qq_line(color="red") +
  theme_classic()

QQ_length_females = ggplot(females, aes(sample = Lunghezza)) +
  labs(title="Q-Q Plot of the length\n of female newborns",
            x="Theoretical Quantiles",
            y="Observed Quantiles")+
  stat_qq(color = "hotpink") +
  stat_qq_line(color="black") +
  theme_classic()

## QQ_plots of the weight
QQ_weight_males = ggplot(males, aes(sample = Peso)) +
  labs(title="Q-Q Plot of the weight\n of male newborns",
            x="Theoretical Quantiles",
            y="Observed Quantiles")+
  stat_qq(color = "dodgerblue") +
  stat_qq_line(color="red") +
  theme_classic()

QQ_weight_females = ggplot(females, aes(sample = Peso)) +
  labs(title="Q-Q Plot of the weight\n of female newborns",
            x="Theoretical Quantiles",
            y="Observed Quantiles")+
  stat_qq(color = "hotpink") +
  stat_qq_line(color="black") +
  theme_classic()

QQ_cranial_males = ggplot(males, aes(sample = Cranio)) +
  labs(title="Q-Q Plot of the cranial circumference\n of male newborns",
            x="Theoretical Quantiles",
            y="Observed Quantiles")+
  stat_qq(color = "dodgerblue") +
  stat_qq_line(color="red") +
  theme_classic()

QQ_cranial_females = ggplot(females, aes(sample = Cranio)) +
  labs(title="Q-Q Plot of the cranial circumference\n of female newborns",
            x="Theoretical Quantiles",
            y="Observed Quantiles")+
  stat_qq(color = "hotpink") +
  stat_qq_line(color="black") +
  theme_classic()

comb_QQ <- QQ_length_males + QQ_length_females +
                  QQ_weight_males + QQ_weight_females +
                  QQ_cranial_males + QQ_cranial_females +
                  plot_layout(ncol=2, heights = c(5,5,5), widths= c(5,5))

print(comb_QQ)



shaptestLM = shapiro.test(males$Lunghezza)
shaptestLF = shapiro.test(females$Lunghezza)
shaptestWM = shapiro.test(males$Peso)
shaptestWF = shapiro.test(females$Peso)
shaptestCM = shapiro.test(males$Cranio)
shaptestCF = shapiro.test(females$Cranio)

cat("The maximum Shapiro-Wilk statistics of the 6 normality tests about distribution of weight, length and cranial circumference across the two sexes is:", min(shaptestLM$statistic, shaptestLF$statistic, shaptestWM$statistic, shaptestWF$statistic, shaptestCM$statistic, shaptestCF$statistic), "\n")
The maximum Shapiro-Wilk statistics of the 6 normality tests about distribution of weight, length and cranial circumference across the two sexes is: 0.9875359 
cat("The minimum Shapiro-Wilk p.value of the 6 normality tests about distribution of weight, length and cranial circumference across the two sexes is:", min(shaptestLM$p.value, shaptestLF$p.value, shaptestWM$p.value, shaptestWF$p.value, shaptestCM$p.value, shaptestCF$p.value))
The minimum Shapiro-Wilk p.value of the 6 normality tests about distribution of weight, length and cranial circumference across the two sexes is: 2.185776e-08

From the Q-Q plots and the Shapiro-Wilk tests we can argue that the anthropometric variables, even if not exactly distributed according to normal distributions, are really close to normal distributions. So we can perform a t.test to verify the null hypothesis that the distribution followed by males and females is the same.

ttest_length = t.test(males$Lunghezza, females$Lunghezza,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 

ttest_weight = t.test(males$Peso, females$Peso,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 

ttest_cranium = t.test(males$Cranio, females$Cranio,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 

cat("The p-values of the t-tests about length, weight and cranial circumference distribution comparison between males and females are: \n", 
    "-p_value(Weight) = ", ttest_weight$p.value , "\n",
    "-p_value(Length) = ", ttest_length$p.value , "\n",
    "-p_value(Cranial diameter) = ", ttest_cranium$p.value )
The p-values of the t-tests about length, weight and cranial circumference distribution comparison between males and females are: 
 -p_value(Weight) =  3.149166e-34 
 -p_value(Length) =  1.88921e-22 
 -p_value(Cranial diameter) =  8.994501e-13

The very small p-values of the three t.tests show that there is a significant statistical difference in the mean of the anthropometric variables between males and females. In particular we can refuse the null hypothesis H0 and assert that the distributions of the anthropometric measurements followed by males and females are different.

Analysis of cesarean deliveries

Now we analyze the number of cesarean deliveries across the different hospitals. First we study the the absolute and relative frequency of each hospital.

#create a conditional dataset with the number of deliveries and cesarean deliveries per hospital
conditional_dataset_ospedale <- 
  dataset %>% 
  group_by(Ospedale) %>%  
  summarise(cesareo = sum(Tipo.parto == "Ces", na.rm = TRUE),
            naturale = sum(Tipo.parto == "Nat", na.rm = TRUE),
            parti= sum())
Freq_ass_hosp = table(dataset["Ospedale"])
Freq_rel_hosp = table(dataset["Ospedale"])/N_observations
Freq_hosp = cbind(Freq_ass_hosp,Freq_rel_hosp)
rownames(Freq_hosp) <- c("Hospital1","Hospital2","Hospital3")
colnames(Freq_hosp) <- c("Absolute frequency","Relative frequency")
kable(t(Freq_hosp), caption = 'Deliveries distribution across hospitals') %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Deliveries distribution across hospitals
Hospital1 Hospital2 Hospital3
Absolute frequency 816.0000 848.0000 834.0000
Relative frequency 0.3264 0.3392 0.3336

We have observed that the distribution of the deliveries across the three hospitals is essentially uniform.

Next we use a boxplot to display the percentage of cesarean deliveries in each hospital.


hospitals=c("osp1" = "Hospital1", "osp2"="Hospital2", "osp3"="Hospital3")

dataset$Tipo.parto <- factor(dataset$Tipo.parto, levels = c("Nat", "Ces"))

bar_cesarean_hospital =  ggplot(data=dataset)+
                                geom_bar(aes(x=Ospedale,
                                             fill=Tipo.parto),
                                         position="fill")+
                                labs(title="Percentage of cesarean childbirths by hospitals",
                                     x="",
                                     y="Percentage",
                                     fill="type of delivery:")+
                                scale_fill_manual(values= c("Ces"="red", "Nat"="ivory2"), labels = c("Ces"= "Cesarean", "Nat"="Natural"))+
                                theme_classic()+
                                theme(plot.title.position = "panel",
                                      plot.title = element_text(size=14, hjust=0.5),
                                      axis.title.x = element_text(size=12),
                                      axis.title.y = element_text(size=12),
                                      axis.text.x = element_text(size=10),
                                      axis.text.y = element_text(size=10),
                                      legend.position = "bottom",
                                      legend.title = element_text(size = 12),
                                      legend.text = element_text(size = 12))+
                                 scale_y_continuous(labels = percent_format(),
                                           breaks = seq(0, 1, by = 0.1))+
                                 scale_x_discrete(labels=hospitals)


print(bar_cesarean_hospital)

The boxplot show that in all the three hospitals the percentage of cesarean deliveries is approximately of 30%. In particular, the number of cesarean deliveries seems to be marginally lower in the Hospital3 with respect to Hospital1 and Hospital2.

Next we test the null hypothesis that the distribution of cesarean deliveries is the same and so uniform across the three hospitals

We first use a Pearson’s Chi-squared test to examine the hypothesis that the proportion of cesarean deliveries is equal across the hospitals. It compares the observed frequencies of cesarean and natural deliveries in each hospital with the expected frequencies calculated under the null hypothesis of uniform distribution of cesarean deliveries across hospitals, literally: \[Expected\_Freq\_Ces(Hosp) = Prob(Cesarean)* Number\_Deliveries(Hosp) \] where \(Prob(Cesarean)=N\_tot(Cesarean)/N\_tot(deliveries)\). The test assesses whether the observed differences are statistically significant by providing a chi-squared statistic and a p-value to support or reject the null hypothesis.

# create contingency table bewtween hospitals and kind of delivery
table_delivery_hospital = table(Ospedale, Tipo.parto)

#The Pearson’s Chi-squared test is performed 
chisq_test = chisq.test(table_delivery_hospital)
cat("The p-value of the Pearson's Chi-squared test is" , chisq_test$p.value)
The p-value of the Pearson's Chi-squared test is 0.5777576

Based on the p-value of the Pearson’s Chi-squared we can not reject the null-hypothesis.

The test Chi-squared that we have performed is based on the assumption that for sufficiently many observations the distribution of the frequencies of cesarean deliveries at each hospital is close to a normal distribution, because of the central limit theorem. However, in this case, we know that the number of cesarean deliveries at the \(i\)-th hospital follows a binomial distribution \(B(p_i)\). So we can also perform a binomial test for each hospital to examine the hypothesis that \(p_i=Prob(Cesarean)\) for each hospital. \(Prob(Cesarean)\) as defined above is assumed as an accurate approximation of probability of cesarean delivery if the distribution is uniform across the hospitals, in particular since the distribution of deliveries per hospital is quite uniform there is no need to use a weighted formula for the probability \(Prob(Cesarean)\). We report the the \(p\)-value and the confidence interval of this test for each hospital.

# first we create a table with the total number of deliveries and cesarean deliveries observed at each hospital.

sub_dataset = subset(dataset, Ospedale=="osp1")
N_ces_hosp = sum(sub_dataset$Tipo.parto=="Ces")
N_deliveries_hosp = nrow(sub_dataset)

Freq_ces_hosp = table(Ospedale[Tipo.parto=="Ces"])
Ces_hosp = cbind(Freq_ass_hosp,Freq_ces_hosp)
Ces_hosp = as.data.frame(Ces_hosp)

rownames(Ces_hosp) <- c("Hospital_1", "Hospital_2", "Hospital_3")

# Next we compute Prob(cesarean) that should be a good approximation of the probability of oberving a cesarean delivery assuming this is uniform across the three hospitals.

prob_cesarean = sum(Tipo.parto=="Ces")/N_observations

# Last we perform a binomial test for each hospital
p_values <- numeric(nrow(Ces_hosp))
conf_int <- numeric(nrow(Ces_hosp))

for(i in seq_len(nrow(Ces_hosp))){
  test <- binom.test(Ces_hosp$Freq_ces_hosp[i], Ces_hosp$Freq_ass_hosp[i], p = prob_cesarean, alternative = "two.sided")
  p_values[i] <- test$p.value
  conf_int[i] <- paste0("(", round(test$conf.int[1],2), ",", round(test$conf.int[2],2),")")  
}

Ces_hosp$p_value <- p_values
Ces_hosp$conf_int <- conf_int

kable(t(Ces_hosp[, c("p_value", "conf_int")]), caption = 'P-values and confidence intervals for the binomial outcomes of each hospital,1n calculated under the assumption of a binomial distribution with a uniform probability\n of cesarean deliveries across all hospitals.') %>%
  kable_styling(bootstrap_options = c("striped", "hover"), position="left")
P-values and confidence intervals for the binomial outcomes of each hospital,1n calculated under the assumption of a binomial distribution with a uniform probability of cesarean deliveries across all hospitals.
Hospital_1 Hospital_2 Hospital_3
p_value 0.7289190 0.5967907 0.4236389
conf_int (0.27,0.33) (0.27,0.33) (0.25,0.31)

Also in this case the p-value of the binomial tests do not allow to reject the null hypothesis asserting that the number of deliveries across the three hospitals is uniform.

Next we analyze the frequency of smoking mothers.

Freq_rel_smoker = sum(dataset$Fumatrici == 1)/N_observations

cat("The percentage of smoking mothers is:", paste0(Freq_rel_smoker,"%"))
The percentage of smoking mothers is: 0.0416%

Moreover we investigate if the smoke influence some of the other variables, including weight, length and Cranial circumference and gestational age.


boxplot_smoking_1= ggplot(data=dataset,
                           aes(x=Fumatrici,
                               y=Peso,
                               fill=Fumatrici))+
                           geom_boxplot()+
                           labs(title="Weight distribution",
                                x=" ",
                                y="weight (kg)")+
                           scale_fill_manual(values= c("0"="green", "1"="grey"), 
                                             labels = c("0"= "Non-smoker", "1"="Smoker"))+
                           theme_classic()+
                           theme(plot.title.position = "panel",
                                 plot.title = element_text(size=16, hjust=0.5),
                                 axis.title.x = element_text(size=14),
                                 axis.title.y = element_text(size=14),
                                 axis.text.x = element_text(size=14),
                                 axis.text.y = element_text(size=10),
                                 legend.position = "none")+
                                 scale_y_continuous(labels = seq(1,5,0.5),
                                                     breaks = seq(1000, 5000, by = 500))+
                                 scale_x_discrete(labels=c("Non-smoker", "Smoker"))


boxplot_smoking_2 = ggplot(data=dataset,
                           aes(x=Fumatrici,
                               y=Lunghezza,
                              fill=Fumatrici))+
                           geom_boxplot()+
                           labs(title="Length distribution",
                                x=" ",
                                y="Length (cm)")+
                           scale_fill_manual(values= c("0"="green", "1"="grey"), 
                                             labels = c("0"= "Non-smoker", "1"="Smoker"))+
                           theme_classic()+
                           theme(plot.title.position = "panel",
                                 plot.title = element_text(size=16, hjust=0.5),
                                 axis.title.x = element_text(size=14),
                                 axis.title.y = element_text(size=14),
                                 axis.text.x = element_text(size=14),
                                 axis.text.y = element_text(size=10),
                                 legend.position = "none")+
                                 scale_y_continuous(breaks = seq(300,600,50),
                                                    labels = seq(30, 60, 5))+
                                 scale_x_discrete(labels=c("Non-smoker", "Smoker"))


boxplot_smoking_3 = ggplot(data=dataset,
                           aes(x=Fumatrici,
                               y=Cranio,
                              fill=Fumatrici))+
                           geom_boxplot()+
                           labs(title="Cranial circumference distribution",
                                x=" ",
                                y="Cranial circumference (cm)")+
                           scale_fill_manual(values= c("0"="green", "1"="grey"), 
                                             labels = c("0"= "Non-smoker", "1"="Smoker"))+
                           theme_classic()+
                           theme(plot.title.position = "panel",
                                 plot.title = element_text(size=16, hjust=0.5),
                                 axis.title.x = element_text(size=14),
                                 axis.title.y = element_text(size=14),
                                 axis.text.x = element_text(size=14),
                                 axis.text.y = element_text(size=10),
                                 legend.position = "none")+
                                 scale_y_continuous(labels = seq(25,40,5),
                                                     breaks = seq(250, 400, by = 50))+
                                 scale_x_discrete(labels=c("Non-smoker", "Smoker"))



boxplot_smoking_4 = ggplot(data=dataset,
                           aes(x=Fumatrici,
                               y=Gestazione,
                              fill=Fumatrici))+
                           geom_boxplot()+
                           labs(title="Gestational time distribution",
                                x=" ",
                                y="Gestational time")+
                           scale_fill_manual(values= c("0"="green", "1"="grey"), 
                                             labels = c("0"= "Non-smoker", "1"="Smoker"))+
                           theme_classic()+
                           theme(plot.title.position = "panel",
                                 plot.title = element_text(size=16, hjust=0.5),
                                 axis.title.x = element_text(size=14),
                                 axis.title.y = element_text(size=14),
                                 axis.text.x = element_text(size=14),
                                 axis.text.y = element_text(size=10),
                                 legend.position = "none")+
                                 scale_y_continuous(breaks = seq(26, 42, by = 2))+
                                 scale_x_discrete(labels=c("Non-smoker", "Smoker"))

comb_smoke <- boxplot_smoking_1 + boxplot_smoking_2 + boxplot_smoking_3 + boxplot_smoking_4 +    plot_layout(ncol=2, heights = c(5,5), widths= c(5,5))

print(comb_smoke)

The boxplot may suggest a difference in the measurements and gestational time between smoking and non smoking mothers. We test this hypothesis using a t.test.

Smokers_data <- dataset %>%
  filter(Fumatrici=="1")

Not_smokers_data <- dataset %>%
  filter(Fumatrici=="0")

ttest_smoke_weight = t.test(Smokers_data$Peso, Not_smokers_data$Peso,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 

ttest_smoke_length = t.test(Smokers_data$Lunghezza, Not_smokers_data$Lunghezza,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 


ttest_smoke_cranium = t.test(Smokers_data$Cranio, Not_smokers_data$Cranio,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 

ttest_smoke_gestation = t.test(Smokers_data$Gestazione, Not_smokers_data$Gestazione,  
                 alternative = "two.sided", 
                 mu = 0, 
                 var.equal = TRUE,
                 conf.level = 0.95,
                 paired = FALSE) 

cat("The p-values of the t-tests about length, weight, cranial circumference and gestation time distribution comparison between smoking and non-smoking mothers are: \n", 
    "-p_value(Weight) = ", ttest_smoke_weight$p.value , "\n",
    "-p_value(Length) = ", ttest_smoke_length$p.value , "\n",
    "-p_value(Cranial diameter) = ", ttest_smoke_cranium$p.value, "\n",
    "-p_value(Cranial diameter) = ", ttest_smoke_gestation$p.value)
The p-values of the t-tests about length, weight, cranial circumference and gestation time distribution comparison between smoking and non-smoking mothers are: 
 -p_value(Weight) =  0.3428237 
 -p_value(Length) =  0.2984492 
 -p_value(Cranial diameter) =  0.6650278 
 -p_value(Cranial diameter) =  0.1064455

The t.tests do not allow us to reject the hypothesis that the gestational time and antropometric measurments of newborns are statistically different according to smoking and not-smoking mothers.

Next we analyze the number of deliveries and relation with the age of the mothers.

First we explore the variable number of deliveries.

Freq_abs_N.deliveries = table(dataset["N.gravidanze"]) 
Freq_rel_N.deliveries = table(dataset["N.gravidanze"])/N_observations

Freq_N.deliveries=data.frame(N.gravidanze = names(Freq_abs_N.deliveries),
                             Freq_abs = as.numeric(Freq_abs_N.deliveries),
                             Freq_rel = paste(round(Freq_rel_N.deliveries,2),"%"))

Freq_N.deliveries$N.gravidanze <- factor(Freq_N.deliveries$N.gravidanze, 
                                        levels=as.character(sort(as.numeric(unique(Freq_N.deliveries$N.gravidanze)))))


ggplot(data=Freq_N.deliveries)+
  geom_bar(aes(x=N.gravidanze, y=Freq_abs),
           stat="identity",
           col="aquamarine",
           fill="aquamarine")+
  labs(title="Number of previous pregnancies",
       x="",
       y="absolute frequency")+
  scale_y_continuous(breaks = seq(0,1200,200))+
  geom_text(aes(x=N.gravidanze, y=Freq_abs+50, label = Freq_rel))+
  theme_classic()+
  theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
        axis.title.y = element_text(size = 14, margin = margin(r = 10)))

We observe that most of deliveries correspond to first deliveries and that less than 10% of the mothers have 4 children or more.

Next we investigate if there is a correlation between the number of children and the age of the mother. To this end we split the dataset in classes according to the Number of deliveries and years of the mother and we display the dstribution according to the cross classes.

dataset$N.gravidanze_CL <- cut(dataset$N.gravidanze, 
                     breaks=c(0,1,2,3,100), 
                     right=FALSE, 
                     labels=c("0", "1","2","3+"))


dataset$Anni.madre_CL <- cut(dataset$Anni.madre, 
                     breaks=c(11,20,26,32,38,46))

dataset$N.gravidanze_CL <- cut(dataset$N.gravidanze, 
                     breaks=c(0,1,2,3,100), 
                     right=FALSE, 
                     labels=c("0","1","2","3+"))

cont_table_year_N.delivery = table(dataset$Anni.madre_CL, dataset$N.gravidanze_CL)

ggpubr::ggballoonplot(data=as.data.frame(cont_table_year_N.delivery),
                      fill="khaki")

As expected we observe that an increasing number of children usually correspond to older mothers. We test the hypothesis the the distribution of number of childer is not uniform across the ages via a Pearson chi-squared test.

chisq_test = chisq.test(cont_table_year_N.delivery)
cat("The p-value of the Pearson's Chi-squared test is" , chisq_test$p.value) 
The p-value of the Pearson's Chi-squared test is 1.116617e-91

The Pearson’s Chi squared test confirms the hypothesis that the distribution of number of deliveries per age is not uniform.

Creation of a regression model for the weight of the newborn.

We start studying the correlations between the different variables and the variable weight. First we display the correlations between the different quantitative variable and the variable weight.

# panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...)
# {
#   #usr <- par("usr"); on.exit(par(usr))
#   #par(usr = c(0, 1, 0, 1))
#   r <- (cor(x, y))
#   txt <- format(c(r, 1), digits = digits)[1]
#   txt <- paste0(prefix, txt)
#   if(missing(cex.cor)) cex.cor <- 0.8/strwidth(txt)
#   text(0.5, 0.5, txt, cex = 1.5)
# }

panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...)
{
  r <- cor(x, y, use = "complete.obs")
  txt <- format(c(r, 1), digits = digits)[1]
  txt <- paste0(prefix, txt)
  
  usr <- par("usr") # leggi coordinate correnti
  x_pos <- mean(usr[1:2])  # centro asse x
  y_pos <- mean(usr[3:4])  # centro asse y
  
  if(missing(cex.cor)) cex.cor <- 10/strwidth(txt)
  text(x_pos, y_pos, txt, cex = 1.5, ...)
}


#correlazioni
pairs(dataset[, c("Anni.madre","N.gravidanze", "Gestazione", "Peso", "Lunghezza", "Cranio")], lower.panel=panel.cor, upper.panel=panel.smooth)

The correlation matrix suggest a medium to high correlation between Weight and Length/Cranium circumference and gestation time. Differently, the weight seems not to be correlated to the number of deliveries and age of the mother. Additionally we have already observed that there is not a significant correlation between the weight of the newborn and the fact that the mother is a smoker. Differently we have observed a strong correlation between the sex of the newborn and its weight.

The scatter plots also suggest a possible nonlinear dependance of the weight of the newborns with respect to gestation time and possibly Length and cranium circumference.

Even if this is not observable from the correlation matrix, the literature suggest that there could be a difference in the weight of the newborns between the first child and the subsequent ones (see International standards for newborn weight, length, and head circumference by gestational age and sex), for this reason we split the dataset according to the Number of delivery classes “0” and “1+” and use this as a qualitative variable in the investigation of a good regression model fo the weight of the newborns.

dataset$N.gravidanze_CL <- cut(dataset$N.gravidanze, 
                     breaks=c(0,1,100), 
                     right=FALSE, 
                     labels=c("0","1+"))

We investiagte the best linear regression model using a stepwise selection procedure. We start from the full model from which we omit the variables Hospital and Kind of delivery since there is no reason why these variables should influence the weight of the newborn. Then we subtract or add back the variables that seems less influential. Finally we select the best model based on the BIC metrics.

mod_tot<-lm(Peso ~ Anni.madre + N.gravidanze_CL + Fumatrici + Gestazione + Lunghezza + Cranio + Sesso, data=dataset)
summary(mod_tot)

Call:
lm(formula = Peso ~ Anni.madre + N.gravidanze_CL + Fumatrici + 
    Gestazione + Lunghezza + Cranio + Sesso, data = dataset)

Residuals:
     Min       1Q   Median       3Q      Max 
-1163.53  -183.34   -14.47   164.63  2615.88 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -6721.5968   141.3193 -47.563  < 2e-16 ***
Anni.madre            1.2298     1.1238   1.094   0.2739    
N.gravidanze_CL1+    23.2268    11.8572   1.959   0.0502 .  
Fumatrici1          -30.5500    27.6434  -1.105   0.2692    
Gestazione           33.1338     3.8410   8.626  < 2e-16 ***
Lunghezza            10.2148     0.3011  33.930  < 2e-16 ***
Cranio               10.5152     0.4282  24.556  < 2e-16 ***
SessoM               78.5750    11.2142   7.007 3.13e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 274.8 on 2490 degrees of freedom
Multiple R-squared:  0.727, Adjusted R-squared:  0.7262 
F-statistic: 947.2 on 7 and 2490 DF,  p-value: < 2.2e-16

The variables “age”, “N.deliveries” and “smoking” seeem to be the less significant. So we start removing the variable “age” from the model.

mod1<-lm(Peso ~  N.gravidanze_CL + Fumatrici + Gestazione + Lunghezza + Cranio + Sesso, data=dataset)
summary(mod1)

Call:
lm(formula = Peso ~ N.gravidanze_CL + Fumatrici + Gestazione + 
    Lunghezza + Cranio + Sesso, data = dataset)

Residuals:
     Min       1Q   Median       3Q      Max 
-1148.91  -183.57   -14.79   163.87  2620.69 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -6679.3566   135.9513 -49.131  < 2e-16 ***
N.gravidanze_CL1+    27.2419    11.2757   2.416   0.0158 *  
Fumatrici1          -30.8461    27.6432  -1.116   0.2646    
Gestazione           32.7241     3.8229   8.560  < 2e-16 ***
Lunghezza            10.2109     0.3010  33.918  < 2e-16 ***
Cranio               10.5387     0.4277  24.640  < 2e-16 ***
SessoM               78.7767    11.2132   7.025 2.74e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 274.8 on 2491 degrees of freedom
Multiple R-squared:  0.7269,    Adjusted R-squared:  0.7262 
F-statistic:  1105 on 6 and 2491 DF,  p-value: < 2.2e-16

cat(“After removing the variable "age" the variables "N.deliveries" and "smoking" are still not significant.” ) cat(“So we remove the variable "smoking" from the model.” )

mod2<-lm(Peso ~ N.gravidanze_CL + Gestazione + Lunghezza + Cranio + Sesso, data=dataset)
summary(mod2)

Call:
lm(formula = Peso ~ N.gravidanze_CL + Gestazione + Lunghezza + 
    Cranio + Sesso, data = dataset)

Residuals:
     Min       1Q   Median       3Q      Max 
-1147.94  -184.03   -14.54   164.49  2624.44 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -6678.5707   135.9561 -49.123  < 2e-16 ***
N.gravidanze_CL1+    26.4198    11.2522   2.348    0.019 *  
Gestazione           32.4468     3.8150   8.505  < 2e-16 ***
Lunghezza            10.2257     0.3008  33.999  < 2e-16 ***
Cranio               10.5444     0.4277  24.654  < 2e-16 ***
SessoM               78.5900    11.2125   7.009 3.07e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 274.8 on 2492 degrees of freedom
Multiple R-squared:  0.7267,    Adjusted R-squared:  0.7262 
F-statistic:  1325 on 5 and 2492 DF,  p-value: < 2.2e-16

After removing the variable smoking the variables N.deliveries is still little significant. So we remove also this variable from the model.

mod3<-lm(Peso ~ Gestazione + Lunghezza + Cranio + Sesso, data=dataset)
summary(mod3)

Call:
lm(formula = Peso ~ Gestazione + Lunghezza + Cranio + Sesso, 
    data = dataset)

Residuals:
    Min      1Q  Median      3Q     Max 
-1138.1  -184.4   -17.4   163.3  2626.7 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -6651.6732   135.5952 -49.055  < 2e-16 ***
Gestazione     31.3262     3.7884   8.269  < 2e-16 ***
Lunghezza      10.2024     0.3009  33.909  < 2e-16 ***
Cranio         10.6706     0.4247  25.126  < 2e-16 ***
SessoM         79.1027    11.2205   7.050 2.31e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 275.1 on 2493 degrees of freedom
Multiple R-squared:  0.7261,    Adjusted R-squared:  0.7257 
F-statistic:  1652 on 4 and 2493 DF,  p-value: < 2.2e-16

In the final model all the remaining variables seem very significant so we don’t remove any further variable. We also note that the adjusted R-squared has remained unchanged after removing the variables “age” and “smoking” while has only slightly reduced when removing the variable “N.deliveries”. Next we test the different models that we have considered by looking at the analysis of variance between each model and the subsequent one and then we look at the BIC value of the four models.

kable(anova(mod_tot, mod1, mod2, mod3), caption="Analysis of Variance of each model with respect to the previous one") %>%
  kable_styling()
Analysis of Variance of each model with respect to the previous one
Res.Df RSS Df Sum of Sq F Pr(>F)
2490 188062157 NA NA NA NA
2491 188152606 -1 -90449.42 1.197578 0.2739122
2492 188246657 -1 -94050.27 1.245254 0.2645685
2493 188663107 -1 -416450.41 5.513930 0.0189435

The analysis of variance between each model and the subsequent one shows that every time we have removed a variable the residual sum of squares has reduced, however the reduction has been statistically significant only once that we have removed all the three variables.

kable(BIC(mod_tot, mod1, mod2, mod3), caption="Bayesian Information Criterion (BIC) of the four models considered") %>%
  kable_styling()
Bayesian Information Criterion (BIC) of the four models considered
df BIC
mod_tot 9 35209.56
mod1 8 35202.94
mod2 7 35196.36
mod3 6 35194.06

Including only linear terms in the variables we find that the best model, according to the BIC values, is the one that predicts the weight of the newborn based only on the variables Length, Cranial circumference, sex and gestation time, followed closely by the model that included also the dependence upon the number of deliveries.

Next we start from the best model that we found and consider if to include also nonlinear terms in the variables. In particular we note that the the weigth should be proportional to the volume of the body and that the volume likely depends on cubic terms in the variables length and cranial circumferece. Indeed if we think about the body as a cylinder or parallelepipedon we could write its volume in terms of \(length\times(cranial\_circumference)^2\) or \(length^2\times cranial\_circumference\).

Additionally the scatterplots suggest a possible nonlinear dependence of the weight on the gestational age. As a first step we study the correlation of some nonlinear terms between each other and with the variable weight.

dataset$Gestazione2 <- (dataset$Gestazione)^2
dataset$logGestazione <- log(dataset$Gestazione)

pairs(dataset[, c("Peso", "Gestazione", "Gestazione2",
                "logGestazione")], lower.panel=panel.cor, upper.panel=panel.smooth)


dataset$Lunghezza2 <- (dataset$Lunghezza)^2
dataset$Lunghezza2xCranio <- (dataset$Lunghezza)^2*(dataset$Cranio)
dataset$Cranio2 <- (dataset$Cranio)^2
dataset$LunghezzaxCranio2 <- dataset$Lunghezza*(dataset$Cranio)^2

pairs(dataset[, c("Peso","Lunghezza", "Lunghezza2", "Lunghezza2xCranio", 
                "Cranio", "Cranio2", "LunghezzaxCranio2")], lower.panel=panel.cor, upper.panel=panel.smooth)

Due to the realtively small range intervals of the variables we note that the correlation between all the variables Gestation time, Length and Cranial circumference with their quadratic version, as also between gestation time and its logarithm, is 1 up to a second error approximation. So it doesn’t make sense to include these terms in the model since we can not expect them to perform significantly better than the linear terms.

The situation is slightly different for the cubic terms mixed in length and cranial circumference. Indeed, even if they have high correlations with the variables length and cranial circumference they also have higher correlations with the variable weight with respect to the linear terms. So we try to include these terms in the model.

tot_non_lin_mod <- lm(Peso ~ N.gravidanze_CL + Gestazione + Lunghezza + Cranio + Lunghezza2xCranio + LunghezzaxCranio2 + Sesso, data=dataset)

nonlin_mod = MASS::stepAIC(tot_non_lin_mod, direction="both", k=log(N_observations))
Start:  AIC=28038.58
Peso ~ N.gravidanze_CL + Gestazione + Lunghezza + Cranio + Lunghezza2xCranio + 
    LunghezzaxCranio2 + Sesso

                    Df Sum of Sq       RSS   AIC
<none>                           182562021 28039
- N.gravidanze_CL    1    604520 183166541 28039
- Lunghezza          1    978930 183540951 28044
- LunghezzaxCranio2  1   1609628 184171649 28053
- Cranio             1   1946141 184508162 28057
- Sesso              1   3096848 185658869 28073
- Lunghezza2xCranio  1   3611489 186173510 28080
- Gestazione         1   7719667 190281688 28134
summary(nonlin_mod)

Call:
lm(formula = Peso ~ N.gravidanze_CL + Gestazione + Lunghezza + 
    Cranio + Lunghezza2xCranio + LunghezzaxCranio2 + Sesso, data = dataset)

Residuals:
     Min       1Q   Median       3Q      Max 
-1177.37  -182.96   -11.36   163.90  1860.40 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -3.618e+03  7.682e+02  -4.709 2.62e-06 ***
N.gravidanze_CL1+  3.190e+01  1.111e+01   2.871 0.004121 ** 
Gestazione         4.063e+01  3.960e+00  10.261  < 2e-16 ***
Lunghezza         -7.825e+00  2.142e+00  -3.654 0.000264 ***
Cranio             2.131e+01  4.136e+00   5.152 2.78e-07 ***
Lunghezza2xCranio  8.887e-05  1.266e-05   7.018 2.88e-12 ***
LunghezzaxCranio2 -9.650e-05  2.060e-05  -4.686 2.94e-06 ***
SessoM             7.207e+01  1.109e+01   6.499 9.73e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 270.8 on 2490 degrees of freedom
Multiple R-squared:  0.735, Adjusted R-squared:  0.7342 
F-statistic: 986.5 on 7 and 2490 DF,  p-value: < 2.2e-16
kable(BIC(mod3, nonlin_mod), caption="Bayesian Information Criterion (BIC) of the linear model vs the nonlinear one") %>%
  kable_styling()
Bayesian Information Criterion (BIC) of the linear model vs the nonlinear one
df BIC
mod3 6 35194.06
nonlin_mod 9 35135.41
NA

The model selected by the stepwise method by taking into account for all the possible nonlinear terms that we have previously considered as a Bayesian Information Criterion (BIC) value significantly smaller than the best model that is linear in all the variables.

Next we compute the variance inflation factors of the selected model.


kable(car::vif(nonlin_mod), caption="Variance Inflation factors of the nonlinear model") %>%
  kable_styling()
Variance Inflation factors of the nonlinear model
x
N.gravidanze_CL 1.035160
Gestazione 1.865538
Lunghezza 108.279420
Cranio 157.281246
Lunghezza2xCranio 666.841683
LunghezzaxCranio2 790.238306
Sesso 1.047540
NA

As expected the model has extremely high variance inflaction factor due to the high correlation coefficients between the variables. Since the high variance inflation factors make the coefficient estimates unstable and unreliable, we prefer to discard the last model.

However we have learnt that the inclusion of nonlinear terms can improve the model. So, next, we try to update the last model by removing variables that are highly correlated to see if we can get a model that is more accurate than the linear model mod3 without having high variance inflation factors. In particular we highlight that a model expressing the weight linearly with respect to a volume would be physically much more meaningfull than one espressing the weight linearly with respect to lengths. For these reasons we opt for removing from the model the two linear variables “length” and “cranial_circumference”

nonlin_mod2 <- update(nonlin_mod, . ~ . - Lunghezza - Cranio)

summary(nonlin_mod2)

Call:
lm(formula = Peso ~ N.gravidanze_CL + Gestazione + Lunghezza2xCranio + 
    LunghezzaxCranio2 + Sesso, data = dataset)

Residuals:
     Min       1Q   Median       3Q      Max 
-1157.23  -183.06   -14.76   163.04  2610.90 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -1.408e+03  1.214e+02 -11.598  < 2e-16 ***
N.gravidanze_CL1+  3.268e+01  1.114e+01   2.934  0.00338 ** 
Gestazione         4.202e+01  3.660e+00  11.480  < 2e-16 ***
Lunghezza2xCranio  2.818e-05  1.560e-06  18.069  < 2e-16 ***
LunghezzaxCranio2  1.118e-05  2.228e-06   5.017 5.62e-07 ***
SessoM             7.099e+01  1.112e+01   6.383 2.06e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 272.2 on 2492 degrees of freedom
Multiple R-squared:  0.732, Adjusted R-squared:  0.7315 
F-statistic:  1361 on 5 and 2492 DF,  p-value: < 2.2e-16
kable(car::vif(nonlin_mod2), caption="Variance Inflation factors of the nonlinear model") %>%
  kable_styling()
Variance Inflation factors of the nonlinear model
x
N.gravidanze_CL 1.029988
Gestazione 1.577089
Lunghezza2xCranio 10.012010
LunghezzaxCranio2 9.153736
Sesso 1.042580

After removing the variables length and cranial circumference the variance inflation factor is still high for the two cubic variables. For this reason we remove from the model also the variable \(Lunghezza\times Cranio^2\) which results the less influential.

nonlin_mod3 <- update(nonlin_mod2, . ~ . - LunghezzaxCranio2)

summary(nonlin_mod3)

Call:
lm(formula = Peso ~ N.gravidanze_CL + Gestazione + Lunghezza2xCranio + 
    Sesso, data = dataset)

Residuals:
     Min       1Q   Median       3Q      Max 
-1178.36  -179.61   -14.04   161.78  2790.87 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -1.325e+03  1.209e+02 -10.964  < 2e-16 ***
N.gravidanze_CL1+  3.886e+01  1.112e+01   3.494 0.000485 ***
Gestazione         4.088e+01  3.671e+00  11.139  < 2e-16 ***
Lunghezza2xCranio  3.536e-05  6.238e-07  56.679  < 2e-16 ***
SessoM             7.028e+01  1.117e+01   6.290 3.73e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 273.5 on 2493 degrees of freedom
Multiple R-squared:  0.7293,    Adjusted R-squared:  0.7289 
F-statistic:  1679 on 4 and 2493 DF,  p-value: < 2.2e-16
kable(car::vif(nonlin_mod3), caption="Variance Inflation factors of the nonlinear model") %>%
  kable_styling()
Variance Inflation factors of the nonlinear model
x
N.gravidanze_CL 1.017382
Gestazione 1.571103
Lunghezza2xCranio 1.586448
Sesso 1.042414

kable(BIC(mod2, nonlin_mod, nonlin_mod2, nonlin_mod3), caption="Bayesian information factor of the linear mode vs nonlinear ones") %>%
  kable_styling()
Bayesian information factor of the linear mode vs nonlinear ones
df BIC
mod2 7 35196.36
nonlin_mod 9 35135.41
nonlin_mod2 7 35147.43
nonlin_mod3 6 35164.71
NA
NA

The final model predicts the weight of the new born based on gestation time, squared length times cranium circumference, sex and number of deliveries. The model has a BIC value larger than the best model which included all the possible nonlinear terms but smaller than the best model that included only linear terms in the variables. Moreover, differently from the best model that included all the possible nonlinear terms, the model “nonlin_mod3” has all the variance inflation factors smaller than 5.

We point out that the model “nonlin_mod3”, including the mixed term \(length^2\times cranial_circumferece\) but not the linear terms in the variables \(length\) ad \(cranial_circumferece\), may break the principle of marginality. However we highlight that the model is also physically more meaningfull that one including the linear terms in the variables \(length\) and \(cranial_circumference\). For these reasons we accept this model and analyze it. We start analyzing its coefficients

kable(coef(nonlin_mod3), caption="Coefficients of final nonlinear model") %>%
  kable_styling()
Coefficients of final nonlinear model
x
(Intercept) -1325.4187855
N.gravidanze_CL1+ 38.8606976
Gestazione 40.8848655
Lunghezza2xCranio 0.0000354
SessoM 70.2824691

The coefficients say that for each unit increment in the gestation time (1 week) we observe an increment of approximately 40gr in the weigth of the newborn. Similarly for each 1mm^3 increment in the volume term (\(Length^2 \times Cranial_circfumference\)) we register an increment of \(3.5*10^-5\) (gr) in the weight of the newborn. Finally we observe that the males will register approximately 7gr more than females and child after the first one weight approximately 4gr more than a first child.

Next we analyze the residuals, outliers and leverages of the model.

par(mfrow=c(2,2))
plot(nonlin_mod3)


lmtest::bptest(nonlin_mod3)

    studentized Breusch-Pagan test

data:  nonlin_mod3
BP = 13.12, df = 4, p-value = 0.01071
lmtest::dwtest(nonlin_mod3)

    Durbin-Watson test

data:  nonlin_mod3
DW = 1.9523, p-value = 0.1163
alternative hypothesis: true autocorrelation is greater than 0
shapiro.test(nonlin_mod3$residuals)

    Shapiro-Wilk normality test

data:  nonlin_mod3$residuals
W = 0.97047, p-value < 2.2e-16

The analysis shows that there is no autocorrelation of the residuals (p-value of the Durbin-Watson test > 0.05). However we observe that the residuals are not normally distributed (p-value of the Shapiro-Wilk test <0.05) and that there is no heteroscedasticity (p-value of the Breusch-Pagan < 0.05). Additionally we observe that there are some highly influential outlier datapoints having Cook distance close to 0.5.

So we investigate deeper the leverages and outliers. We consider leverages the points having leverage value larger than the threshold. This is set equal to 3 times the leverage mean, i.e. the number of parameters of the model over the number of observations in the dataset. Moreover the outliers are identified by a Bonferroni test on the studentized residuals of the model.


#leverage
lev<-hatvalues(nonlin_mod3)
plot(lev)
p<-sum(lev)
n<-length(lev)
soglia=3*p/n
abline(h=soglia,col=2)
title(main = "Leverage values of model mod1")

leverages=which(lev>soglia)
cat("There are", length(leverages), "leverages\n")
There are 46 leverages
#outliers
plot(rstudent(nonlin_mod3))
abline(h=c(-2,2))
title(main = "Studentized Residuals of model mod1")



cat("The outliers are:\n")
The outliers are:
car::outlierTest(nonlin_mod3)


#distanza di cook
cook<-cooks.distance(nonlin_mod3)
plot(cook,ylim = c(0,1)) 
title(main = "Cook distance of residuals of model mod1")

We note that the most influential outlier, the datapoint 1549, with a Cook distance around 0.2, could have a relevant effect on the estimate of the coefficients of the model but not drastical. By investigating this datapoint we find that this is the same datapoint that we have already met before which has a length much lower than expected, probably for a typo in its transposition.

kable(t(dataset[1549,]), caption="Outlier datapoint") %>%
  kable_styling()
Outlier datapoint
1549
Anni.madre 35
N.gravidanze 1
Fumatrici 0
Gestazione 38
Peso 4370
Lunghezza 315
Cranio 374
Tipo.parto Nat
Ospedale osp3
Sesso F
N.gravidanze_CL 1+
Anni.madre_CL (32,38]
Gestazione2 1444
logGestazione 3.637586
Lunghezza2 99225
Lunghezza2xCranio 37110150
Cranio2 139876
LunghezzaxCranio2 44060940
NA

Next we evaluate the quality of the model in terms of the \(R^2\) and RMSE metrics.

# R^2 :
summary_mod <- summary(nonlin_mod3)
R2 <- summary_mod$r.squared
cat("The R^2 value of the model is equal to", R2, ", so the model is explaining about 73% of the variance of the variable Weight. \n")
The R^2 value of the model is equal to 0.7293134 , so the model is explaining about 73% of the variance of the variable Weight. 
# RMSE:
predictions <- predict(nonlin_mod3, newdata = dataset)
residuals <- dataset$Peso - predictions
RMSE <- sqrt(mean(residuals^2))
perRMSE=RMSE/mean(dataset$Peso)
cat("The RMSE value of the model is equal to", RMSE, ".\n") 
The RMSE value of the model is equal to 273.2093 .
cat("This means that the model has a mean error of prediction of" , paste0(round(perRMSE,2),"%"),"with respect to the mean of the variable weight.\n")
This means that the model has a mean error of prediction of 0.08% with respect to the mean of the variable weight.
cat("Moreover this means that the variance of the prediction errors of the model is about", round(RMSE/sd(dataset$Peso),2), "of the variance of the variable weight")
Moreover this means that the variance of the prediction errors of the model is about 0.52 of the variance of the variable weight

Using the model to make predictions

Assume to have a female baby that will be delivered at the 39th week and whose mother has already delivered two children. Then we can predict her weight. For the new baby we assume a length and cranial circumference equal to the mean weight and cranial circumferene of female newborns at the 39th gestation week.

length= mean(subset(dataset, Sesso == "F" & Gestazione==39)$Lunghezza)
cranium= mean(subset(dataset, Sesso == "F" & Gestazione==39)$Cranio)

new_data <- data.frame("N.gravidanze" = c(2),
                       "Gestazione"=c(39),
                       "Lunghezza"=c(length),
                       "Cranio"=c(cranium),
                       "Sesso"=c("F")
                       )

new_data$Lunghezza2xCranio = (new_data$Lunghezza)^2*(new_data$Cranio)
new_data$Cranio2 <- (new_data$Cranio)^2
new_data$N.gravidanze_CL <- cut(new_data$N.gravidanze, 
                     breaks=c(0,1,100), 
                     right=FALSE, 
                     labels=c("0","1+"))


weight_prediction <- round(predict(nonlin_mod3, newdata = new_data))


cat("The regression model predicts a weight equal to", paste0(weight_prediction,"gr"), ".\n") 
The regression model predicts a weight equal to 3218gr .

Finally we display the relations captured in the model.

First we display how the weight is distributed according to the sex and number of deliveries with respect to the quantitative variables in the model, i.e. \(gestation\_time\) and \((length)^2\times cranial\_circumference\).


plot_fin_1 = ggplot(data=dataset)+
                    geom_point(aes(x=Gestazione,
                                 y=Peso,
                                 col=Sesso),position = "jitter")+
                    geom_smooth(aes(x=Gestazione,
                                    y=Peso,
                                    col=Sesso), se=F, method = "lm")+
                   scale_color_manual(values= sex_colors, 
                                   labels = c("M"= "Male", "F"="Female"))+
                   labs(title = "Weight as a function of Gestation time",
                        x = "gestation time",
                        y = "weight (gr)")+
                    theme_classic()+
                    theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                          axis.title.y = element_text(size = 14, margin = margin(r = 10)),
                          legend.title = element_text(size = 14),
                          legend.text = element_text(size = 14))


legend <- as_ggplot(get_legend(plot_fin_1))

plot_fin_1 = ggplot(data=dataset)+
                    geom_point(aes(x=Gestazione,
                                 y=Peso,
                                 col=Sesso),position = "jitter")+
                    geom_smooth(aes(x=Gestazione,
                                    y=Peso,
                                    col=Sesso), se=F, method = "lm")+
                   scale_color_manual(values= sex_colors, 
                                   labels = c("M"= "Male", "F"="Female"))+
                   labs(title = "Weight as a function of Gestation time",
                        x = "gestation time",
                        y = "weight (gr)")+
                    theme_classic()+
                    theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                          axis.title.y = element_text(size = 14, margin = margin(r = 10)),
                          legend.position = "none")


 plot_fin_3 = ggplot(data=dataset)+
                    geom_point(aes(x=Lunghezza2xCranio,
                                 y=Peso,
                                 col=Sesso))+
                    geom_smooth(aes(x=Lunghezza2xCranio,
                                    y=Peso,
                                    col=Sesso), se=F, method = "lm")+
                   scale_color_manual(values= sex_colors, 
                                   labels = c("M"= "Male", "F"="Female"))+
                    labs(title = "Weight as a function of \n Lengt^2 x cranial circumference",
                        x = "Lengt^2 x cranial circumference (mm^3)",
                        y = "weight (gr)")+
                    theme_classic()+
                    theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                          axis.title.y = element_text(size = 14, margin = margin(r = 10)),
                          legend.position = "none")

  
  
comb_fin <- plot_fin_1 + plot_fin_3 + legend + plot_layout(ncol=3, heights = c(5), widths= c(5,5,1.5))

print(comb_fin)


plot_fin_1 = ggplot(data=dataset)+
                    geom_point(aes(x=Gestazione,
                                 y=Peso,
                                 col=N.gravidanze_CL),position = "jitter")+
                    geom_smooth(aes(x=Gestazione,
                                    y=Peso,
                                    col=N.gravidanze_CL), se=F, method = "lm")+
                   labs(title = "Weight as a function of Gestation time",
                        x = "gestation time",
                        y = "weight (gr)",
                        color = "N° previous deliveries")+
                    theme_classic()+
                    theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                          axis.title.y = element_text(size = 14, margin = margin(r = 10)),
                          legend.title = element_text(size = 14),
                          legend.text = element_text(size = 14))


legend <- as_ggplot(get_legend(plot_fin_1))

plot_fin_1 = ggplot(data=dataset)+
                    geom_point(aes(x=Gestazione,
                                 y=Peso,
                                 col=N.gravidanze_CL),position = "jitter")+
                    geom_smooth(aes(x=Gestazione,
                                    y=Peso,
                                    col=N.gravidanze_CL), se=F, method = "lm")+
                   labs(title = "Weight as a function of Gestation time",
                        x = "gestation time",
                        y = "weight (gr)")+
                    theme_classic()+
                    theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                          axis.title.y = element_text(size = 14, margin = margin(r = 10)),
                          legend.position = "none")

  
 plot_fin_3 = ggplot(data=dataset)+
                    geom_point(aes(x=Lunghezza2xCranio,
                                 y=Peso,
                                 col=N.gravidanze_CL))+
                    geom_smooth(aes(x=Lunghezza2xCranio,
                                    y=Peso,
                                    col=N.gravidanze_CL), se=F, method = "lm")+
                    labs(title = "Weight as a function of \n Lengt^2 x cranial circumference",
                        x = "Lengt^2 x cranial circumference (mm^3)",
                        y = "weight (gr)")+
                    theme_classic()+
                    theme(axis.title.x = element_text(size = 14, margin = margin(t = 10)),
                          axis.title.y = element_text(size = 14, margin = margin(r = 10)),
                          legend.position = "none")

  
  
comb_fin <- plot_fin_1 + plot_fin_3 + legend + plot_layout(ncol=3, heights = c(5), widths= c(5,5,1.5))

print(comb_fin)

The plots confirm that the weight increases while increasing all the other variables in the model. Moreover we observe that the weight observed in males is always higher than the weight observed in females. Differently the difference in weight between first children and not first children is only observable when varying the variable gestation time. In this case we observe that the weight of first children is slightly smaller than the weight of not first children. The observations are in agreements with the discussion about the \(\beta\) coefficients of the model.

LS0tDQp0aXRsZTogIlN0YXRpc3RpY2FsX01vZGVsX05ld2Jvcm5fd2VpZ2h0Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KU3RhdGlzdGljYWwgTW9kZWwgZm9yIFByZWRpY3RpbmcgTmV3Ym9ybiBXZWlnaHQNCg0KQ29tcGFueSBDb250ZXh0IENvbXBhbnk6IE5lb25hdGFsIEhlYWx0aCBTb2x1dGlvbnMgDQoNCk9iamVjdGl2ZTogQ3JlYXRlIGEgc3RhdGlzdGljYWwgbW9kZWwgY2FwYWJsZSBvZiBhY2N1cmF0ZWx5IHByZWRpY3RpbmcgbmV3Ym9ybg0KYmlydGggd2VpZ2h0LCBiYXNlZCBvbiBjbGluaWNhbCB2YXJpYWJsZXMgY29sbGVjdGVkIGZyb20gdGhyZWUNCmhvc3BpdGFscy4gICANCg0KVGhlIHByb2plY3QgYWltcyB0byBpbXByb3ZlIHRoZSBtYW5hZ2VtZW50IG9mIGhpZ2gtcmlzayBwcmVnbmFuY2llcywgb3B0aW1pemluZyBob3NwaXRhbCByZXNvdXJjZXMgYW5kIGVuc3VyaW5nIGJldHRlciBvdXRjb21lcyBmb3IgbmVvbmF0YWwgaGVhbHRoLiAgIA0KDQpUaGUgdmFyaWFibGVzIGNvbGxlY3RlZCBpbmNsdWRlOiAgIA0KDQpNYXRlcm5hbCBBZ2U6IE1lYXN1cmUgb2YgYWdlIGluIHllYXJzLiAgIA0KDQotIE51bWJlciBvZiBQcmVnbmFuY2llczogSG93IG1hbnkgcHJlZ25hbmNpZXMgdGhlIG1vdGhlciBoYXMgcHJldmlvdXNseSBoYWQuICAgDQoNCi0gTWF0ZXJuYWwgc21va2luZzogQSBiaW5hcnkgaW5kaWNhdG9yICgwID0gbm9uLXNtb2tlciwgMSA9IHNtb2tlcikuICAgDQoNCi0gUHJlZ25hbmN5IGR1cmF0aW9uOiBOdW1iZXIgb2Ygd2Vla3Mgb2YgZ2VzdGF0aW9uLiAgIA0KDQotIEluZmFudCB3ZWlnaHQ6IEJpcnRoIHdlaWdodCBpbiBncmFtcy4gICANCg0KLSBMZW5ndGggYW5kIEhlYWQgY2lyY3VtZmVyZW5jZTogSW5mYW50IGxlbmd0aCBhbmQNCmhlYWQgZGlhbWV0ZXIsIHdoaWNoIGNhbiBhbHNvIGJlIG1lYXN1cmVkIGR1cmluZyBwcmVnbmFuY3kgdmlhDQp1bHRyYXNvdW5kLiAgIA0KDQotIFR5cGUgb2YgZGVsaXZlcnk6IE5hdHVyYWwgb3IgY2VzYXJlYW4uICAgDQoNCi0gSG9zcGl0YWwgb2YgYmlydGg6IEhvc3BpdGFsIDEsIDIsIG9yIDMuICAgDQoNCi0gSW5mYW50IHNleDogTWFsZSAoTSkgb3IgZmVtYWxlIChGKS4gICANCg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShtb21lbnRzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoZ2dwdWJyKQ0Kb3B0aW9ucyh3aWR0aCA9IDEwMCkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KGNhcikNCmxpYnJhcnkoZ2dwdWJyKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBJTVBPUlQgVEhFIERBVEFTRVQNCmRhdGFzZXQgPC0gcmVhZC5jc3YoIm5lb25hdGkuY3N2IikNCg0KTl9vYnNlcnZhdGlvbnMgPSBkaW0oZGF0YXNldClbMV0NCk5fdmFyaWFibGVzID0gZGltKGRhdGFzZXQpWzJdDQpkYXRhc2V0JEZ1bWF0cmljaSA8LSBmYWN0b3IoZGF0YXNldCRGdW1hdHJpY2kpDQoNCmF0dGFjaChkYXRhc2V0KQ0KYGBgDQoNCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KGRhdGFzZXQpLCBjYXB0aW9uPSJTdW1tYXJ5IG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQiKSAlPiUNCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KV2Ugbm90ZSB0aGF0IHRoZSB2YXJpYWJsZSBBZ2Ugb2YgdGhlIG1vdGhlciBoYXMgYSBtaW5pbXVtIHZhbHVlIGVxdWFsIHRvIDAgbWVhbmluZyB0aGF0IHRoZXJlIGFyZSBzb21lIHdyb25nIGRhdGEuIFNvIHdlIGZpcnN0IGRpc3BsYXkgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgYWdlIG9mIHRoZSBtb3RoZXJzLiANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHN1YnNldChkYXRhc2V0KSkrDQogIGdlb21fYmFyKGFlcyh4PUFubmkubWFkcmUpLA0KICAgICAgICAgICBzdGF0PSJjb3VudCIsDQogICAgICAgICAgIGNvbD0ia2hha2kiLA0KICAgICAgICAgICBmaWxsPSJraGFraSIpKw0KICBsYWJzKHRpdGxlPSJEaXN0cmlidXRpb24gb2YgdGhlIG1vdGhlciBhZ2UiLA0KICAgICAgIHg9IkFnZSIsDQogICAgICAgeT0iYWJzb2x1dGUgZnJlcXVlbmN5IikrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAwLDIwMCkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsNDYsMikpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbih0ID0gMTApKSwNCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHIgPSAxMCkpKQ0KYGBgDQpXZSBjYW4gYXNzdW1lIHRoYXQgdGhlIG1pbmltdW0gbWVhbmluZ2Z1bGwgYWdlIG9mIGEgbW90aGVyIGlzIDEyIHllYXJzLiBTbyB3ZSBmaWx0ZXIgdGhlIGRhdGFzZXQgdG8gcmVtb3ZlIHRoZSBkaXJ0eSBkYXRhIGNvcnJlc3BvbmRpbmcgdG8gbW90aGVycyB3aXRoIGFuIGFnZSBzbWFsbGVyIHRoYW4gMTIuIA0KDQpgYGB7cn0NCmRhdGFzZXQgPC0gZGF0YXNldCAlPiUgDQogIGZpbHRlcihBbm5pLm1hZHJlID49MTIpDQpgYGANCg0KDQoNCiMjIE5leHQsIHdlIHN0YXJ0IGFuYWx5emluZyB0aGUgYW50aHJvcG9tZXRyaWNzIGRhdGEgKFdlaWd0aCwgTGVuZ3RoIGFuZCBDcmFuaWFsIENpcmN1bWZlcmVuY2UpIG9mIHRoZSBuZXdib3JucyBpbiB0aGUgZGF0YXNldC4gDQojIyMgRmlyc3Qgd2UgcmVwb3J0IGEgdGFibGUgd2l0aCB0aGUgc3VtbWFyeSBvZiB0aGUgc3RhdGlzdGljcyBvZiB0aGUgYW50aHJvcG9tZXRyaWNzIHZhcmlhYmxlcw0KDQpgYGB7ciBwcmVzc3VyZSwgZWNobz1GQUxTRX0NCmN2X2Z1bmN0IDwtIGZ1bmN0aW9uKHgpIHsNCiAgcmV0dXJuKHNkKHgpIC8gbWVhbih4KSAqIDEwMCkNCn0NCg0Ka3VydG9zaXNfaW5kZXggPC0gZnVuY3Rpb24oeCkgew0KICByZXR1cm4oa3VydG9zaXMoeCktMykNCn0NCg0KDQpzdGQuZXJyIDwtIGZ1bmN0aW9uKHgpew0KICByZXR1cm4gKHNkKHgpL3NxcnQoc3VtKCFpcy5uYSh4KSkpKQ0KfQ0KDQoNCnN1Yl9kYXRhIDwtIHN1YnNldChkYXRhc2V0W2MoIkNyYW5pbyIsIlBlc28iLCJMdW5naGV6emEiKV0pIywgIEdlc3RhemlvbmU+PTM3KQ0KDQpzdGF0aXN0aWNzIDwtIGRhdGEuZnJhbWUoDQogICAgICAgIA0KICBxdWFydGlsZV8xID0gcm91bmQoYXBwbHkoc3ViX2RhdGEsIDIsIGZ1bmN0aW9uKHgpIHF1YW50aWxlKHgscHJvYnM9MC4yNSwgbmFtZXM9RkFMU0UpKSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIG1lZGlhbiA9IHJvdW5kKGFwcGx5KHN1Yl9kYXRhLCAyLCBtZWRpYW4pLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBxdWFydGlsZV8zID0gcm91bmQoYXBwbHkoc3ViX2RhdGEsIDIsIGZ1bmN0aW9uKHgpIHF1YW50aWxlKHgscHJvYnM9MC43NSwgbmFtZXM9RkFMU0UpKSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIG1lYW4gPSByb3VuZChhcHBseShzdWJfZGF0YSwgMiwgbWVhbiksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBzZCA9IHJvdW5kKGFwcGx5KHN1Yl9kYXRhLCAyLCBzZCksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBjdiA9IHJvdW5kKGFwcGx5KHN1Yl9kYXRhLCAyLCBjdl9mdW5jdCksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBza2V3bmVzcyA9IHJvdW5kKGFwcGx5KHN1Yl9kYXRhLCAyLCBza2V3bmVzcyksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBrdXJ0b3NpcyA9IHJvdW5kKGFwcGx5KHN1Yl9kYXRhLCAyLCBrdXJ0b3Npc19pbmRleCksMiksDQogIA0KICBzdGRfZXJyID0gcm91bmQoYXBwbHkoc3ViX2RhdGEsMixzdGQuZXJyKSwyKQ0KKQ0KDQoNCnJvd25hbWVzKHN0YXRpc3RpY3MpIDwtIGMoIkNyYW5pYWwgY2lyY3VtZmVyZW5jZSAobW0pIiwgIldlaWdodCAoZ3IpIiwgIkxlbmd0aCAobW0pIikNCg0Ka2FibGUoc3RhdGlzdGljcywgY2FwdGlvbj0iSW5kaWNlcyBvZiB3ZWlnaHQsIGxlbmd0aCBhbmQgY3JhbmlhbCBkaWFtZXRlciIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQoNCiMjIyBTZWNvbmQgd2UgZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBhbnRocm9wb21ldHJpYyB2YXJpYWJsZXMgYWdhaW4gYSBub3JtYWwgZGlzdHJpYnV0aW9uDQoNCmBgYHtyfQ0KIyB0aGUgZnVuY3Rpb24gbm9ybWFsaXplIHRha2VzIGluIGlucHV0IGEgdmFyaWFibGUgaW4gc3RyaW5nIGZvcm0gYW5kIHJldHVybnMgaXRzIHZhbHVlcyBub3JtYWxpemVkIGJ5IGl0cyBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24NCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KXsNCiAgcmV0dXJuICgoeC1tZWFuKHgpKS9zZCh4KSkNCn0NCg0KIyB3ZSBnZW5lcmF0ZSBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24NCmdhdXNzaWFuX2Rpc3RyaWJ1dGlvbiA8LSBybm9ybSgxMDAwMDAwLDAsMSkNCg0KDQojIHdlIHBsb3QgdGhlIGRlbnNpdHkgb2YgdGhlIGRpZmZlcmVudCB2YXJpYWJsZXMgYWdhaW5zdCBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24NCmdncGxvdCgpKw0KICBnZW9tX2RlbnNpdHkoYWVzKHg9Z2F1c3NpYW5fZGlzdHJpYnV0aW9uLCBmaWxsPSJOMDEiKSwgY29sb3I9TkEsICBhbHBoYT0uOCkrDQogIGdlb21fZGVuc2l0eShhZXMoeD1ub3JtYWxpemUoUGVzbyksIGNvbG9yPSJXZWlnaHQiKSwgbGluZXdpZHRoPTEuNSkrDQogIGdlb21fZGVuc2l0eShhZXMoeD1ub3JtYWxpemUoQ3JhbmlvKSwgY29sb3I9IkNyYW5pYWwgY2lyY3VtZmVyZW5jZSIpLCBsaW5ld2lkdGg9MS41KSsNCiAgZ2VvbV9kZW5zaXR5KGFlcyh4PW5vcm1hbGl6ZShMdW5naGV6emEpLCBjb2xvcj0iTGVuZ3RoIiksIGxpbmV3aWR0aD0xLjUpKw0KICBsYWJzKHRpdGxlPSJEaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxlcyIsDQogICAgICAgeD0iTm9ybWFsaXplZCB2YXJpYWJsZSAiLA0KICAgICAgIHk9ImRlbnNpdHkiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhOMDEgPSAiZ3JleSIpLCAgIA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKE4wMSA9ICJOKDAsMSkiKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGFuZWwiLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUpKSsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvciA9IE5BKSkpKw0KICBsYWJzKGNvbG9yID0gIlZhcmlhYmlsZSIsDQogICAgICBmaWxsID0gIkRpc3RyaWJ1emlvbmUgbm9ybWFsZSIpDQpgYGANCg0KVGhlIHBsb3RzIGFzIGFsc28gdGhlIHZhbHVlcyBvZiBza2V3ZW5lc3MgYW5kIEt1cnRvc2lzIHNob3cgdGhhdDogICANCg0KLSBBbGwgdGhlIGFudGhyb3BvbWV0cmljIHZhcmlhYmxlcyBhcmUgbW9yZSBwZWFrZWQgdGhhbiBhIG5vcm1hbCBkaXN0cmlidXRpb24sIHNvIHRoZSBwcm9iYWJpbGl0eSBvZiBvdXRsaWVycyBpcyBzbWFsbGVyIHRoYXQgZm9yIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggc2FtZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24uICAgDQoNCi0gVmFsdWVzIGluIHRoZSBsZWZ0IHRhaWxzIG9mIHRoZSBkaXN0cmlidXRpb25zIGFyZSBtb3JlIGxpa2VseSB0aGFuIHZhbHVlcyBpbiB0aGUgcmlnaHQgdGFpbC4gVGhpcyBjb3VsZCBiZSBkdWUgdG8gdGhlIGRhdGEgcmVsYXRpdmUgdG8gcHJlbWF0dXJlIGJpcnRocy4gICANCg0KDQpUbyBjaGVjayBpZiB0aGUgYXN5bW1ldHJ5IGlzIGR1ZSB0byB0aGUgcHJlbWF0dXJlIGJpcnRocyB3ZSBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgYmlydGhzIGJ5IGdlc3RhdGlvbmFsIHdlZWtzLiAgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1kYXRhc2V0KSsNCiAgZ2VvbV9iYXIoYWVzKHg9R2VzdGF6aW9uZSksDQogICAgICAgICAgIHN0YXQ9ImNvdW50IiwNCiAgICAgICAgICAgY29sPSJwaW5rIiwNCiAgICAgICAgICAgZmlsbD0icGluayIpKw0KICBsYWJzKHRpdGxlPSJEaXN0cmlidXRpb24gb2YgZ2VzdGF0aW9uYWwgYWdlIGF0IGNoaWxkYmlydGgiLA0KICAgICAgIHg9Imdlc3RhdGlvbmFsIGFnZSAod2Vla3MpIiwNCiAgICAgICB5PSJhYnNvbHV0ZSBmcmVxdWVuY3kiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDc1MCw1MCkpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDI1LDQzLDEpKSsNCiAgdGhlbWVfY2xhc3NpYygpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE1LCBoanVzdCA9IDEpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSkNCmBgYA0KDQoNCkEgZGVsaXZlcnkgaXMgY29uc2lkZXJlZCBwcmVtYXR1cmUgaWYgaXQgb2NjdXJzIGJlZm9yZSAzNyB3ZWVrcyBvZiBnZXN0YXRpb24uIEJ5IGxvb2tpbmcgYXQgdGhlIGJhcnBsb3Qgd2Ugbm90ZSB0aGVyZSBleGlzdCBkaWZmZXJlbnQgbWFueSBwcmVtYXR1cmUgYmlydGhkcyBpbiB0aGUgZGF0YXNldC4gV2UgZXhwZWN0IHRoZSBhbnRocm9wb21ldHJpYyBtZWFzdXJlbWVudHMgb2YgcHJlbWF0dXJlIG5ld2Jvcm5zIHRvIGJlIHNpZ25pZmljYW50bHkgc21hbGxlci4gIA0KDQpTbyB3ZSBmaWx0ZXIgdGhlIGRhdGEgcmVsYXRpdmUgb25seSB0byB0aGUgbm9uLXByZW1hdHVyZSBiaXJ0aHMgYW5kIHN0dWR5IHRoZWlyIHN0YXRpc3RpY3MuIA0KDQoNCmBgYHtyfQ0KcmVndWxhcl9kZWxpdmVyaWVzX2RhdGEgPC0gZGF0YXNldCAlPiUgZmlsdGVyKEdlc3RhemlvbmUgPj0gMzcpDQoNCnN0YXRpc3RpY3NfcmVndWxhcl9kZWxpdmVyaWVzIDwtIGRhdGEuZnJhbWUoDQogICAgICAgIA0KICBxdWFydGlsZV8xID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwgMiwgZnVuY3Rpb24oeCkgcXVhbnRpbGUoeCxwcm9icz0wLjI1LCBuYW1lcz1GQUxTRSkpLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgbWVkaWFuID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwgMiwgbWVkaWFuKSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgcXVhcnRpbGVfMyA9IHJvdW5kKGFwcGx5KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhW2MoIkNyYW5pbyIsIlBlc28iLCJMdW5naGV6emEiKV0sIDIsIGZ1bmN0aW9uKHgpIHF1YW50aWxlKHgscHJvYnM9MC43NSwgbmFtZXM9RkFMU0UpKSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIG1lYW4gPSByb3VuZChhcHBseShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YVtjKCJDcmFuaW8iLCJQZXNvIiwiTHVuZ2hlenphIildLCAyLCBtZWFuKSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIHNkID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwgMiwgc2QpLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgY3YgPSByb3VuZChhcHBseShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YVtjKCJDcmFuaW8iLCJQZXNvIiwiTHVuZ2hlenphIildLCAyLCBjdl9mdW5jdCksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBza2V3bmVzcyA9IHJvdW5kKGFwcGx5KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhW2MoIkNyYW5pbyIsIlBlc28iLCJMdW5naGV6emEiKV0sIDIsIHNrZXduZXNzKSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIGt1cnRvc2lzID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwgMiwga3VydG9zaXNfaW5kZXgpLDIpLA0KICANCiAgc3RkX2VyciA9IHJvdW5kKGFwcGx5KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhW2MoIkNyYW5pbyIsIlBlc28iLCJMdW5naGV6emEiKV0sMixzdGQuZXJyKSwyKQ0KKQ0KDQpyb3duYW1lcyhzdGF0aXN0aWNzX3JlZ3VsYXJfZGVsaXZlcmllcykgPC0gYygiQ3JhbmlhbCBjaXJjdW1mZXJlbmNlIChtbSkiLCAiV2VpZ2h0IChncikiLCAiTGVuZ3RoIChtbSkiKQ0KDQprYWJsZShzdGF0aXN0aWNzX3JlZ3VsYXJfZGVsaXZlcmllcywgY2FwdGlvbj0iSW5kaWNlcyBvZiB3ZWlnaHQsIGxlbmd0aCBhbmQgY3JhbmlhbCBkaWFtZXRlciBvZiB0aGUgbm9uLXByZW1hdHVyZSBiaXJ0aHMiKSAlPiUNCiAga2FibGVfc3R5bGluZygpDQoNCg0KDQojIHdlIHBsb3QgdGhlIGRlbnNpdHkgb2YgdGhlIGRpZmZlcmVudCB2YXJpYWJsZXMgYWdhaW5zdCBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24NCmdncGxvdCgpKw0KICBnZW9tX2RlbnNpdHkoYWVzKHg9Z2F1c3NpYW5fZGlzdHJpYnV0aW9uLCBmaWxsPSJOMDEiKSwgY29sb3I9TkEsICBhbHBoYT0uOCkrDQogIGdlb21fZGVuc2l0eShhZXMoeD1ub3JtYWxpemUocmVndWxhcl9kZWxpdmVyaWVzX2RhdGEkUGVzbyksIGNvbG9yPSJXZWlnaHQiKSwgbGluZXdpZHRoPTEuNSkrDQogIGdlb21fZGVuc2l0eShhZXMoeD1ub3JtYWxpemUocmVndWxhcl9kZWxpdmVyaWVzX2RhdGEkQ3JhbmlvKSwgY29sb3I9IkNyYW5pYWwgY2lyY3VtZmVyZW5jZSIpLCBsaW5ld2lkdGg9MS41KSsNCiAgZ2VvbV9kZW5zaXR5KGFlcyh4PW5vcm1hbGl6ZShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YSRMdW5naGV6emEpLCBjb2xvcj0iTGVuZ3RoIiksIGxpbmV3aWR0aD0xLjUpKw0KICBsYWJzKHRpdGxlPSJEaXN0cmlidXRpb24gb2YgdGhlIGFudGhyb3BvbWV0cmljIHZhcmlhYmxlcyBcbiByZWxhdGl2ZSB0byB0aGUgbm9uLXByZW1hdHVyZSBkZWxpdmVyaWVzIiwNCiAgICAgICB4PSJOb3JtYWxpemVkIHZhcmlhYmxlICIsDQogICAgICAgeT0iZGVuc2l0eSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKE4wMSA9ICJncmV5IiksICAgDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoTjAxID0gIk4oMCwxKSIpKSArDQogIHRoZW1lX2NsYXNzaWMoKSsNCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwYW5lbCIsDQogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSkpKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG9yID0gTkEpKSkrDQogIGxhYnMoY29sb3IgPSAiVmFyaWFiaWxlIiwNCiAgICAgIGZpbGwgPSAiRGlzdHJpYnV6aW9uZSBub3JtYWxlIikNCmBgYA0KDQpUaGUgc2lnbmlmaWNhbnRseSBzbWFsbGVyIHZhbHVlcyBvZiBza2V3ZW5lc3Mgb2YgYWxsIHRoZSBhbnRocm9wb21ldHJpYyB2YXJpYWJsZXMgY29ycmVzcG9uZGluZyBvbmx5IHRvIHRoZSBub24tcHJlbWF0dXJlIGJpcnRocyBjb25maXJtIHRoYXQgdGhlIGFzeW1tZXRyeSB0aGF0IHdlIGhhZCBwcmV2aW91c2x5IG9ic2VydmVkIHdhcyBtYWlubHkgZHVlIHRvIHRoZSBwcmVzZW5jZSBvZiBwcmVtYXR1cmUgYmlydGhzLiBBbHNvIHRoZSB2YWx1ZXMgb2YgdGhlIGt1cnRvc2lzIGhhdmUgc2lnbmlmaWNhbmx0eSBkZWNyZWFzZWQgc3VnZ2VzdGluZyBhIG11Y2ggYmV0dGVyIG5vcm1hbCBhcHByb3hpbWF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGFudGhyb3BvbWV0cmljIHZhcmlhYmxlcyB3aGVuIGZpbHRlcmluZyB0aGUgZGF0YSBjb3JyZXNwb25kaW5nIHRvIHRoZSBvbmx5IG5vbi1wcmVtYXR1cmUgZGVsaXZlcmllcy4gVGhlIGxlbmd0aCBpcyB0aGUgb25seSB2YXJpYWJsZXMgdGhhdCBpcyBub3QgcmVhbGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgbm9uLW5vcm1hbGl0eSBvZiB0aGUgdmFyaWFibGUgTGVuZ2h0IGNvdWxkIGJlIGR1ZSB0byB0aGUgcHJlc2VuemUgb2Ygc29tZSBjb3JydXB0ZWQgZGF0YS4gDQpUbyB0aGlzIGVuZCB3ZSBzdHVkeSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYW50aHJvcG9tZXRyaWMgdmFyaWFibGVzLg0KDQoNCmBgYHtyfQ0KDQoNCmNhdCgiVGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50ZSBvZiB0aGUgdmFyaWFibGUgTGVuZ3RoIHdpdGggdGhlIHZhcmlibGVzIFdlaWdodCBhbmQgQ3JhbmlhbCBjaXJjdW1mZXJlbmNlIGluIG5vbiBwcmVtYXR1cmUgbmV3Ym9ybnMgaXMgZXF1YWwgdG86IFxuIiwgDQogICAgIi1jb3IoTGVuZ3RoLCBXZWlnaHQpID0gIiwgY29yKHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhJEx1bmdoZXp6YSwgcmVndWxhcl9kZWxpdmVyaWVzX2RhdGEkUGVzbykNCiAsICJcbiIsDQogICAgIi1jb3IoTGVuZ3RoLCBDcmFuaWFsIGNpcmN1bWZlcmVuY2UpID0gIiwgY29yKHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhJEx1bmdoZXp6YSwgcmVndWxhcl9kZWxpdmVyaWVzX2RhdGEkQ3JhbmlvKQ0KKQ0KYGBgDQpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9DQpzY2F0dDEgPSBnZ3Bsb3QocmVndWxhcl9kZWxpdmVyaWVzX2RhdGEsIGFlcyh4ID0gTHVuZ2hlenphLCB5ID0gUGVzbykpICsgDQogICAgICAgICAgICAgICAgZ2VvbV9wb2ludCgpKw0KICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHIgPSAxMCkpKSsNCiAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJTY2F0dGVycGxvdCBvZiBMZW5naHQgYWdhaW5zdCBXZWlnaHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHg9IiBMZW5naHQobW0pIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PSIgV2VpZ2h0KGdyKSIpKw0KICAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAwMCw1MDAwLDUwMCkpKw0KICAgICAgICAgICAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMzAwLDYwMCw1MCkpDQoNCg0Kc2NhdHQyID0gZ2dwbG90KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhLCBhZXMoeCA9IEx1bmdoZXp6YSwgeSA9IENyYW5pbykpICsgDQogICAgICAgICAgICAgICAgZ2VvbV9wb2ludCgpKw0KICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHIgPSAxMCkpKSsNCiAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJTY2F0dGVycGxvdCBvZiBMZW5naHQgYWdhaW5zdCBDcmFuaWFsIGNpcmN1bWZlcmVuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHg9IiBMZW5naHQobW0pIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PSIgQ3JhbmlhbCBjaXJjdW1mZXJlbmNlKG1tKSIpKw0KICAgICAgICAgICAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMzAwLDYwMCw1MCkpDQoNCmNvbWJfZmlnX3NjYXR0IDwtIHNjYXR0MSArIHNjYXR0MiArIHBsb3RfbGF5b3V0KG5jb2w9MiwgaGVpZ2h0cyA9IGMoNSksIHdpZHRocz0gYyg1LDUpKQ0KcHJpbnQoY29tYl9maWdfc2NhdHQpDQpgYGANCkZyb20gdGhlIHNjYXR0ZXJwbG90cywgaXQgY2FuIGJlIHNlZW4gdGhhdCB0aGVyZSBpcyBvbmUgb3V0bGllciBtZWFzdXJlbWVudCByZXBvcnRpbmcgYSBsZW5ndGggc21hbGxlciB0aGFuIDM1MCBtbSwgd2hpY2ggY291bGQgYmUgZHVlIHRvIGFuIGVycm9yIGluIHRoZSBkYXRhc2V0LCBzdWNoIGFzIGEgdHlwbyBpbiB0aGUgYW5ub3RhdGlvbiBvZiB0aGUgbmV3Ym9ybidzIGxlbmd0aC4gDQpTbyB3ZSBkZWNpZGUgdG8gY2xlYW4gdGhlIGRhdGFzZXQgb2YgcmVndWxhciBuZXdib3JucyBmcm9tIHRoaXMgZGF0YXBvaW50Lg0KDQpgYGB7cn0NCnJlZ3VsYXJfZGVsaXZlcmllc19kYXRhIDwtIHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhICU+JSBmaWx0ZXIoTHVuZ2hlenphID49IDM1MCkNCg0Kc3RhdGlzdGljc19yZWd1bGFyX2RlbGl2ZXJpZXMgPC0gZGF0YS5mcmFtZSgNCiAgICAgICAgDQogIHF1YXJ0aWxlXzEgPSByb3VuZChhcHBseShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YVtjKCJDcmFuaW8iLCJQZXNvIiwiTHVuZ2hlenphIildLCAyLCBmdW5jdGlvbih4KSBxdWFudGlsZSh4LHByb2JzPTAuMjUsIG5hbWVzPUZBTFNFKSksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBtZWRpYW4gPSByb3VuZChhcHBseShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YVtjKCJDcmFuaW8iLCJQZXNvIiwiTHVuZ2hlenphIildLCAyLCBtZWRpYW4pLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBxdWFydGlsZV8zID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwgMiwgZnVuY3Rpb24oeCkgcXVhbnRpbGUoeCxwcm9icz0wLjc1LCBuYW1lcz1GQUxTRSkpLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgbWVhbiA9IHJvdW5kKGFwcGx5KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhW2MoIkNyYW5pbyIsIlBlc28iLCJMdW5naGV6emEiKV0sIDIsIG1lYW4pLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgc2QgPSByb3VuZChhcHBseShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YVtjKCJDcmFuaW8iLCJQZXNvIiwiTHVuZ2hlenphIildLCAyLCBzZCksMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBjdiA9IHJvdW5kKGFwcGx5KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhW2MoIkNyYW5pbyIsIlBlc28iLCJMdW5naGV6emEiKV0sIDIsIGN2X2Z1bmN0KSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIHNrZXduZXNzID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwgMiwgc2tld25lc3MpLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAga3VydG9zaXMgPSByb3VuZChhcHBseShyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YVtjKCJDcmFuaW8iLCJQZXNvIiwiTHVuZ2hlenphIildLCAyLCBrdXJ0b3Npc19pbmRleCksMiksDQogIA0KICBzdGRfZXJyID0gcm91bmQoYXBwbHkocmVndWxhcl9kZWxpdmVyaWVzX2RhdGFbYygiQ3JhbmlvIiwiUGVzbyIsIkx1bmdoZXp6YSIpXSwyLHN0ZC5lcnIpLDIpDQopDQoNCnJvd25hbWVzKHN0YXRpc3RpY3NfcmVndWxhcl9kZWxpdmVyaWVzKSA8LSBjKCJDcmFuaWFsIGNpcmN1bWZlcmVuY2UgKG1tKSIsICJXZWlnaHQgKGdyKSIsICJMZW5ndGggKG1tKSIpDQoNCmthYmxlKHN0YXRpc3RpY3NfcmVndWxhcl9kZWxpdmVyaWVzLCBjYXB0aW9uPSJJbmRpY2VzIG9mIHdlaWdodCwgbGVuZ3RoIGFuZCBjcmFuaWFsIGRpYW1ldGVyIG9mIHRoZSBub24tcHJlbWF0dXJlIGJpcnRocyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpXZSBvYnNlcnZlIHRoYXQgbm93LCBpbiB0aGUgY2xlYW5lZCBkYXRhc2V0LCBhbHNvIHRoZSB2YXJpYWJsZSBMZW5ndGggZm9sbG93cyBhcHByb3hpbWF0ZWx5IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgd2l0aCBib3RoIGt1cnRvc2lzIGFuZCBza2V3bmVzcyBjb2VmZmljaWVudHMgY2xvc2UgdG8gMC4NCg0KDQojIyMgQW5hbHlzaXMgb2Ygd2VpZ2h0LCBsZW5ndGggYW5kIGNyYW5pYWwgZGlhbWV0ZXIgb2YgdGhlIHNhbXBsZSB3aXRoIHJlc3BlY3QgdG8gdGhlIHBvcHVsYXRpb24gc3RhdGlzdGljcy4NCg0KQWNjb3JkaW5nIHRvIHRoZSBzdHVkeSBbSW50ZXJuYXRpb25hbCBzdGFuZGFyZHMgZm9yIG5ld2Jvcm4gd2VpZ2h0LA0KbGVuZ3RoLCBhbmQgaGVhZCBjaXJjdW1mZXJlbmNlIGJ5IGdlc3RhdGlvbmFsIGFnZSBhbmQNCnNleF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvUzAxNDAtNjczNigxNCk2MDkzMi02KSwgdGhlIGJpcnRod2VpZ2h0LA0KYmlydGhsZW5ndGggYW5kIGJpcnRoIGhlYWQgY2lyY3VtZmVyZW5jZSBvZiBiYWJpZXMgYm9ybiBpbiBJdGFseSBmcm9tDQp0aGUgMzd0aCB3ZWVrIG9mIGdlc3RhdGlvbiBvbiwgaGF2ZSB0aGUgZm9sbG93aW5nIG1lYW4gYW5kIHN0YW5kYXJkDQpkZXZpYXRpb246DQoNCi0gICBiaXJ0aHdlaWdodDogTWVhbiA9IDMuMywgU0QgPSAwLjQsDQoNCi0gICBiaXJ0aGxlbmd0aDogTWVhbiA9IDQ5LjQsIFNEID0gMS43LA0KDQotICAgYmlydGggaGVhZCBjaXJjdW1mZXJlbmNlOiBNZWFuID0gMzQuMCwgU0QgPSAxLjINCg0KRnJvbSB0aGUgY2VudHJhbCBsaW1pdCB0aGVvcmVtIHdlIGtub3cgdGhhdCBnaXZlbiBhIHJhbmRvbSB2YXJpYWJsZQ0KWCB3aXRoIG1lYW4gJFxtdV8wJCBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uICRcc2lnbWEkLCB0aGUgbWVhbiBvdmVyIGENCnJhbmRvbSBzYW1wbGUgb2YgTiBwZW9wbGUgZGlzdHJpYnV0ZXMgYXMgYSByYW5kb20gdmFyaWFibGUgZm9sbG93aW5nIGENCm5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuICRcbXVfMCQgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAkXHNpZ21hL04kLg0KDQpTbyBmb3IgdGhlIHRocmVlIHZhcmlhYmxlcyB3ZWlnaHQsIGxlbmd0aCBhbmQgY3JhbmlhbCBjaXJjdW1mZXJlbmNlIHdlDQphc3N1bWUgYXMgbnVsbCBoeXBvdGhlc2lzIChIMCkgdGhhdCB0aGV5IGhhdmUgbWVhbiAkXG11XzA9JCAzMzAwIGdyLA0KNDkuNCBjbSBhbmQgMzQgY20gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAkXHNpZ21hPSQgNDAwZ3IsIDEuN2NtLCAxLjJjbSwNCnJlc3BlY3RpdmVseS4gVGhlbiB3ZSB0ZXN0IHRoaXMgaHlwb3RoZXNlcyB1c2luZyBhIFotdGVzdC4gVG8gdGhpcyBlbmQNCndlIGNvbnNpZGVyIHRoZSBzdWItZGF0YXNldCBjb3JyZXNwb25kaW5nIHRvIHRoZSBiaXJ0aHMgYWZ0ZXIgdGhlIDM3dGgNCndlZWsgb2YgZ2VzdGF0aW9uLCBoYXZpbmcgbGVuZ3RoICROJC4gDQoNClRoZW4gd2UgY29tcHV0ZSB0aGUgWi10ZXN0IHZhbHVlIA0KJCRaID0gXGZyYWN7bWVhbihWYXJpYWJsZSktXG11XzB9e1xzaWdtYS9cc3FydHtOfX0uJCQNCg0KDQpXZSBjb21wdXRlIHRoZSBjb3JyZXNwb25maW5nIHAtdmFsdWVzLCBpLmUuIHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIGEgdmFsdWUgZXF1YWwgb3INCnNtYWxsZXIgdGhhbiBtZWFuKFZhcmlhYmxlKSBieSByYW5kb21seSBzYW1wbGluZyBOIGJpcnRocy4gSWYgdGhlDQpwLXZhbHVlIGlzIHNtYWxsZXIgdGhhbiB0aGUgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlIHRoYXQgd2Ugc2V0IHRvIDAuMDUsDQp3ZSByZWplY3QgdGhlIGh5cG90aGVzaXMgSDAuDQoNCmBgYHtyfQ0Kel90ZXN0ID0gZnVuY3Rpb24oeCwgbXUwLCBzdGRldiwgYWxmYSl7DQoNCiAgeCA9IG5hLm9taXQoeCkNCiAgbXVfY2FwID0gbWVhbih4KQ0KICBuID0gbGVuZ3RoKHgpDQogIA0KICBaID0gKG11X2NhcCAtIG11MCkgLyAoc3RkZXYgLyBzcXJ0KG4pKQ0KICB2YWxvcmkuc29nbGlhID0gcW5vcm0oYyhhbGZhLzIsIDEtYWxmYS8yKSkNCiAgDQogIENJIDwtIGMobXVfY2FwK3Fub3JtKGFsZmEvMikqKHN0ZGV2L3NxcnQobikpLA0KICAgICAgICAgIG11X2NhcC1xbm9ybShhbGZhLzIpKihzdGRldi9zcXJ0KG4pKSkNCiAgICANCiAgcmV0dXJuKA0KICAgIGxpc3QobWVhbi5zYW1wbGU9IG11X2NhcCwNCiAgICAgICAgIHN0YXQudGVzdCA9IFosDQogICAgICAgICB0aHJlc2hvbGQudmFsID0gdmFsb3JpLnNvZ2xpYSwNCiAgICAgICAgIHB2YWx1ZSA9IDIqcG5vcm0oLWFicyhaKSksDQogICAgICAgICBJbnQuQ29uZi4gPSBDSQ0KICAgICAgICAgKSkNCn0NCmBgYA0KDQoNCg0KYGBge3J9DQp6X3Rlc3Rfd2VpZ2h0ID0gel90ZXN0KHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhJFBlc28sIDMzMDAsIDQwMCwgMC4wNSkNCg0Kel90ZXN0X2xlbmd0aCA9IHpfdGVzdChyZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YSRMdW5naGV6emEsIDQ5NCwgMTcsIDAuMDUpDQoNCnpfdGVzdF9jcmFuaWFsX2RpYW0gPSB6X3Rlc3QocmVndWxhcl9kZWxpdmVyaWVzX2RhdGEkQ3JhbmlvLCAzNDAsIDEyLCAwLjA1KQ0KDQoNCmNhdCgiVGhlIHAtdmFsdWVzIG9mIGxlbmd0aCwgd2VpZ2h0IGFuZCBjcmFuaWFsIGNpcmN1bWZlcmVuY2UgYXJlIGFsbCBzbWFsbGVyIHRoYW4gMC4wNSwgc28gd2UgcmVqZWN0IHRoZSBoeXBvdGhlc2lzIEgwIHRoYXQgdGhlIHRocmVlIHZhcmlhYmxlcyBhcmUgZGlzdHJpYnV0ZWQgYWNjb3JkaW5nIHRvIHRoZSBkaXN0cmlidXRpb24gb2Ygd2VpZ2h0LCBsZW5ndGggYW5kIGNyYW5pYWwgZGlhbWV0ZXIgb2YgdGhlIGl0YWxpYW4gcG9wdWxhdGlvbi4gXG4iLCANCiAgICAiLXBfdmFsdWUoV2VpZ2h0KSA9ICIsIHpfdGVzdF93ZWlnaHQkcHZhbHVlICwgIlxuIiwNCiAgICAiLXBfdmFsdWUoTGVuZ3RoKSA9ICIsIHpfdGVzdF9sZW5ndGgkcHZhbHVlICwgIlxuIiwNCiAgICAiLXBfdmFsdWUoQ3JhbmlhbCBkaWFtZXRlcikgPSAiLCB6X3Rlc3RfY3JhbmlhbF9kaWFtJHB2YWx1ZSApDQpgYGANCg0KSW4gcGFydGljdWxhciB3ZSBkaXNwbGF5IHRoZSBaLXRlc3QgdmFsdWVzIG9mIHRoZSBhbnRocm9wb21ldHJpYyB2YXJpYWJsZXMgYWdhaW5zdCB0aGUgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlIG9mIDAuMDUgb2YgdGhlIFotdGVzdC4NCg0KYGBge3J9DQpnZ3Bsb3QoKSsNCiAgZ2VvbV9kZW5zaXR5KGFlcyh4PWdhdXNzaWFuX2Rpc3RyaWJ1dGlvbiwgZmlsbD0iTjAxIiksIGNvbG9yPU5BLCAgYWxwaGE9LjgpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD16X3Rlc3Rfd2VpZ2h0JHN0YXQudGVzdCwgY29sb3I9IldlaWdodCIpLCBsaW5ld2lkdGg9MS41KSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9el90ZXN0X2xlbmd0aCRzdGF0LnRlc3QsIGNvbG9yPSJMZW5ndGgiKSwgbGluZXdpZHRoPTEuNSkrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9el90ZXN0X2NyYW5pYWxfZGlhbSRzdGF0LnRlc3QsIGNvbG9yPSJDcmFuaWFsIGNpcmN1bWZlcmVuY2UiKSwgbGluZXdpZHRoPTEuNSkrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9el90ZXN0X2xlbmd0aCR0aHJlc2hvbGQudmFsLCBjb2xvcj0iVGhyZXNob2xkIHA9MC4wNSIpLGxpbmV3aWR0aD0xLCBsaW5ldHlwZT0iZGFzaGVkIikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIldlaWdodCI9ICJnb2xkMSIsICJMZW5ndGgiPSJnb2xkNCIsICJDcmFuaWFsIGNpcmN1bWZlcmVuY2UiPSJnb2xkMyIsICJUaHJlc2hvbGQgcD0wLjA1Ij0icmVkIikpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKE4wMSA9ICJncmV5IiksICAgDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoTjAxID0gIk4oMCwxKSIpKSArDQogIGxhYnModGl0bGU9IlogdmFsdWVzIG9mIHdlaWdodCwgbGVuZ3RoIGFuZCBjcmFuaWFsIGNpcmN1bWZlcmVuY2UiLA0KICAgICAgIHg9IiAiLA0KICAgICAgIHk9IiAiKSsNCiAgdGhlbWVfY2xhc3NpYygpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE1LCBoanVzdCA9IDEpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0iYm90dG9tIikNCmBgYA0KDQoNCiMjIyBBbmFseXNpcyBvZiB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIGFudHJob3BvbWV0cmljIHZhcmlhYmxlcyBhY3Jvc3MgdGhlIGRpZmZlcmVudCBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzLg0KDQpGaXJzdCB3ZSBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb24gb2YgbmV3Ym9ybnMgYmV0d2VlbiB0aGUgdHdvIHNleGVzLCB3aGljaCByZXN1bHQgcXVpdGUgdW5pZm9ybS4NCg0KDQoNCmBgYHtyfQ0Kc2V4X2NvbG9ycyA8LSBjKCJNIiA9ICJkb2RnZXJibHVlIiwgIkYiID0gImhvdHBpbmsiKSAgDQoNCg0KZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogIGdlb21fYmFyKGFlcyh4PVNlc3NvLA0KICAgICAgICAgICAgICAgZmlsbD1TZXNzbywNCiAgICAgICAgICAgICAgIGNvbG9yPVNlc3NvKSwNCiAgICAgICAgICAgc3RhdD0iY291bnQiKSsNCiAgbGFicyh0aXRsZT0iU2V4IGRpc3RyaWJ1dGlvbiBvZiBuZXdib3JucyIsDQogICAgICAgeD0ic2V4IiwNCiAgICAgICB5PSJhYnNvbHV0ZSBmcmVxdWVuY3kiLA0KICAgICAgIGNvbG9yPSJTRVg6IiwNCiAgICAgICBmaWxsPSJTRVg6IikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gc2V4X2NvbG9ycykrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxNTAwLDEwMCkpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMTUsIGhqdXN0ID0gMSksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbih0ID0gMTApKSwNCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHIgPSAxMCkpKQ0KYGBgDQoNClNlY29uZCB3ZSBzdHVkeSB0aGUgc3RhdGlzdGljcyBvZiB0aGUgYW50aHJvcG9tZXRyaWMgdmFyaWFibGVzIGFjcm9zcyB0aGUgdHdvIHNleGVzIGFuZCBkaXNwbGF5IHRoZW0gdmlhIGEgYm94cGxvdC4gSW4gb3JkZXIgdG8gdGVzdCBsYXRlciBkaWZmZXJlbmNlcyBhY3Jvc3MgdGhlIHNleGVzIHVzaW5nIGEgdC50ZXN0IGl0IGlzIGltcG9ydGFudCB0byBjb21wYXJlIG5vcm1hbGx5IGRpc3RydWJ1dGVkIHF1YW50aXRpZXMuIEZvciB0aGlzIHJlYXNvbiwgd2UgcmVzdHJpY3Qgb3VyIHN0dWR5IG9ubHkgdG8gdGhlIG5vdC1wcmVtYXR1cmUgZGVsaXZlcmllcy4NCg0KDQpgYGB7cn0NCmN2X2Z1bmN0IDwtIGZ1bmN0aW9uKHgpIHsNCiAgcmV0dXJuKHNkKHgpIC8gbWVhbih4KSAqIDEwMCkNCn0NCg0KDQpjb25kaXRpb25hbF9kYXRhc2V0X3NleCA8LSByZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YSAlPiUgDQogIGdyb3VwX2J5KFNlc3NvKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX3Blc28gPSByb3VuZChtZWFuKFBlc28pLDIpLA0KICAgICAgICAgICAgc2RfcGVzbyA9IHJvdW5kKHNkKFBlc28pLDIpLA0KICAgICAgICAgICAgY3ZfcGVzbyA9IHJvdW5kKGN2X2Z1bmN0KFBlc28pLCAyKSwNCiAgICAgICAgICAgIHNrZXdfcGVzbyA9IHJvdW5kKHNrZXduZXNzKFBlc28pLCAyKSwNCiAgICAgICAgICAgIGt1cnRfcGVzbyA9IHJvdW5kKGt1cnRvc2lzX2luZGV4KFBlc28pLCAyKSwNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgbWVhbl9sdW5naGV6emEgPSByb3VuZChtZWFuKEx1bmdoZXp6YSksMiksDQogICAgICAgICAgICBzZF9sdW5naGV6emEgPSByb3VuZChzZChMdW5naGV6emEpLDIpLA0KICAgICAgICAgICAgY3ZfbHVuZ2hlenphID0gcm91bmQoY3ZfZnVuY3QoTHVuZ2hlenphKSwgMiksDQogICAgICAgICAgICBza2V3X2x1bmdoZXp6YSA9IHJvdW5kKHNrZXduZXNzKEx1bmdoZXp6YSksIDIpLA0KICAgICAgICAgICAga3VydF9sdW5naGV6emEgPSByb3VuZChrdXJ0b3Npc19pbmRleChMdW5naGV6emEpLCAyKSwNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgbWVhbl9jcmFuaW8gPSByb3VuZChtZWFuKENyYW5pbyksMiksDQogICAgICAgICAgICBzZF9jcmFuaW8gPSByb3VuZChzZChDcmFuaW8pLDIpLA0KICAgICAgICAgICAgY3ZfY3JhbmlvID0gcm91bmQoY3ZfZnVuY3QoQ3JhbmlvKSwgMiksDQogICAgICAgICAgICBza2V3X2NyYW5pbyA9IHJvdW5kKHNrZXduZXNzKENyYW5pbyksIDIpLA0KICAgICAgICAgICAga3VydF9jcmFuaW8gPSByb3VuZChrdXJ0b3Npc19pbmRleChDcmFuaW8pLCAyKSkNCg0KDQpjb2xuYW1lcyhjb25kaXRpb25hbF9kYXRhc2V0X3NleCkgPC0gYygic2V4IiwgIm1lYW4iLCAic2QiLCAiY3YiLCAiU2tldyIsICJrdXJ0IiAsICJtZWFuIiwgInNkIiwgImN2IiAsICJTa2V3IiwgImt1cnQiLCAgIm1lYW4iLCAic2QiLCAiY3YiLCAiU2tldyIsICJrdXJ0IikNCg0Ka2FibGUoY29uZGl0aW9uYWxfZGF0YXNldF9zZXgsIGNhcHRpb249IkluZGljZXMgb2Ygd2VpZ2h0LCBsZW5ndGggYW5kIGNyYW5pYWwgZGlhbWV0ZXIgb2Ygbm9uLXByZW1hdHVyZSBuZXdib3JucywgZGl2aWRlZCBieSBzZXgiKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJjb25kZW5zZWQiLCBmdWxsX3dpZHRoID0gVFJVRSkgJT4lDQogIGFkZF9oZWFkZXJfYWJvdmUoYygiICI9MSwgIldlaWdodCIgPSA1LCAiTGVuZ3RoIiA9IDUsICJDcmFuaWFsIGNpcmN1bWZlcmVuY2UiID0gNSkpICU+JQ0KICByb3dfc3BlYygxLCBiYWNrZ3JvdW5kID0gInBpbmsiKSU+JQ0KICByb3dfc3BlYygyLCBiYWNrZ3JvdW5kID0gImxpZ2h0Ymx1ZSIpICU+JQ0KICBjb2x1bW5fc3BlYyhjKDEsNiwxMSksIGJvcmRlcl9yaWdodCA9IFRSVUUpDQpgYGANCg0KDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9Nn0NCg0KYm94X2xlbmd0aF9zZXggPC0gZ2dwbG90KGRhdGE9cmVndWxhcl9kZWxpdmVyaWVzX2RhdGEsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeD1TZXNzbywNCiAgICAgICAgICAgICAgICAgICAgICAgIHk9THVuZ2hlenphLA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1TZXNzbyksDQogICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1TZXNzbykrDQogICAgICAgICAgICAgICAgICAgICBnZW9tX2JveHBsb3QoKSsNCiAgICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGU9Ikxlbmd0aCBkaXRyaWJ1dGlvbiBhY2NvcmRpbmcgdG8gc2V4XG4gb2Ygbm9uIHByZW1hdHVyZSBuZXdib3JucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHg9IiAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5PSJsZW5ndGggKGNtKSIpKw0KICAgICAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBzZXhfY29sb3JzKSsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGFuZWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBoanVzdD0wLjUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrDQogICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgzMTAsIDU2MCwgYnkgPSAyNSkpKw0KICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIk0iPSJNYWxlIiwgIkYiPSJGZW1hbGUiKSkNCg0KDQpib3hfd2VpZ2h0X3NleCA8LSBnZ3Bsb3QoZGF0YT1yZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgIGFlcyh4PVNlc3NvLA0KICAgICAgICAgICAgICAgICAgICAgICAgeT1QZXNvLA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1TZXNzbyksDQogICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1TZXNzbykrDQogICAgICAgICAgICAgICAgICAgICBnZW9tX2JveHBsb3QoKSsNCiAgICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGU9IldlaWdodCBkaXRyaWJ1dGlvbiBhY2NvcmRpbmcgdG8gc2V4XG4gb2Ygbm9uIHByZW1hdHVyZSBuZXdib3JucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHg9IiAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5PSJ3ZWlnaHQgKEtnKSIpKw0KICAgICAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBzZXhfY29sb3JzKSsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGFuZWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBoanVzdD0wLjUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrDQogICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxMDAwLCA1MDAwLCBieSA9IDQwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKDEuMCwgNS4wLCBieSA9IDAuNCkpKw0KICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIk0iPSJNYWxlIiwgIkYiPSJGZW1hbGUiKSkNCg0KDQpib3hfaGVhZF9zZXggPC0gZ2dwbG90KGRhdGE9cmVndWxhcl9kZWxpdmVyaWVzX2RhdGEsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeD1TZXNzbywNCiAgICAgICAgICAgICAgICAgICAgICAgIHk9Q3JhbmlvLA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1TZXNzbyksDQogICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1TZXNzbykrDQogICAgICAgICAgICAgICAgICAgICBnZW9tX2JveHBsb3QoKSsNCiAgICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGU9IkNyYW5pYWwgZGlhbWV0ZXIgZGl0cmlidXRpb24gYWNjb3JkaW5nIHRvIHNleFxuIG9mIG5vbiBwcmVtYXR1cmUgbmV3Ym9ybnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4PSIgIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeT0iZGlhbWV0ZXIgKGNtKSIpKw0KICAgICAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjKCJNIj0iZG9kZ2VyYmx1ZSIsICJGIj0iaG90cGluayIpKSsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGFuZWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBoanVzdD0wLjUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrDQogICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgyNTAsIDQwMCwgYnkgPSAyNSkpKw0KICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIk0iPSJNYWxlIiwgIkYiPSJGZW1hbGUiKSkNCg0KDQpjb21iX2ZpZ19hbnRyb3BvbWV0cmljcyA8LSBib3hfaGVhZF9zZXggKyBib3hfd2VpZ2h0X3NleCArIGJveF9sZW5ndGhfc2V4ICsgcGxvdF9sYXlvdXQobmNvbD0zLCBoZWlnaHRzID0gYyg1KSwgd2lkdGhzPSBjKDUsNSw1KSkNCnByaW50KGNvbWJfZmlnX2FudHJvcG9tZXRyaWNzKQ0KYGBgDQpUaGUgZGF0YSBzdWdnZXN0IGEgZGlmZmVyZW5jZSBpbiB0aGUgYW50aHJvcG9tZXRyaWMgdmFyaWFibGVzIGFjcm9zcyB0aGUgdHdvIHNleGVzLCB3aXRoIGhpZ2hlciB2YWx1ZXMgZm9yIGFsbCB0aGUgdmFyaWFibGVzIGluIGNhc2Ugb2YgbWFsZXMgcmF0aGVyIHRoYW4gZmVtYWxlcy4gDQoNCiMjIyBXZSB0ZXN0IHRoZSBoeXBvdGhlc2lzIHRoYXQgdGhlIGFudHJvcG9tZXRyaWMgdmFsdWVzIG9mIG1hbGVzIGFuZCBmZW1hbGVzIGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudA0KDQpJbiBvcmRlciB0byBwZXJmb3JtIGEgbWVhbmluZ2Z1bGwgdC10ZXN0IHRoYXQgY29tcGFyZXMgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgYW50aHJvcG9tZXRyaWMgbWVhc3VyZW1lbnRzIG9mIG1hbGVzIGFuZCBmZW1hbGVzIHdlIGZpcnN0IGhhdmUgdG8gY2hlY2sgdGhhdCB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgYW50cmhvcG9tZXRyaWMgbWVhc3VyZW1lbnRzIG9mIG1hbGVzIGFuZCBmZW1hbGVzIGFyZSBhcHByb3hpbWF0ZWxseSBub3JtYWwgYW5kIGhhdmUgc2ltaWxhciB2YXJpYW5jZXMuIA0KVGhlIHByZXZpb3VzIHRhYmxlIHN1Z2dlc3QgdGhhdCB0aGlzIHNob3VsZCBiZSB0cnVlIGlmIHdlIHJlc3RyaWN0IG91ciBzdHVkeSBvbmx5IHRvIHRoZSBub24tcHJlbWF0dXJlIGRlbGl2ZXJpZXMuIA0KV2UgcGVyZm9ybSBRLVEgdGVzdHMgYW5kIFNoYXBpcm8gV2lsayB0ZXN0cyB0byB2ZXJpZnkgdGhhdCB0aGUgdGhyZWUgdmFyaWFibGVzIGRpc3RyaWJ1dGUgYWNjb3JkaW5nIHRvIG5vcm1hbCBkaXN0cmlidXRpb25zIGZvciBib3RoIHNleGVzLiBXZSByZWNhbGwgdGhlIFEtUSBwbG90cyBkaXNwbGF5IHRoZSBvYnNlcnZlZCBxdWFudGlsZXMgb2YgdGhlIHZhcmlhYmxlcyBhZ2FpbnN0IHRoZSBxdWFudGlsZXMgb2YgYSBHYXVzc2lhbiBkaXN0cmlidXRpb24uIFNvLCBpZiB0aGUgdmFyaWFibGVzIGNvbWUgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24sIHRoZSBwb2ludHMgaW4gdGhlIFEtUSBwbG90IHdpbGwgYXBwcm94aW1hdGVseSBsaWUgYWxvbmcgYSBzdHJhaWdodCBsaW5lLg0KDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTV9DQoNCm1hbGVzIDwtIHJlZ3VsYXJfZGVsaXZlcmllc19kYXRhICU+JQ0KICBmaWx0ZXIoU2Vzc289PSJNIikNCg0KZmVtYWxlcyA8LSByZWd1bGFyX2RlbGl2ZXJpZXNfZGF0YSAlPiUNCiAgZmlsdGVyKFNlc3NvPT0iRiIgJiBMdW5naGV6emE+NDAwKQ0KDQoNCiMjIFFRLXBsb3RzIG9mIHRoZSBsZW5ndGgNClFRX2xlbmd0aF9tYWxlcyA9IGdncGxvdChtYWxlcywgYWVzKHNhbXBsZSA9IEx1bmdoZXp6YSkpICsNCiAgbGFicyh0aXRsZT0iUS1RIFBsb3Qgb2YgdGhlIGxlbmd0aFxuIG9mIG1hbGUgbmV3Ym9ybnMiLA0KICAgICAgICAgICAgeD0iVGhlb3JldGljYWwgUXVhbnRpbGVzIiwNCiAgICAgICAgICAgIHk9Ik9ic2VydmVkIFF1YW50aWxlcyIpKw0KICBzdGF0X3FxKGNvbG9yID0gImRvZGdlcmJsdWUiKSArDQogIHN0YXRfcXFfbGluZShjb2xvcj0icmVkIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KUVFfbGVuZ3RoX2ZlbWFsZXMgPSBnZ3Bsb3QoZmVtYWxlcywgYWVzKHNhbXBsZSA9IEx1bmdoZXp6YSkpICsNCiAgbGFicyh0aXRsZT0iUS1RIFBsb3Qgb2YgdGhlIGxlbmd0aFxuIG9mIGZlbWFsZSBuZXdib3JucyIsDQogICAgICAgICAgICB4PSJUaGVvcmV0aWNhbCBRdWFudGlsZXMiLA0KICAgICAgICAgICAgeT0iT2JzZXJ2ZWQgUXVhbnRpbGVzIikrDQogIHN0YXRfcXEoY29sb3IgPSAiaG90cGluayIpICsNCiAgc3RhdF9xcV9saW5lKGNvbG9yPSJibGFjayIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMjIFFRX3Bsb3RzIG9mIHRoZSB3ZWlnaHQNClFRX3dlaWdodF9tYWxlcyA9IGdncGxvdChtYWxlcywgYWVzKHNhbXBsZSA9IFBlc28pKSArDQogIGxhYnModGl0bGU9IlEtUSBQbG90IG9mIHRoZSB3ZWlnaHRcbiBvZiBtYWxlIG5ld2Jvcm5zIiwNCiAgICAgICAgICAgIHg9IlRoZW9yZXRpY2FsIFF1YW50aWxlcyIsDQogICAgICAgICAgICB5PSJPYnNlcnZlZCBRdWFudGlsZXMiKSsNCiAgc3RhdF9xcShjb2xvciA9ICJkb2RnZXJibHVlIikgKw0KICBzdGF0X3FxX2xpbmUoY29sb3I9InJlZCIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNClFRX3dlaWdodF9mZW1hbGVzID0gZ2dwbG90KGZlbWFsZXMsIGFlcyhzYW1wbGUgPSBQZXNvKSkgKw0KICBsYWJzKHRpdGxlPSJRLVEgUGxvdCBvZiB0aGUgd2VpZ2h0XG4gb2YgZmVtYWxlIG5ld2Jvcm5zIiwNCiAgICAgICAgICAgIHg9IlRoZW9yZXRpY2FsIFF1YW50aWxlcyIsDQogICAgICAgICAgICB5PSJPYnNlcnZlZCBRdWFudGlsZXMiKSsNCiAgc3RhdF9xcShjb2xvciA9ICJob3RwaW5rIikgKw0KICBzdGF0X3FxX2xpbmUoY29sb3I9ImJsYWNrIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KUVFfY3JhbmlhbF9tYWxlcyA9IGdncGxvdChtYWxlcywgYWVzKHNhbXBsZSA9IENyYW5pbykpICsNCiAgbGFicyh0aXRsZT0iUS1RIFBsb3Qgb2YgdGhlIGNyYW5pYWwgY2lyY3VtZmVyZW5jZVxuIG9mIG1hbGUgbmV3Ym9ybnMiLA0KICAgICAgICAgICAgeD0iVGhlb3JldGljYWwgUXVhbnRpbGVzIiwNCiAgICAgICAgICAgIHk9Ik9ic2VydmVkIFF1YW50aWxlcyIpKw0KICBzdGF0X3FxKGNvbG9yID0gImRvZGdlcmJsdWUiKSArDQogIHN0YXRfcXFfbGluZShjb2xvcj0icmVkIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KUVFfY3JhbmlhbF9mZW1hbGVzID0gZ2dwbG90KGZlbWFsZXMsIGFlcyhzYW1wbGUgPSBDcmFuaW8pKSArDQogIGxhYnModGl0bGU9IlEtUSBQbG90IG9mIHRoZSBjcmFuaWFsIGNpcmN1bWZlcmVuY2VcbiBvZiBmZW1hbGUgbmV3Ym9ybnMiLA0KICAgICAgICAgICAgeD0iVGhlb3JldGljYWwgUXVhbnRpbGVzIiwNCiAgICAgICAgICAgIHk9Ik9ic2VydmVkIFF1YW50aWxlcyIpKw0KICBzdGF0X3FxKGNvbG9yID0gImhvdHBpbmsiKSArDQogIHN0YXRfcXFfbGluZShjb2xvcj0iYmxhY2siKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQpjb21iX1FRIDwtIFFRX2xlbmd0aF9tYWxlcyArIFFRX2xlbmd0aF9mZW1hbGVzICsNCiAgICAgICAgICAgICAgICAgIFFRX3dlaWdodF9tYWxlcyArIFFRX3dlaWdodF9mZW1hbGVzICsNCiAgICAgICAgICAgICAgICAgIFFRX2NyYW5pYWxfbWFsZXMgKyBRUV9jcmFuaWFsX2ZlbWFsZXMgKw0KICAgICAgICAgICAgICAgICAgcGxvdF9sYXlvdXQobmNvbD0yLCBoZWlnaHRzID0gYyg1LDUsNSksIHdpZHRocz0gYyg1LDUpKQ0KDQpwcmludChjb21iX1FRKQ0KDQoNCnNoYXB0ZXN0TE0gPSBzaGFwaXJvLnRlc3QobWFsZXMkTHVuZ2hlenphKQ0Kc2hhcHRlc3RMRiA9IHNoYXBpcm8udGVzdChmZW1hbGVzJEx1bmdoZXp6YSkNCnNoYXB0ZXN0V00gPSBzaGFwaXJvLnRlc3QobWFsZXMkUGVzbykNCnNoYXB0ZXN0V0YgPSBzaGFwaXJvLnRlc3QoZmVtYWxlcyRQZXNvKQ0Kc2hhcHRlc3RDTSA9IHNoYXBpcm8udGVzdChtYWxlcyRDcmFuaW8pDQpzaGFwdGVzdENGID0gc2hhcGlyby50ZXN0KGZlbWFsZXMkQ3JhbmlvKQ0KDQpjYXQoIlRoZSBtYXhpbXVtIFNoYXBpcm8tV2lsayBzdGF0aXN0aWNzIG9mIHRoZSA2IG5vcm1hbGl0eSB0ZXN0cyBhYm91dCBkaXN0cmlidXRpb24gb2Ygd2VpZ2h0LCBsZW5ndGggYW5kIGNyYW5pYWwgY2lyY3VtZmVyZW5jZSBhY3Jvc3MgdGhlIHR3byBzZXhlcyBpczoiLCBtaW4oc2hhcHRlc3RMTSRzdGF0aXN0aWMsIHNoYXB0ZXN0TEYkc3RhdGlzdGljLCBzaGFwdGVzdFdNJHN0YXRpc3RpYywgc2hhcHRlc3RXRiRzdGF0aXN0aWMsIHNoYXB0ZXN0Q00kc3RhdGlzdGljLCBzaGFwdGVzdENGJHN0YXRpc3RpYyksICJcbiIpDQoNCmNhdCgiVGhlIG1pbmltdW0gU2hhcGlyby1XaWxrIHAudmFsdWUgb2YgdGhlIDYgbm9ybWFsaXR5IHRlc3RzIGFib3V0IGRpc3RyaWJ1dGlvbiBvZiB3ZWlnaHQsIGxlbmd0aCBhbmQgY3JhbmlhbCBjaXJjdW1mZXJlbmNlIGFjcm9zcyB0aGUgdHdvIHNleGVzIGlzOiIsIG1pbihzaGFwdGVzdExNJHAudmFsdWUsIHNoYXB0ZXN0TEYkcC52YWx1ZSwgc2hhcHRlc3RXTSRwLnZhbHVlLCBzaGFwdGVzdFdGJHAudmFsdWUsIHNoYXB0ZXN0Q00kcC52YWx1ZSwgc2hhcHRlc3RDRiRwLnZhbHVlKSkNCmBgYA0KDQpGcm9tIHRoZSBRLVEgcGxvdHMgYW5kIHRoZSBTaGFwaXJvLVdpbGsgdGVzdHMgd2UgY2FuIGFyZ3VlIHRoYXQgdGhlIGFudGhyb3BvbWV0cmljIHZhcmlhYmxlcywgZXZlbiBpZiBub3QgZXhhY3RseSBkaXN0cmlidXRlZCBhY2NvcmRpbmcgdG8gbm9ybWFsIGRpc3RyaWJ1dGlvbnMsIGFyZSByZWFsbHkgY2xvc2UgdG8gbm9ybWFsIGRpc3RyaWJ1dGlvbnMuIFNvIHdlIGNhbiBwZXJmb3JtIGEgdC50ZXN0IHRvIHZlcmlmeSB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBmb2xsb3dlZCBieSBtYWxlcyBhbmQgZmVtYWxlcyBpcyB0aGUgc2FtZS4NCg0KYGBge3J9DQp0dGVzdF9sZW5ndGggPSB0LnRlc3QobWFsZXMkTHVuZ2hlenphLCBmZW1hbGVzJEx1bmdoZXp6YSwgIA0KICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCANCiAgICAgICAgICAgICAgICAgbXUgPSAwLCANCiAgICAgICAgICAgICAgICAgdmFyLmVxdWFsID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUsDQogICAgICAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFKSANCg0KdHRlc3Rfd2VpZ2h0ID0gdC50ZXN0KG1hbGVzJFBlc28sIGZlbWFsZXMkUGVzbywgIA0KICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCANCiAgICAgICAgICAgICAgICAgbXUgPSAwLCANCiAgICAgICAgICAgICAgICAgdmFyLmVxdWFsID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUsDQogICAgICAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFKSANCg0KdHRlc3RfY3Jhbml1bSA9IHQudGVzdChtYWxlcyRDcmFuaW8sIGZlbWFsZXMkQ3JhbmlvLCAgDQogICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsIA0KICAgICAgICAgICAgICAgICBtdSA9IDAsIA0KICAgICAgICAgICAgICAgICB2YXIuZXF1YWwgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBjb25mLmxldmVsID0gMC45NSwNCiAgICAgICAgICAgICAgICAgcGFpcmVkID0gRkFMU0UpIA0KDQpjYXQoIlRoZSBwLXZhbHVlcyBvZiB0aGUgdC10ZXN0cyBhYm91dCBsZW5ndGgsIHdlaWdodCBhbmQgY3JhbmlhbCBjaXJjdW1mZXJlbmNlIGRpc3RyaWJ1dGlvbiBjb21wYXJpc29uIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXMgYXJlOiBcbiIsIA0KICAgICItcF92YWx1ZShXZWlnaHQpID0gIiwgdHRlc3Rfd2VpZ2h0JHAudmFsdWUgLCAiXG4iLA0KICAgICItcF92YWx1ZShMZW5ndGgpID0gIiwgdHRlc3RfbGVuZ3RoJHAudmFsdWUgLCAiXG4iLA0KICAgICItcF92YWx1ZShDcmFuaWFsIGRpYW1ldGVyKSA9ICIsIHR0ZXN0X2NyYW5pdW0kcC52YWx1ZSApDQoNCmBgYA0KVGhlIHZlcnkgc21hbGwgcC12YWx1ZXMgb2YgdGhlIHRocmVlIHQudGVzdHMgc2hvdyB0aGF0IHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgc3RhdGlzdGljYWwgZGlmZmVyZW5jZSBpbiB0aGUgbWVhbiBvZiB0aGUgYW50aHJvcG9tZXRyaWMgdmFyaWFibGVzIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXMuIEluIHBhcnRpY3VsYXIgd2UgY2FuIHJlZnVzZSB0aGUgbnVsbCBoeXBvdGhlc2lzIEgwIGFuZCBhc3NlcnQgdGhhdCB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgYW50aHJvcG9tZXRyaWMgbWVhc3VyZW1lbnRzIGZvbGxvd2VkIGJ5IG1hbGVzIGFuZCBmZW1hbGVzIGFyZSBkaWZmZXJlbnQuDQoNCg0KDQojIyBBbmFseXNpcyBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzDQoNCk5vdyB3ZSBhbmFseXplIHRoZSBudW1iZXIgb2YgY2VzYXJlYW4gZGVsaXZlcmllcyBhY3Jvc3MgdGhlIGRpZmZlcmVudCBob3NwaXRhbHMuDQpGaXJzdCB3ZSBzdHVkeSB0aGUgdGhlIGFic29sdXRlIGFuZCByZWxhdGl2ZSBmcmVxdWVuY3kgb2YgZWFjaCBob3NwaXRhbC4gDQoNCmBgYHtyfQ0KI2NyZWF0ZSBhIGNvbmRpdGlvbmFsIGRhdGFzZXQgd2l0aCB0aGUgbnVtYmVyIG9mIGRlbGl2ZXJpZXMgYW5kIGNlc2FyZWFuIGRlbGl2ZXJpZXMgcGVyIGhvc3BpdGFsDQpjb25kaXRpb25hbF9kYXRhc2V0X29zcGVkYWxlIDwtIA0KICBkYXRhc2V0ICU+JSANCiAgZ3JvdXBfYnkoT3NwZWRhbGUpICU+JSAgDQogIHN1bW1hcmlzZShjZXNhcmVvID0gc3VtKFRpcG8ucGFydG8gPT0gIkNlcyIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBuYXR1cmFsZSA9IHN1bShUaXBvLnBhcnRvID09ICJOYXQiLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgcGFydGk9IHN1bSgpKQ0KYGBgDQoNCmBgYHtyfQ0KRnJlcV9hc3NfaG9zcCA9IHRhYmxlKGRhdGFzZXRbIk9zcGVkYWxlIl0pDQpGcmVxX3JlbF9ob3NwID0gdGFibGUoZGF0YXNldFsiT3NwZWRhbGUiXSkvTl9vYnNlcnZhdGlvbnMNCkZyZXFfaG9zcCA9IGNiaW5kKEZyZXFfYXNzX2hvc3AsRnJlcV9yZWxfaG9zcCkNCnJvd25hbWVzKEZyZXFfaG9zcCkgPC0gYygiSG9zcGl0YWwxIiwiSG9zcGl0YWwyIiwiSG9zcGl0YWwzIikNCmNvbG5hbWVzKEZyZXFfaG9zcCkgPC0gYygiQWJzb2x1dGUgZnJlcXVlbmN5IiwiUmVsYXRpdmUgZnJlcXVlbmN5IikNCmthYmxlKHQoRnJlcV9ob3NwKSwgY2FwdGlvbiA9ICdEZWxpdmVyaWVzIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgaG9zcGl0YWxzJykgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKQ0KYGBgDQpXZSBoYXZlIG9ic2VydmVkIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGVsaXZlcmllcyBhY3Jvc3MgdGhlIHRocmVlIGhvc3BpdGFscyBpcyBlc3NlbnRpYWxseSB1bmlmb3JtLg0KDQpOZXh0IHdlIHVzZSBhIGJveHBsb3QgdG8gZGlzcGxheSB0aGUgcGVyY2VudGFnZSBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIGluIGVhY2ggaG9zcGl0YWwuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0NCg0KaG9zcGl0YWxzPWMoIm9zcDEiID0gIkhvc3BpdGFsMSIsICJvc3AyIj0iSG9zcGl0YWwyIiwgIm9zcDMiPSJIb3NwaXRhbDMiKQ0KDQpkYXRhc2V0JFRpcG8ucGFydG8gPC0gZmFjdG9yKGRhdGFzZXQkVGlwby5wYXJ0bywgbGV2ZWxzID0gYygiTmF0IiwgIkNlcyIpKQ0KDQpiYXJfY2VzYXJlYW5faG9zcGl0YWwgPSAgZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21fYmFyKGFlcyh4PU9zcGVkYWxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1UaXBvLnBhcnRvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb249ImZpbGwiKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFicyh0aXRsZT0iUGVyY2VudGFnZSBvZiBjZXNhcmVhbiBjaGlsZGJpcnRocyBieSBob3NwaXRhbHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHg9IiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT0iUGVyY2VudGFnZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD0idHlwZSBvZiBkZWxpdmVyeToiKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjKCJDZXMiPSJyZWQiLCAiTmF0Ij0iaXZvcnkyIiksIGxhYmVscyA9IGMoIkNlcyI9ICJDZXNhcmVhbiIsICJOYXQiPSJOYXR1cmFsIikpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9jbGFzc2ljKCkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGFuZWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGhqdXN0PTAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMSwgYnkgPSAwLjEpKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWhvc3BpdGFscykNCg0KDQpwcmludChiYXJfY2VzYXJlYW5faG9zcGl0YWwpDQpgYGANClRoZSBib3hwbG90IHNob3cgdGhhdCBpbiBhbGwgdGhlIHRocmVlIGhvc3BpdGFscyB0aGUgcGVyY2VudGFnZSBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIGlzIGFwcHJveGltYXRlbHkgb2YgMzAlLiAgSW4gcGFydGljdWxhciwgdGhlIG51bWJlciBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIHNlZW1zIHRvIGJlIG1hcmdpbmFsbHkgbG93ZXIgaW4gdGhlIEhvc3BpdGFsMyB3aXRoIHJlc3BlY3QgdG8gSG9zcGl0YWwxIGFuZCBIb3NwaXRhbDIuDQoNCg0KIyMjIE5leHQgd2UgdGVzdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIGlzIHRoZSBzYW1lIGFuZCBzbyB1bmlmb3JtIGFjcm9zcyB0aGUgdGhyZWUgaG9zcGl0YWxzDQoNCldlIGZpcnN0IHVzZSBhIFBlYXJzb27igJlzIENoaS1zcXVhcmVkIHRlc3QgdG8gZXhhbWluZSB0aGUgaHlwb3RoZXNpcyB0aGF0DQp0aGUgcHJvcG9ydGlvbiBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIGlzIGVxdWFsIGFjcm9zcyB0aGUgaG9zcGl0YWxzLiBJdA0KY29tcGFyZXMgdGhlIG9ic2VydmVkIGZyZXF1ZW5jaWVzIG9mIGNlc2FyZWFuIGFuZCBuYXR1cmFsIGRlbGl2ZXJpZXMgaW4NCmVhY2ggaG9zcGl0YWwgd2l0aCB0aGUgZXhwZWN0ZWQgZnJlcXVlbmNpZXMgY2FsY3VsYXRlZCB1bmRlciB0aGUgbnVsbA0KaHlwb3RoZXNpcyBvZiB1bmlmb3JtIGRpc3RyaWJ1dGlvbiBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIGFjcm9zcw0KaG9zcGl0YWxzLCBsaXRlcmFsbHk6DQokJEV4cGVjdGVkXF9GcmVxXF9DZXMoSG9zcCkgPSBQcm9iKENlc2FyZWFuKSogTnVtYmVyXF9EZWxpdmVyaWVzKEhvc3ApICQkDQp3aGVyZSAkUHJvYihDZXNhcmVhbik9TlxfdG90KENlc2FyZWFuKS9OXF90b3QoZGVsaXZlcmllcykkLiBUaGUgdGVzdA0KYXNzZXNzZXMgd2hldGhlciB0aGUgb2JzZXJ2ZWQgZGlmZmVyZW5jZXMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQNCmJ5IHByb3ZpZGluZyBhIGNoaS1zcXVhcmVkIHN0YXRpc3RpYyBhbmQgYSBwLXZhbHVlIHRvIHN1cHBvcnQgb3IgcmVqZWN0DQp0aGUgbnVsbCBoeXBvdGhlc2lzLg0KDQpgYGB7cn0NCiMgY3JlYXRlIGNvbnRpbmdlbmN5IHRhYmxlIGJld3R3ZWVuIGhvc3BpdGFscyBhbmQga2luZCBvZiBkZWxpdmVyeQ0KdGFibGVfZGVsaXZlcnlfaG9zcGl0YWwgPSB0YWJsZShPc3BlZGFsZSwgVGlwby5wYXJ0bykNCg0KI1RoZSBQZWFyc29u4oCZcyBDaGktc3F1YXJlZCB0ZXN0IGlzIHBlcmZvcm1lZCANCmNoaXNxX3Rlc3QgPSBjaGlzcS50ZXN0KHRhYmxlX2RlbGl2ZXJ5X2hvc3BpdGFsKQ0KY2F0KCJUaGUgcC12YWx1ZSBvZiB0aGUgUGVhcnNvbidzIENoaS1zcXVhcmVkIHRlc3QgaXMiICwgY2hpc3FfdGVzdCRwLnZhbHVlKQ0KYGBgDQoNCjwhLS0gIyBkYXRhc2V0JFRpcG8ucGFydG8ubnVtID0gaWZlbHNlKFRpcG8ucGFydG89PSJOYXQiLDEsMCkgLS0+DQo8IS0tICMgIC0tPg0KPCEtLSAjICAtLT4NCjwhLS0gIyBwYWlyd2lzZS50LnRlc3QoZGF0YXNldCRUaXBvLnBhcnRvLm51bSwgT3NwZWRhbGUsIC0tPg0KPCEtLSAjICAgICAgICAgICAgICAgICBwYWlyZWQgPSBGLCAtLT4NCjwhLS0gIyAgICAgICAgICAgICAgICAgcG9vbC5zZCA9IFQsIC0tPg0KPCEtLSAjICAgICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAibm9uZSIpIC0tPg0KDQpCYXNlZCBvbiB0aGUgcC12YWx1ZSBvZiB0aGUgUGVhcnNvbidzIENoaS1zcXVhcmVkIHdlIGNhbiBub3QgcmVqZWN0IHRoZSBudWxsLWh5cG90aGVzaXMuDQoNClRoZSB0ZXN0IENoaS1zcXVhcmVkIHRoYXQgd2UgaGF2ZSBwZXJmb3JtZWQgaXMgYmFzZWQgb24gdGhlIGFzc3VtcHRpb24NCnRoYXQgZm9yIHN1ZmZpY2llbnRseSBtYW55IG9ic2VydmF0aW9ucyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZQ0KZnJlcXVlbmNpZXMgb2YgY2VzYXJlYW4gZGVsaXZlcmllcyBhdCBlYWNoIGhvc3BpdGFsIGlzIGNsb3NlIHRvIGEgbm9ybWFsDQpkaXN0cmlidXRpb24sIGJlY2F1c2Ugb2YgdGhlIGNlbnRyYWwgbGltaXQgdGhlb3JlbS4gSG93ZXZlciwgaW4gdGhpcw0KY2FzZSwgd2Uga25vdyB0aGF0IHRoZSBudW1iZXIgb2YgY2VzYXJlYW4gZGVsaXZlcmllcyBhdCB0aGUgJGkkLXRoDQpob3NwaXRhbCBmb2xsb3dzIGEgYmlub21pYWwgZGlzdHJpYnV0aW9uICRCKHBfaSkkLiBTbyB3ZSBjYW4gYWxzbw0KcGVyZm9ybSBhIGJpbm9taWFsIHRlc3QgZm9yIGVhY2ggaG9zcGl0YWwgdG8gZXhhbWluZSB0aGUgaHlwb3RoZXNpcyB0aGF0DQokcF9pPVByb2IoQ2VzYXJlYW4pJCBmb3IgZWFjaCBob3NwaXRhbC4gJFByb2IoQ2VzYXJlYW4pJCBhcyBkZWZpbmVkDQphYm92ZSBpcyBhc3N1bWVkIGFzIGFuIGFjY3VyYXRlIGFwcHJveGltYXRpb24gb2YgcHJvYmFiaWxpdHkgb2YgY2VzYXJlYW4gZGVsaXZlcnkgaWYgdGhlIGRpc3RyaWJ1dGlvbiBpcyB1bmlmb3JtIGFjcm9zcyB0aGUgaG9zcGl0YWxzLCBpbiBwYXJ0aWN1bGFyIHNpbmNlIHRoZSBkaXN0cmlidXRpb24gb2YgZGVsaXZlcmllcyBwZXIgaG9zcGl0YWwgaXMgcXVpdGUgdW5pZm9ybSB0aGVyZSBpcyBubyBuZWVkIHRvIHVzZSBhIHdlaWdodGVkIGZvcm11bGEgZm9yIHRoZSBwcm9iYWJpbGl0eSAkUHJvYihDZXNhcmVhbikkLiBXZSByZXBvcnQgdGhlIHRoZSAkcCQtdmFsdWUgYW5kIHRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIHRoaXMgdGVzdCBmb3IgZWFjaCBob3NwaXRhbC4NCg0KYGBge3J9DQojIGZpcnN0IHdlIGNyZWF0ZSBhIHRhYmxlIHdpdGggdGhlIHRvdGFsIG51bWJlciBvZiBkZWxpdmVyaWVzIGFuZCBjZXNhcmVhbiBkZWxpdmVyaWVzIG9ic2VydmVkIGF0IGVhY2ggaG9zcGl0YWwuDQoNCnN1Yl9kYXRhc2V0ID0gc3Vic2V0KGRhdGFzZXQsIE9zcGVkYWxlPT0ib3NwMSIpDQpOX2Nlc19ob3NwID0gc3VtKHN1Yl9kYXRhc2V0JFRpcG8ucGFydG89PSJDZXMiKQ0KTl9kZWxpdmVyaWVzX2hvc3AgPSBucm93KHN1Yl9kYXRhc2V0KQ0KDQpGcmVxX2Nlc19ob3NwID0gdGFibGUoT3NwZWRhbGVbVGlwby5wYXJ0bz09IkNlcyJdKQ0KQ2VzX2hvc3AgPSBjYmluZChGcmVxX2Fzc19ob3NwLEZyZXFfY2VzX2hvc3ApDQpDZXNfaG9zcCA9IGFzLmRhdGEuZnJhbWUoQ2VzX2hvc3ApDQoNCnJvd25hbWVzKENlc19ob3NwKSA8LSBjKCJIb3NwaXRhbF8xIiwgIkhvc3BpdGFsXzIiLCAiSG9zcGl0YWxfMyIpDQoNCiMgTmV4dCB3ZSBjb21wdXRlIFByb2IoY2VzYXJlYW4pIHRoYXQgc2hvdWxkIGJlIGEgZ29vZCBhcHByb3hpbWF0aW9uIG9mIHRoZSBwcm9iYWJpbGl0eSBvZiBvYmVydmluZyBhIGNlc2FyZWFuIGRlbGl2ZXJ5IGFzc3VtaW5nIHRoaXMgaXMgdW5pZm9ybSBhY3Jvc3MgdGhlIHRocmVlIGhvc3BpdGFscy4NCg0KcHJvYl9jZXNhcmVhbiA9IHN1bShUaXBvLnBhcnRvPT0iQ2VzIikvTl9vYnNlcnZhdGlvbnMNCg0KIyBMYXN0IHdlIHBlcmZvcm0gYSBiaW5vbWlhbCB0ZXN0IGZvciBlYWNoIGhvc3BpdGFsDQpwX3ZhbHVlcyA8LSBudW1lcmljKG5yb3coQ2VzX2hvc3ApKQ0KY29uZl9pbnQgPC0gbnVtZXJpYyhucm93KENlc19ob3NwKSkNCg0KZm9yKGkgaW4gc2VxX2xlbihucm93KENlc19ob3NwKSkpew0KICB0ZXN0IDwtIGJpbm9tLnRlc3QoQ2VzX2hvc3AkRnJlcV9jZXNfaG9zcFtpXSwgQ2VzX2hvc3AkRnJlcV9hc3NfaG9zcFtpXSwgcCA9IHByb2JfY2VzYXJlYW4sIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpDQogIHBfdmFsdWVzW2ldIDwtIHRlc3QkcC52YWx1ZQ0KICBjb25mX2ludFtpXSA8LSBwYXN0ZTAoIigiLCByb3VuZCh0ZXN0JGNvbmYuaW50WzFdLDIpLCAiLCIsIHJvdW5kKHRlc3QkY29uZi5pbnRbMl0sMiksIikiKSAgDQp9DQoNCkNlc19ob3NwJHBfdmFsdWUgPC0gcF92YWx1ZXMNCkNlc19ob3NwJGNvbmZfaW50IDwtIGNvbmZfaW50DQoNCmthYmxlKHQoQ2VzX2hvc3BbLCBjKCJwX3ZhbHVlIiwgImNvbmZfaW50IildKSwgY2FwdGlvbiA9ICdQLXZhbHVlcyBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSBiaW5vbWlhbCBvdXRjb21lcyBvZiBlYWNoIGhvc3BpdGFsLDFuIGNhbGN1bGF0ZWQgdW5kZXIgdGhlIGFzc3VtcHRpb24gb2YgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24gd2l0aCBhIHVuaWZvcm0gcHJvYmFiaWxpdHlcbiBvZiBjZXNhcmVhbiBkZWxpdmVyaWVzIGFjcm9zcyBhbGwgaG9zcGl0YWxzLicpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBwb3NpdGlvbj0ibGVmdCIpDQpgYGANCkFsc28gaW4gdGhpcyBjYXNlIHRoZSBwLXZhbHVlIG9mIHRoZSBiaW5vbWlhbCB0ZXN0cyBkbyBub3QgYWxsb3cgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYXNzZXJ0aW5nIHRoYXQgdGhlIG51bWJlciBvZiBkZWxpdmVyaWVzIGFjcm9zcyB0aGUgdGhyZWUgaG9zcGl0YWxzIGlzIHVuaWZvcm0uDQoNCg0KIyMgTmV4dCB3ZSBhbmFseXplIHRoZSBmcmVxdWVuY3kgb2Ygc21va2luZyBtb3RoZXJzLg0KDQoNCmBgYHtyfQ0KRnJlcV9yZWxfc21va2VyID0gc3VtKGRhdGFzZXQkRnVtYXRyaWNpID09IDEpL05fb2JzZXJ2YXRpb25zDQoNCmNhdCgiVGhlIHBlcmNlbnRhZ2Ugb2Ygc21va2luZyBtb3RoZXJzIGlzOiIsIHBhc3RlMChGcmVxX3JlbF9zbW9rZXIsIiUiKSkNCmBgYA0KTW9yZW92ZXIgd2UgaW52ZXN0aWdhdGUgaWYgdGhlIHNtb2tlIGluZmx1ZW5jZSBzb21lIG9mIHRoZSBvdGhlciB2YXJpYWJsZXMsIGluY2x1ZGluZyB3ZWlnaHQsIGxlbmd0aCBhbmQgQ3JhbmlhbCBjaXJjdW1mZXJlbmNlIGFuZCBnZXN0YXRpb25hbCBhZ2UuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQoNCmJveHBsb3Rfc21va2luZ18xPSBnZ3Bsb3QoZGF0YT1kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHg9RnVtYXRyaWNpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9UGVzbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsPUZ1bWF0cmljaSkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJXZWlnaHQgZGlzdHJpYnV0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeD0iICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9IndlaWdodCAoa2cpIikrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoIjAiPSJncmVlbiIsICIxIj0iZ3JleSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiPSAiTm9uLXNtb2tlciIsICIxIj0iU21va2VyIikpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwYW5lbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGhqdXN0PTAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzZXEoMSw1LDAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgxMDAwLCA1MDAwLCBieSA9IDUwMCkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiTm9uLXNtb2tlciIsICJTbW9rZXIiKSkNCg0KDQpib3hwbG90X3Ntb2tpbmdfMiA9IGdncGxvdChkYXRhPWRhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1GdW1hdHJpY2ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT1MdW5naGV6emEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsPUZ1bWF0cmljaSkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJMZW5ndGggZGlzdHJpYnV0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeD0iICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9Ikxlbmd0aCAoY20pIikrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoIjAiPSJncmVlbiIsICIxIj0iZ3JleSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiPSAiTm9uLXNtb2tlciIsICIxIj0iU21va2VyIikpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwYW5lbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGhqdXN0PTAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMzAwLDYwMCw1MCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKDMwLCA2MCwgNSkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiTm9uLXNtb2tlciIsICJTbW9rZXIiKSkNCg0KDQpib3hwbG90X3Ntb2tpbmdfMyA9IGdncGxvdChkYXRhPWRhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1GdW1hdHJpY2ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT1DcmFuaW8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsPUZ1bWF0cmljaSkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJDcmFuaWFsIGNpcmN1bWZlcmVuY2UgZGlzdHJpYnV0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeD0iICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9IkNyYW5pYWwgY2lyY3VtZmVyZW5jZSAoY20pIikrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoIjAiPSJncmVlbiIsICIxIj0iZ3JleSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiPSAiTm9uLXNtb2tlciIsICIxIj0iU21va2VyIikpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwYW5lbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGhqdXN0PTAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzZXEoMjUsNDAsNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgyNTAsIDQwMCwgYnkgPSA1MCkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiTm9uLXNtb2tlciIsICJTbW9rZXIiKSkNCg0KDQoNCmJveHBsb3Rfc21va2luZ180ID0gZ2dwbG90KGRhdGE9ZGF0YXNldCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4PUZ1bWF0cmljaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PUdlc3RhemlvbmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsPUZ1bWF0cmljaSkpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJHZXN0YXRpb25hbCB0aW1lIGRpc3RyaWJ1dGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHg9IiAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PSJHZXN0YXRpb25hbCB0aW1lIikrDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoIjAiPSJncmVlbiIsICIxIj0iZ3JleSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiPSAiTm9uLXNtb2tlciIsICIxIj0iU21va2VyIikpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwYW5lbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGhqdXN0PTAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjYsIDQyLCBieSA9IDIpKSsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIk5vbi1zbW9rZXIiLCAiU21va2VyIikpDQoNCmNvbWJfc21va2UgPC0gYm94cGxvdF9zbW9raW5nXzEgKyBib3hwbG90X3Ntb2tpbmdfMiArIGJveHBsb3Rfc21va2luZ18zICsgYm94cGxvdF9zbW9raW5nXzQgKyAgICBwbG90X2xheW91dChuY29sPTIsIGhlaWdodHMgPSBjKDUsNSksIHdpZHRocz0gYyg1LDUpKQ0KDQpwcmludChjb21iX3Ntb2tlKQ0KDQpgYGANClRoZSBib3hwbG90IG1heSBzdWdnZXN0IGEgZGlmZmVyZW5jZSBpbiB0aGUgbWVhc3VyZW1lbnRzIGFuZCBnZXN0YXRpb25hbCB0aW1lIGJldHdlZW4gc21va2luZyBhbmQgbm9uIHNtb2tpbmcgbW90aGVycy4gV2UgdGVzdCB0aGlzIGh5cG90aGVzaXMgdXNpbmcgYSB0LnRlc3QuDQoNCg0KYGBge3J9DQpTbW9rZXJzX2RhdGEgPC0gZGF0YXNldCAlPiUNCiAgZmlsdGVyKEZ1bWF0cmljaT09IjEiKQ0KDQpOb3Rfc21va2Vyc19kYXRhIDwtIGRhdGFzZXQgJT4lDQogIGZpbHRlcihGdW1hdHJpY2k9PSIwIikNCg0KdHRlc3Rfc21va2Vfd2VpZ2h0ID0gdC50ZXN0KFNtb2tlcnNfZGF0YSRQZXNvLCBOb3Rfc21va2Vyc19kYXRhJFBlc28sICANCiAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwgDQogICAgICAgICAgICAgICAgIG11ID0gMCwgDQogICAgICAgICAgICAgICAgIHZhci5lcXVhbCA9IFRSVUUsDQogICAgICAgICAgICAgICAgIGNvbmYubGV2ZWwgPSAwLjk1LA0KICAgICAgICAgICAgICAgICBwYWlyZWQgPSBGQUxTRSkgDQoNCnR0ZXN0X3Ntb2tlX2xlbmd0aCA9IHQudGVzdChTbW9rZXJzX2RhdGEkTHVuZ2hlenphLCBOb3Rfc21va2Vyc19kYXRhJEx1bmdoZXp6YSwgIA0KICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCANCiAgICAgICAgICAgICAgICAgbXUgPSAwLCANCiAgICAgICAgICAgICAgICAgdmFyLmVxdWFsID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUsDQogICAgICAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFKSANCg0KDQp0dGVzdF9zbW9rZV9jcmFuaXVtID0gdC50ZXN0KFNtb2tlcnNfZGF0YSRDcmFuaW8sIE5vdF9zbW9rZXJzX2RhdGEkQ3JhbmlvLCAgDQogICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsIA0KICAgICAgICAgICAgICAgICBtdSA9IDAsIA0KICAgICAgICAgICAgICAgICB2YXIuZXF1YWwgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBjb25mLmxldmVsID0gMC45NSwNCiAgICAgICAgICAgICAgICAgcGFpcmVkID0gRkFMU0UpIA0KDQp0dGVzdF9zbW9rZV9nZXN0YXRpb24gPSB0LnRlc3QoU21va2Vyc19kYXRhJEdlc3RhemlvbmUsIE5vdF9zbW9rZXJzX2RhdGEkR2VzdGF6aW9uZSwgIA0KICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCANCiAgICAgICAgICAgICAgICAgbXUgPSAwLCANCiAgICAgICAgICAgICAgICAgdmFyLmVxdWFsID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUsDQogICAgICAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFKSANCg0KY2F0KCJUaGUgcC12YWx1ZXMgb2YgdGhlIHQtdGVzdHMgYWJvdXQgbGVuZ3RoLCB3ZWlnaHQsIGNyYW5pYWwgY2lyY3VtZmVyZW5jZSBhbmQgZ2VzdGF0aW9uIHRpbWUgZGlzdHJpYnV0aW9uIGNvbXBhcmlzb24gYmV0d2VlbiBzbW9raW5nIGFuZCBub24tc21va2luZyBtb3RoZXJzIGFyZTogXG4iLCANCiAgICAiLXBfdmFsdWUoV2VpZ2h0KSA9ICIsIHR0ZXN0X3Ntb2tlX3dlaWdodCRwLnZhbHVlICwgIlxuIiwNCiAgICAiLXBfdmFsdWUoTGVuZ3RoKSA9ICIsIHR0ZXN0X3Ntb2tlX2xlbmd0aCRwLnZhbHVlICwgIlxuIiwNCiAgICAiLXBfdmFsdWUoQ3JhbmlhbCBkaWFtZXRlcikgPSAiLCB0dGVzdF9zbW9rZV9jcmFuaXVtJHAudmFsdWUsICJcbiIsDQogICAgIi1wX3ZhbHVlKENyYW5pYWwgZGlhbWV0ZXIpID0gIiwgdHRlc3Rfc21va2VfZ2VzdGF0aW9uJHAudmFsdWUpDQpgYGANClRoZSB0LnRlc3RzIGRvIG5vdCBhbGxvdyB1cyB0byByZWplY3QgdGhlIGh5cG90aGVzaXMgdGhhdCB0aGUgZ2VzdGF0aW9uYWwgdGltZSBhbmQgYW50cm9wb21ldHJpYyBtZWFzdXJtZW50cyBvZiBuZXdib3JucyBhcmUgc3RhdGlzdGljYWxseSBkaWZmZXJlbnQgYWNjb3JkaW5nIHRvIHNtb2tpbmcgYW5kIG5vdC1zbW9raW5nIG1vdGhlcnMuDQoNCg0KIyMgTmV4dCB3ZSBhbmFseXplIHRoZSBudW1iZXIgb2YgZGVsaXZlcmllcyBhbmQgcmVsYXRpb24gd2l0aCB0aGUgYWdlIG9mIHRoZSBtb3RoZXJzLg0KDQpGaXJzdCB3ZSBleHBsb3JlIHRoZSB2YXJpYWJsZSBudW1iZXIgb2YgZGVsaXZlcmllcy4NCg0KYGBge3J9DQpGcmVxX2Fic19OLmRlbGl2ZXJpZXMgPSB0YWJsZShkYXRhc2V0WyJOLmdyYXZpZGFuemUiXSkgDQpGcmVxX3JlbF9OLmRlbGl2ZXJpZXMgPSB0YWJsZShkYXRhc2V0WyJOLmdyYXZpZGFuemUiXSkvTl9vYnNlcnZhdGlvbnMNCg0KRnJlcV9OLmRlbGl2ZXJpZXM9ZGF0YS5mcmFtZShOLmdyYXZpZGFuemUgPSBuYW1lcyhGcmVxX2Fic19OLmRlbGl2ZXJpZXMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGcmVxX2FicyA9IGFzLm51bWVyaWMoRnJlcV9hYnNfTi5kZWxpdmVyaWVzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRnJlcV9yZWwgPSBwYXN0ZShyb3VuZChGcmVxX3JlbF9OLmRlbGl2ZXJpZXMsMiksIiUiKSkNCg0KRnJlcV9OLmRlbGl2ZXJpZXMkTi5ncmF2aWRhbnplIDwtIGZhY3RvcihGcmVxX04uZGVsaXZlcmllcyROLmdyYXZpZGFuemUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1hcy5jaGFyYWN0ZXIoc29ydChhcy5udW1lcmljKHVuaXF1ZShGcmVxX04uZGVsaXZlcmllcyROLmdyYXZpZGFuemUpKSkpKQ0KDQoNCmdncGxvdChkYXRhPUZyZXFfTi5kZWxpdmVyaWVzKSsNCiAgZ2VvbV9iYXIoYWVzKHg9Ti5ncmF2aWRhbnplLCB5PUZyZXFfYWJzKSwNCiAgICAgICAgICAgc3RhdD0iaWRlbnRpdHkiLA0KICAgICAgICAgICBjb2w9ImFxdWFtYXJpbmUiLA0KICAgICAgICAgICBmaWxsPSJhcXVhbWFyaW5lIikrDQogIGxhYnModGl0bGU9Ik51bWJlciBvZiBwcmV2aW91cyBwcmVnbmFuY2llcyIsDQogICAgICAgeD0iIiwNCiAgICAgICB5PSJhYnNvbHV0ZSBmcmVxdWVuY3kiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMDAsMjAwKSkrDQogIGdlb21fdGV4dChhZXMoeD1OLmdyYXZpZGFuemUsIHk9RnJlcV9hYnMrNTAsIGxhYmVsID0gRnJlcV9yZWwpKSsNCiAgdGhlbWVfY2xhc3NpYygpKw0KICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSkNCmBgYA0KDQpXZSBvYnNlcnZlIHRoYXQgbW9zdCBvZiBkZWxpdmVyaWVzIGNvcnJlc3BvbmQgdG8gZmlyc3QgZGVsaXZlcmllcyBhbmQgdGhhdCBsZXNzIHRoYW4gMTAlIG9mIHRoZSBtb3RoZXJzIGhhdmUgNCBjaGlsZHJlbiBvciBtb3JlLg0KDQoNCk5leHQgd2UgaW52ZXN0aWdhdGUgaWYgdGhlcmUgaXMgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgY2hpbGRyZW4gYW5kIHRoZSBhZ2Ugb2YgdGhlIG1vdGhlci4gVG8gdGhpcyBlbmQgd2Ugc3BsaXQgdGhlIGRhdGFzZXQgaW4gY2xhc3NlcyBhY2NvcmRpbmcgdG8gdGhlIE51bWJlciBvZiBkZWxpdmVyaWVzIGFuZCB5ZWFycyBvZiB0aGUgbW90aGVyIGFuZCB3ZSBkaXNwbGF5IHRoZSBkc3RyaWJ1dGlvbiBhY2NvcmRpbmcgdG8gdGhlIGNyb3NzIGNsYXNzZXMuDQoNCmBgYHtyfQ0KZGF0YXNldCROLmdyYXZpZGFuemVfQ0wgPC0gY3V0KGRhdGFzZXQkTi5ncmF2aWRhbnplLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1jKDAsMSwyLDMsMTAwKSwgDQogICAgICAgICAgICAgICAgICAgICByaWdodD1GQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMCIsICIxIiwiMiIsIjMrIikpDQoNCg0KZGF0YXNldCRBbm5pLm1hZHJlX0NMIDwtIGN1dChkYXRhc2V0JEFubmkubWFkcmUsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPWMoMTEsMjAsMjYsMzIsMzgsNDYpKQ0KDQpkYXRhc2V0JE4uZ3JhdmlkYW56ZV9DTCA8LSBjdXQoZGF0YXNldCROLmdyYXZpZGFuemUsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPWMoMCwxLDIsMywxMDApLCANCiAgICAgICAgICAgICAgICAgICAgIHJpZ2h0PUZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCIwIiwiMSIsIjIiLCIzKyIpKQ0KDQpjb250X3RhYmxlX3llYXJfTi5kZWxpdmVyeSA9IHRhYmxlKGRhdGFzZXQkQW5uaS5tYWRyZV9DTCwgZGF0YXNldCROLmdyYXZpZGFuemVfQ0wpDQoNCmdncHVicjo6Z2diYWxsb29ucGxvdChkYXRhPWFzLmRhdGEuZnJhbWUoY29udF90YWJsZV95ZWFyX04uZGVsaXZlcnkpLA0KICAgICAgICAgICAgICAgICAgICAgIGZpbGw9ImtoYWtpIikNCmBgYA0KDQo8IS0tICMgIC0tPg0KPCEtLSAjICAtLT4NCjwhLS0gIyBmcmVxX2Fzc19hZ2UgPC0gdGFibGUoZGF0YXNldCRBbm5pLm1hZHJlX0NMKSAtLT4NCjwhLS0gIyAgLS0+DQo8IS0tICMgY291bnQgPC0gc3VtKChkYXRhc2V0JEFubmkubWFkcmUgPiAxMCkgJiAoZGF0YXNldCRBbm5pLm1hZHJlIDw9IDQ2KSkgLS0+DQo8IS0tICMgIC0tPg0KPCEtLSAjIGZyZXFfcmVsX2FnZSA8LSByb3VuZChmcmVxX2Fzc19hZ2UvY291bnQsMykgLS0+DQo8IS0tICMgIC0tPg0KPCEtLSAjIGZyZXFfYWdlPWFzLmRhdGEuZnJhbWUoY2JpbmQoZnJlcV9hc3NfYWdlLCBmcmVxX3JlbF9hZ2UpKSAtLT4NCjwhLS0gIyAgLS0+DQo8IS0tICMga2FibGUoZnJlcV9hZ2UsIGNhcHRpb249IkRpc3RyaWJ1dGlvbiBvZiB0aGUgZGVsaXZlcmllcyBmb3IgY2xhc3NlcyBvZiBhZ2UiKSAlPiUgLS0+DQo8IS0tICMgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBwb3NpdGlvbj0iY2VudGVyIikgLS0+DQoNCg0KPCEtLSBnZ3Bsb3QoZGF0YSA9IHN1YnNldChkYXRhc2V0LCAhaXMubmEoQW5uaS5tYWRyZV9DTCkpKSsgLS0+DQo8IS0tICAgZ2VvbV9iYXIoYWVzKHg9QW5uaS5tYWRyZV9DTCksIC0tPg0KPCEtLSAgICAgICAgICAgIHN0YXQ9ImNvdW50IiwgLS0+DQo8IS0tICAgICAgICAgICAgY29sPSJraGFraSIsIC0tPg0KPCEtLSAgICAgICAgICAgIGZpbGw9ImtoYWtpIikrIC0tPg0KPCEtLSAgIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiB0aGUgbW90aGVyIGFnZSIsIC0tPg0KPCEtLSAgICAgICAgeD0iQWdlIiwgLS0+DQo8IS0tICAgICAgICB5PSJhYnNvbHV0ZSBmcmVxdWVuY3kiKSsgLS0+DQo8IS0tICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMDAsMjAwKSkrIC0tPg0KPCEtLSAgIHRoZW1lX2NsYXNzaWMoKSsgLS0+DQo8IS0tICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLCAtLT4NCjwhLS0gICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSkpIC0tPg0KDQoNCkFzIGV4cGVjdGVkIHdlIG9ic2VydmUgdGhhdCBhbiBpbmNyZWFzaW5nIG51bWJlciBvZiBjaGlsZHJlbiB1c3VhbGx5IGNvcnJlc3BvbmQgdG8gb2xkZXIgbW90aGVycy4gV2UgdGVzdCB0aGUgaHlwb3RoZXNpcyB0aGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBudW1iZXIgb2YgY2hpbGRlciBpcyBub3QgdW5pZm9ybSBhY3Jvc3MgdGhlIGFnZXMgdmlhIGEgUGVhcnNvbiBjaGktc3F1YXJlZCB0ZXN0Lg0KDQpgYGB7cn0NCmNoaXNxX3Rlc3QgPSBjaGlzcS50ZXN0KGNvbnRfdGFibGVfeWVhcl9OLmRlbGl2ZXJ5KQ0KY2F0KCJUaGUgcC12YWx1ZSBvZiB0aGUgUGVhcnNvbidzIENoaS1zcXVhcmVkIHRlc3QgaXMiICwgY2hpc3FfdGVzdCRwLnZhbHVlKSANCmBgYA0KDQpUaGUgUGVhcnNvbidzIENoaSBzcXVhcmVkIHRlc3QgY29uZmlybXMgdGhlIGh5cG90aGVzaXMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIG51bWJlciBvZiBkZWxpdmVyaWVzIHBlciBhZ2UgaXMgbm90IHVuaWZvcm0uIA0KDQojIyBDcmVhdGlvbiBvZiBhIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIHRoZSB3ZWlnaHQgb2YgdGhlIG5ld2Jvcm4uDQpXZSBzdGFydCBzdHVkeWluZyB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIGRpZmZlcmVudCB2YXJpYWJsZXMgYW5kIHRoZSB2YXJpYWJsZSB3ZWlnaHQuIEZpcnN0IHdlIGRpc3BsYXkgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZSBkaWZmZXJlbnQgcXVhbnRpdGF0aXZlIHZhcmlhYmxlIGFuZCB0aGUgdmFyaWFibGUgd2VpZ2h0Lg0KDQpgYGB7cn0NCiMgcGFuZWwuY29yIDwtIGZ1bmN0aW9uKHgsIHksIGRpZ2l0cyA9IDIsIHByZWZpeCA9ICIiLCBjZXguY29yLCAuLi4pDQojIHsNCiMgICAjdXNyIDwtIHBhcigidXNyIik7IG9uLmV4aXQocGFyKHVzcikpDQojICAgI3Bhcih1c3IgPSBjKDAsIDEsIDAsIDEpKQ0KIyAgIHIgPC0gKGNvcih4LCB5KSkNCiMgICB0eHQgPC0gZm9ybWF0KGMociwgMSksIGRpZ2l0cyA9IGRpZ2l0cylbMV0NCiMgICB0eHQgPC0gcGFzdGUwKHByZWZpeCwgdHh0KQ0KIyAgIGlmKG1pc3NpbmcoY2V4LmNvcikpIGNleC5jb3IgPC0gMC44L3N0cndpZHRoKHR4dCkNCiMgICB0ZXh0KDAuNSwgMC41LCB0eHQsIGNleCA9IDEuNSkNCiMgfQ0KDQpwYW5lbC5jb3IgPC0gZnVuY3Rpb24oeCwgeSwgZGlnaXRzID0gMiwgcHJlZml4ID0gIiIsIGNleC5jb3IsIC4uLikNCnsNCiAgciA8LSBjb3IoeCwgeSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQogIHR4dCA8LSBmb3JtYXQoYyhyLCAxKSwgZGlnaXRzID0gZGlnaXRzKVsxXQ0KICB0eHQgPC0gcGFzdGUwKHByZWZpeCwgdHh0KQ0KICANCiAgdXNyIDwtIHBhcigidXNyIikgIyBsZWdnaSBjb29yZGluYXRlIGNvcnJlbnRpDQogIHhfcG9zIDwtIG1lYW4odXNyWzE6Ml0pICAjIGNlbnRybyBhc3NlIHgNCiAgeV9wb3MgPC0gbWVhbih1c3JbMzo0XSkgICMgY2VudHJvIGFzc2UgeQ0KICANCiAgaWYobWlzc2luZyhjZXguY29yKSkgY2V4LmNvciA8LSAxMC9zdHJ3aWR0aCh0eHQpDQogIHRleHQoeF9wb3MsIHlfcG9zLCB0eHQsIGNleCA9IDEuNSwgLi4uKQ0KfQ0KDQoNCiNjb3JyZWxhemlvbmkNCnBhaXJzKGRhdGFzZXRbLCBjKCJBbm5pLm1hZHJlIiwiTi5ncmF2aWRhbnplIiwgIkdlc3RhemlvbmUiLCAiUGVzbyIsICJMdW5naGV6emEiLCAiQ3JhbmlvIildLCBsb3dlci5wYW5lbD1wYW5lbC5jb3IsIHVwcGVyLnBhbmVsPXBhbmVsLnNtb290aCkNCmBgYA0KDQpUaGUgY29ycmVsYXRpb24gbWF0cml4IHN1Z2dlc3QgYSBtZWRpdW0gdG8gaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIFdlaWdodCBhbmQgTGVuZ3RoL0NyYW5pdW0gY2lyY3VtZmVyZW5jZSBhbmQgZ2VzdGF0aW9uIHRpbWUuIERpZmZlcmVudGx5LCB0aGUgd2VpZ2h0IHNlZW1zIG5vdCB0byBiZSBjb3JyZWxhdGVkIHRvIHRoZSBudW1iZXIgb2YgZGVsaXZlcmllcyBhbmQgYWdlIG9mIHRoZSBtb3RoZXIuIEFkZGl0aW9uYWxseSB3ZSBoYXZlIGFscmVhZHkgb2JzZXJ2ZWQgdGhhdCB0aGVyZSBpcyBub3QgYSBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB3ZWlnaHQgb2YgdGhlIG5ld2Jvcm4gYW5kIHRoZSBmYWN0IHRoYXQgdGhlIG1vdGhlciBpcyBhIHNtb2tlci4gRGlmZmVyZW50bHkgd2UgaGF2ZSBvYnNlcnZlZCBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBzZXggb2YgdGhlIG5ld2Jvcm4gYW5kIGl0cyB3ZWlnaHQuIA0KDQpUaGUgc2NhdHRlciBwbG90cyBhbHNvIHN1Z2dlc3QgYSBwb3NzaWJsZSBub25saW5lYXIgZGVwZW5kYW5jZSBvZiB0aGUgd2VpZ2h0IG9mIHRoZSBuZXdib3JucyB3aXRoIHJlc3BlY3QgdG8gZ2VzdGF0aW9uIHRpbWUgYW5kIHBvc3NpYmx5IExlbmd0aCBhbmQgY3Jhbml1bSBjaXJjdW1mZXJlbmNlLg0KDQpFdmVuIGlmIHRoaXMgaXMgbm90IG9ic2VydmFibGUgZnJvbSB0aGUgY29ycmVsYXRpb24gbWF0cml4LCB0aGUgbGl0ZXJhdHVyZSBzdWdnZXN0IHRoYXQgdGhlcmUgY291bGQgYmUgYSBkaWZmZXJlbmNlIGluIHRoZSB3ZWlnaHQgb2YgdGhlIG5ld2Jvcm5zIGJldHdlZW4gdGhlIGZpcnN0IGNoaWxkIGFuZCB0aGUgc3Vic2VxdWVudCBvbmVzIChzZWUgW0ludGVybmF0aW9uYWwgc3RhbmRhcmRzIGZvciBuZXdib3JuIHdlaWdodCwgbGVuZ3RoLCBhbmQgaGVhZCBjaXJjdW1mZXJlbmNlIGJ5IGdlc3RhdGlvbmFsIGFnZSBhbmQgc2V4XShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9TMDE0MC02NzM2KDE0KTYwOTMyLTYpKSwgZm9yIHRoaXMgcmVhc29uIHdlIHNwbGl0IHRoZSBkYXRhc2V0IGFjY29yZGluZyB0byB0aGUgTnVtYmVyIG9mIGRlbGl2ZXJ5IGNsYXNzZXMgIjAiIGFuZCAiMSsiIGFuZCB1c2UgdGhpcyBhcyBhIHF1YWxpdGF0aXZlIHZhcmlhYmxlIGluIHRoZSBpbnZlc3RpZ2F0aW9uIG9mIGEgZ29vZCByZWdyZXNzaW9uIG1vZGVsIGZvIHRoZSB3ZWlnaHQgb2YgdGhlIG5ld2Jvcm5zLg0KDQpgYGB7cn0NCmRhdGFzZXQkTi5ncmF2aWRhbnplX0NMIDwtIGN1dChkYXRhc2V0JE4uZ3JhdmlkYW56ZSwgDQogICAgICAgICAgICAgICAgICAgICBicmVha3M9YygwLDEsMTAwKSwgDQogICAgICAgICAgICAgICAgICAgICByaWdodD1GQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMCIsIjErIikpDQpgYGANCg0KDQo8IS0tIGdncGxvdChkYXRhPWRhdGFzZXQsIC0tPg0KPCEtLSAgICAgICAgYWVzKHg9Ti5ncmF2aWRhbnplX0NMLCAtLT4NCjwhLS0gICAgICAgICAgICB5PVBlc28sIC0tPg0KPCEtLSAgICAgICAgICAgIGZpbGw9Ti5ncmF2aWRhbnplX0NMKSkrIC0tPg0KPCEtLSAgICAgICAgZ2VvbV9ib3hwbG90KGNvbG9yPSJibGFjayIpKyAtLT4NCjwhLS0gICAgICAgIGxhYnModGl0bGU9IldlaWdodCBkaXN0cmlidXRpb24gYWNjb3JkaW5nIHRvIE51bWJlciBvZiBkZWxpdmVyaWVzIiwgLS0+DQo8IS0tICAgICAgICAgICAgIHg9IiAiLCAtLT4NCjwhLS0gICAgICAgICAgICAgeT0id2VpZ2h0IChrZykiKSsgLS0+DQo8IS0tICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoIjAiPSJhcXVhbWFyaW5lIiwgIjErIj0iYXF1YW1hcmluZTMiKSkrIC0tPg0KPCEtLSAgICAgICAgdGhlbWVfY2xhc3NpYygpKyAtLT4NCjwhLS0gICAgICAgIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGFuZWwiLCAtLT4NCjwhLS0gICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNiwgaGp1c3Q9MC41KSwgLS0+DQo8IS0tICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksIC0tPg0KPCEtLSAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLCAtLT4NCjwhLS0gICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLCAtLT4NCjwhLS0gICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLCAtLT4NCjwhLS0gICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrIC0tPg0KPCEtLSAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNlcSgxLDUsMC41KSwgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgxMDAwLCA1MDAwLCBieSA9IDUwMCkpKyAtLT4NCjwhLS0gICAgICAgICAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjFzdCIsICIybmQiLCAiM3JkKyIpKSAtLT4NCg0KDQoNCjwhLS0gYGBge3J9IC0tPg0KPCEtLSBwYWlyd2lzZS50LnRlc3QoZGF0YXNldCRQZXNvLCBkYXRhc2V0JE4uZ3JhdmlkYW56ZV9DTCwgcC5hZGp1c3QubWV0aG9kID0gImJvbmZlcnJvbmkiKSAgLS0+DQo8IS0tIGBgYCAtLT4NCg0KDQpXZSBpbnZlc3RpYWd0ZSB0aGUgYmVzdCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBhIHN0ZXB3aXNlIHNlbGVjdGlvbiBwcm9jZWR1cmUuIFdlIHN0YXJ0IGZyb20gdGhlIGZ1bGwgbW9kZWwgZnJvbSB3aGljaCB3ZSBvbWl0IHRoZSB2YXJpYWJsZXMgSG9zcGl0YWwgYW5kIEtpbmQgb2YgZGVsaXZlcnkgc2luY2UgdGhlcmUgaXMgbm8gcmVhc29uIHdoeSB0aGVzZSB2YXJpYWJsZXMgc2hvdWxkIGluZmx1ZW5jZSB0aGUgd2VpZ2h0IG9mIHRoZSBuZXdib3JuLiBUaGVuIHdlIHN1YnRyYWN0IG9yIGFkZCBiYWNrIHRoZSB2YXJpYWJsZXMgdGhhdCBzZWVtcyBsZXNzIGluZmx1ZW50aWFsLiBGaW5hbGx5IHdlIHNlbGVjdCB0aGUgYmVzdCBtb2RlbCBiYXNlZCBvbiB0aGUgQklDIG1ldHJpY3MuICANCg0KYGBge3J9DQptb2RfdG90PC1sbShQZXNvIH4gQW5uaS5tYWRyZSArIE4uZ3JhdmlkYW56ZV9DTCArIEZ1bWF0cmljaSArIEdlc3RhemlvbmUgKyBMdW5naGV6emEgKyBDcmFuaW8gKyBTZXNzbywgZGF0YT1kYXRhc2V0KQ0Kc3VtbWFyeShtb2RfdG90KQ0KYGBgDQpUaGUgdmFyaWFibGVzICJhZ2UiLCAiTi5kZWxpdmVyaWVzIiBhbmQgInNtb2tpbmciIHNlZWVtIHRvIGJlIHRoZSBsZXNzIHNpZ25pZmljYW50LiBTbyB3ZSBzdGFydCByZW1vdmluZyB0aGUgdmFyaWFibGUgImFnZSIgZnJvbSB0aGUgbW9kZWwuDQoNCmBgYHtyfQ0KbW9kMTwtbG0oUGVzbyB+ICBOLmdyYXZpZGFuemVfQ0wgKyBGdW1hdHJpY2kgKyBHZXN0YXppb25lICsgTHVuZ2hlenphICsgQ3JhbmlvICsgU2Vzc28sIGRhdGE9ZGF0YXNldCkNCnN1bW1hcnkobW9kMSkNCmBgYA0KY2F0KCJBZnRlciByZW1vdmluZyB0aGUgdmFyaWFibGUgXCJhZ2VcIiB0aGUgdmFyaWFibGVzIFwiTi5kZWxpdmVyaWVzXCIgYW5kIFwic21va2luZ1wiIGFyZSBzdGlsbCBub3Qgc2lnbmlmaWNhbnQuXG4iICkNCmNhdCgiU28gd2UgcmVtb3ZlIHRoZSB2YXJpYWJsZSBcInNtb2tpbmdcIiBmcm9tIHRoZSBtb2RlbC5cbiIgKQ0KDQoNCmBgYHtyfQ0KbW9kMjwtbG0oUGVzbyB+IE4uZ3JhdmlkYW56ZV9DTCArIEdlc3RhemlvbmUgKyBMdW5naGV6emEgKyBDcmFuaW8gKyBTZXNzbywgZGF0YT1kYXRhc2V0KQ0Kc3VtbWFyeShtb2QyKQ0KYGBgDQoNCg0KQWZ0ZXIgcmVtb3ZpbmcgdGhlIHZhcmlhYmxlIHNtb2tpbmcgdGhlIHZhcmlhYmxlcyBOLmRlbGl2ZXJpZXMgaXMgc3RpbGwgbGl0dGxlIHNpZ25pZmljYW50Lg0KU28gd2UgcmVtb3ZlIGFsc28gdGhpcyB2YXJpYWJsZSBmcm9tIHRoZSBtb2RlbC4NCg0KDQpgYGB7cn0NCm1vZDM8LWxtKFBlc28gfiBHZXN0YXppb25lICsgTHVuZ2hlenphICsgQ3JhbmlvICsgU2Vzc28sIGRhdGE9ZGF0YXNldCkNCnN1bW1hcnkobW9kMykNCmBgYA0KDQoNCkluIHRoZSBmaW5hbCBtb2RlbCBhbGwgdGhlIHJlbWFpbmluZyB2YXJpYWJsZXMgc2VlbSB2ZXJ5IHNpZ25pZmljYW50IHNvIHdlIGRvbid0IHJlbW92ZSBhbnkgZnVydGhlciB2YXJpYWJsZS4NCldlIGFsc28gbm90ZSB0aGF0IHRoZSBhZGp1c3RlZCBSLXNxdWFyZWQgaGFzIHJlbWFpbmVkIHVuY2hhbmdlZCBhZnRlciByZW1vdmluZyB0aGUgdmFyaWFibGVzICJhZ2UiIGFuZCAic21va2luZyIgd2hpbGUgaGFzIG9ubHkgc2xpZ2h0bHkgcmVkdWNlZCB3aGVuIHJlbW92aW5nIHRoZSB2YXJpYWJsZSAiTi5kZWxpdmVyaWVzIi4NCk5leHQgd2UgdGVzdCB0aGUgZGlmZmVyZW50IG1vZGVscyB0aGF0IHdlIGhhdmUgY29uc2lkZXJlZCBieSBsb29raW5nIGF0IHRoZSBhbmFseXNpcyBvZiB2YXJpYW5jZSBiZXR3ZWVuIGVhY2ggbW9kZWwgYW5kIHRoZSBzdWJzZXF1ZW50IG9uZSBhbmQgdGhlbiB3ZSBsb29rIGF0IHRoZSBCSUMgdmFsdWUgb2YgdGhlIGZvdXIgbW9kZWxzLg0KDQoNCmBgYHtyfQ0Ka2FibGUoYW5vdmEobW9kX3RvdCwgbW9kMSwgbW9kMiwgbW9kMyksIGNhcHRpb249IkFuYWx5c2lzIG9mIFZhcmlhbmNlIG9mIGVhY2ggbW9kZWwgd2l0aCByZXNwZWN0IHRvIHRoZSBwcmV2aW91cyBvbmUiKSAlPiUNCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQpUaGUgYW5hbHlzaXMgb2YgdmFyaWFuY2UgYmV0d2VlbiBlYWNoIG1vZGVsIGFuZCB0aGUgc3Vic2VxdWVudCBvbmUgc2hvd3MgdGhhdCBldmVyeSB0aW1lIHdlIGhhdmUgcmVtb3ZlZCBhIHZhcmlhYmxlIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBoYXMgcmVkdWNlZCwgaG93ZXZlciB0aGUgcmVkdWN0aW9uIGhhcyBiZWVuIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgb25seSBvbmNlIHRoYXQgd2UgaGF2ZSByZW1vdmVkIGFsbCB0aGUgdGhyZWUgdmFyaWFibGVzLg0KDQoNCmBgYHtyfQ0Ka2FibGUoQklDKG1vZF90b3QsIG1vZDEsIG1vZDIsIG1vZDMpLCBjYXB0aW9uPSJCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEJJQykgb2YgdGhlIGZvdXIgbW9kZWxzIGNvbnNpZGVyZWQiKSAlPiUNCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQpJbmNsdWRpbmcgb25seSBsaW5lYXIgdGVybXMgaW4gdGhlIHZhcmlhYmxlcyB3ZSBmaW5kIHRoYXQgdGhlIGJlc3QgbW9kZWwsIGFjY29yZGluZyB0byB0aGUgQklDIHZhbHVlcywgaXMgdGhlIG9uZSB0aGF0IHByZWRpY3RzIHRoZSB3ZWlnaHQgb2YgdGhlIG5ld2Jvcm4gYmFzZWQgb25seSBvbiB0aGUgdmFyaWFibGVzIExlbmd0aCwgQ3JhbmlhbCBjaXJjdW1mZXJlbmNlLCBzZXggYW5kIGdlc3RhdGlvbiB0aW1lLCBmb2xsb3dlZCBjbG9zZWx5IGJ5IHRoZSBtb2RlbCB0aGF0IGluY2x1ZGVkIGFsc28gdGhlIGRlcGVuZGVuY2UgdXBvbiB0aGUgbnVtYmVyIG9mIGRlbGl2ZXJpZXMuDQoNCk5leHQgd2Ugc3RhcnQgZnJvbSB0aGUgYmVzdCBtb2RlbCB0aGF0IHdlIGZvdW5kIGFuZCBjb25zaWRlciBpZiB0byBpbmNsdWRlIGFsc28gbm9ubGluZWFyIHRlcm1zIGluIHRoZSB2YXJpYWJsZXMuIA0KSW4gcGFydGljdWxhciB3ZSBub3RlIHRoYXQgdGhlIHRoZSB3ZWlndGggc2hvdWxkIGJlIHByb3BvcnRpb25hbCB0byB0aGUgdm9sdW1lIG9mIHRoZSBib2R5IGFuZCB0aGF0IHRoZSB2b2x1bWUgbGlrZWx5IGRlcGVuZHMgb24gY3ViaWMgdGVybXMgaW4gdGhlIHZhcmlhYmxlcyBsZW5ndGggYW5kIGNyYW5pYWwgY2lyY3VtZmVyZWNlLiBJbmRlZWQgaWYgd2UgdGhpbmsgYWJvdXQgdGhlIGJvZHkgYXMgYSBjeWxpbmRlciBvciBwYXJhbGxlbGVwaXBlZG9uIHdlIGNvdWxkIHdyaXRlIGl0cyB2b2x1bWUgaW4gdGVybXMgb2YgJGxlbmd0aFx0aW1lcyhjcmFuaWFsXF9jaXJjdW1mZXJlbmNlKV4yJCBvciAkbGVuZ3RoXjJcdGltZXMgY3JhbmlhbFxfY2lyY3VtZmVyZW5jZSQuIA0KDQpBZGRpdGlvbmFsbHkgdGhlIHNjYXR0ZXJwbG90cyBzdWdnZXN0IGEgcG9zc2libGUgbm9ubGluZWFyIGRlcGVuZGVuY2Ugb2YgdGhlIHdlaWdodCBvbiB0aGUgZ2VzdGF0aW9uYWwgYWdlLiBBcyBhIGZpcnN0IHN0ZXAgd2Ugc3R1ZHkgdGhlIGNvcnJlbGF0aW9uIG9mIHNvbWUgbm9ubGluZWFyIHRlcm1zIGJldHdlZW4gZWFjaCBvdGhlciBhbmQgd2l0aCB0aGUgdmFyaWFibGUgd2VpZ2h0LiANCg0KYGBge3J9DQpkYXRhc2V0JEdlc3RhemlvbmUyIDwtIChkYXRhc2V0JEdlc3RhemlvbmUpXjINCmRhdGFzZXQkbG9nR2VzdGF6aW9uZSA8LSBsb2coZGF0YXNldCRHZXN0YXppb25lKQ0KDQpwYWlycyhkYXRhc2V0WywgYygiUGVzbyIsICJHZXN0YXppb25lIiwgIkdlc3RhemlvbmUyIiwNCiAgICAgICAgICAgICAgICAibG9nR2VzdGF6aW9uZSIpXSwgbG93ZXIucGFuZWw9cGFuZWwuY29yLCB1cHBlci5wYW5lbD1wYW5lbC5zbW9vdGgpDQoNCmRhdGFzZXQkTHVuZ2hlenphMiA8LSAoZGF0YXNldCRMdW5naGV6emEpXjINCmRhdGFzZXQkTHVuZ2hlenphMnhDcmFuaW8gPC0gKGRhdGFzZXQkTHVuZ2hlenphKV4yKihkYXRhc2V0JENyYW5pbykNCmRhdGFzZXQkQ3JhbmlvMiA8LSAoZGF0YXNldCRDcmFuaW8pXjINCmRhdGFzZXQkTHVuZ2hlenpheENyYW5pbzIgPC0gZGF0YXNldCRMdW5naGV6emEqKGRhdGFzZXQkQ3JhbmlvKV4yDQoNCnBhaXJzKGRhdGFzZXRbLCBjKCJQZXNvIiwiTHVuZ2hlenphIiwgIkx1bmdoZXp6YTIiLCAiTHVuZ2hlenphMnhDcmFuaW8iLCANCiAgICAgICAgICAgICAgICAiQ3JhbmlvIiwgIkNyYW5pbzIiLCAiTHVuZ2hlenpheENyYW5pbzIiKV0sIGxvd2VyLnBhbmVsPXBhbmVsLmNvciwgdXBwZXIucGFuZWw9cGFuZWwuc21vb3RoKQ0KYGBgDQoNCkR1ZSB0byB0aGUgcmVhbHRpdmVseSBzbWFsbCByYW5nZSBpbnRlcnZhbHMgb2YgdGhlIHZhcmlhYmxlcyB3ZSBub3RlIHRoYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gYWxsIHRoZSB2YXJpYWJsZXMgR2VzdGF0aW9uIHRpbWUsIExlbmd0aCBhbmQgQ3JhbmlhbCBjaXJjdW1mZXJlbmNlIHdpdGggdGhlaXIgcXVhZHJhdGljIHZlcnNpb24sIGFzIGFsc28gYmV0d2VlbiBnZXN0YXRpb24gdGltZSBhbmQgaXRzIGxvZ2FyaXRobSwgaXMgMSB1cCB0byBhIHNlY29uZCBlcnJvciBhcHByb3hpbWF0aW9uLiBTbyBpdCBkb2Vzbid0IG1ha2Ugc2Vuc2UgdG8gaW5jbHVkZSB0aGVzZSB0ZXJtcyBpbiB0aGUgbW9kZWwgc2luY2Ugd2UgY2FuIG5vdCBleHBlY3QgdGhlbSB0byBwZXJmb3JtIHNpZ25pZmljYW50bHkgYmV0dGVyIHRoYW4gdGhlIGxpbmVhciB0ZXJtcy4NCg0KVGhlIHNpdHVhdGlvbiBpcyBzbGlnaHRseSBkaWZmZXJlbnQgZm9yIHRoZSBjdWJpYyB0ZXJtcyBtaXhlZCBpbiBsZW5ndGggYW5kIGNyYW5pYWwgY2lyY3VtZmVyZW5jZS4gSW5kZWVkLCBldmVuIGlmIHRoZXkgaGF2ZSBoaWdoIGNvcnJlbGF0aW9ucyB3aXRoIHRoZSB2YXJpYWJsZXMgbGVuZ3RoIGFuZCBjcmFuaWFsIGNpcmN1bWZlcmVuY2UgdGhleSBhbHNvIGhhdmUgaGlnaGVyIGNvcnJlbGF0aW9ucyB3aXRoIHRoZSB2YXJpYWJsZSB3ZWlnaHQgd2l0aCByZXNwZWN0IHRvIHRoZSBsaW5lYXIgdGVybXMuIFNvIHdlIHRyeSB0byBpbmNsdWRlIHRoZXNlIHRlcm1zIGluIHRoZSBtb2RlbC4NCg0KDQpgYGB7cn0NCnRvdF9ub25fbGluX21vZCA8LSBsbShQZXNvIH4gTi5ncmF2aWRhbnplX0NMICsgR2VzdGF6aW9uZSArIEx1bmdoZXp6YSArIENyYW5pbyArIEx1bmdoZXp6YTJ4Q3JhbmlvICsgTHVuZ2hlenpheENyYW5pbzIgKyBTZXNzbywgZGF0YT1kYXRhc2V0KQ0KDQpub25saW5fbW9kID0gTUFTUzo6c3RlcEFJQyh0b3Rfbm9uX2xpbl9tb2QsIGRpcmVjdGlvbj0iYm90aCIsIGs9bG9nKE5fb2JzZXJ2YXRpb25zKSkNCg0Kc3VtbWFyeShub25saW5fbW9kKQ0KDQprYWJsZShCSUMobW9kMywgbm9ubGluX21vZCksIGNhcHRpb249IkJheWVzaWFuIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQklDKSBvZiB0aGUgbGluZWFyIG1vZGVsIHZzIHRoZSBub25saW5lYXIgb25lIikgJT4lDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpgYGANClRoZSBtb2RlbCBzZWxlY3RlZCBieSB0aGUgc3RlcHdpc2UgbWV0aG9kIGJ5IHRha2luZyBpbnRvIGFjY291bnQgZm9yIGFsbCB0aGUgcG9zc2libGUgbm9ubGluZWFyIHRlcm1zIHRoYXQgd2UgaGF2ZSBwcmV2aW91c2x5IGNvbnNpZGVyZWQgYXMgYSBCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEJJQykgdmFsdWUgc2lnbmlmaWNhbnRseSBzbWFsbGVyIHRoYW4gdGhlIGJlc3QgbW9kZWwgdGhhdCBpcyBsaW5lYXIgaW4gYWxsIHRoZSB2YXJpYWJsZXMuIA0KDQpOZXh0IHdlIGNvbXB1dGUgdGhlIHZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3JzIG9mIHRoZSBzZWxlY3RlZCBtb2RlbC4gDQoNCmBgYHtyfQ0KDQprYWJsZShjYXI6OnZpZihub25saW5fbW9kKSwgY2FwdGlvbj0iVmFyaWFuY2UgSW5mbGF0aW9uIGZhY3RvcnMgb2YgdGhlIG5vbmxpbmVhciBtb2RlbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCg0KYGBgDQpBcyBleHBlY3RlZCB0aGUgbW9kZWwgaGFzIGV4dHJlbWVseSBoaWdoIHZhcmlhbmNlIGluZmxhY3Rpb24gZmFjdG9yIGR1ZSB0byB0aGUgaGlnaCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYmV0d2VlbiB0aGUgdmFyaWFibGVzLiBTaW5jZSB0aGUgaGlnaCB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9ycyBtYWtlIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMgdW5zdGFibGUgYW5kIHVucmVsaWFibGUsIHdlIHByZWZlciB0byBkaXNjYXJkIHRoZSBsYXN0IG1vZGVsLg0KDQpIb3dldmVyIHdlIGhhdmUgbGVhcm50IHRoYXQgdGhlIGluY2x1c2lvbiBvZiBub25saW5lYXIgdGVybXMgY2FuIGltcHJvdmUgdGhlIG1vZGVsLiBTbywgbmV4dCwgd2UgdHJ5IHRvIHVwZGF0ZSB0aGUgbGFzdCBtb2RlbCBieSByZW1vdmluZyB2YXJpYWJsZXMgdGhhdCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gc2VlIGlmIHdlIGNhbiBnZXQgYSBtb2RlbCB0aGF0IGlzIG1vcmUgYWNjdXJhdGUgdGhhbiB0aGUgbGluZWFyIG1vZGVsIG1vZDMgd2l0aG91dCBoYXZpbmcgaGlnaCB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9ycy4gSW4gcGFydGljdWxhciB3ZSBoaWdobGlnaHQgdGhhdCBhIG1vZGVsIGV4cHJlc3NpbmcgdGhlIHdlaWdodCBsaW5lYXJseSB3aXRoIHJlc3BlY3QgdG8gYSB2b2x1bWUgd291bGQgYmUgcGh5c2ljYWxseSBtdWNoIG1vcmUgbWVhbmluZ2Z1bGwgdGhhbiBvbmUgZXNwcmVzc2luZyB0aGUgd2VpZ2h0IGxpbmVhcmx5IHdpdGggcmVzcGVjdCB0byBsZW5ndGhzLiBGb3IgdGhlc2UgcmVhc29ucyB3ZSBvcHQgZm9yIHJlbW92aW5nIGZyb20gdGhlIG1vZGVsIHRoZSB0d28gbGluZWFyIHZhcmlhYmxlcyAibGVuZ3RoIiBhbmQgImNyYW5pYWxfY2lyY3VtZmVyZW5jZSINCg0KYGBge3J9DQpub25saW5fbW9kMiA8LSB1cGRhdGUobm9ubGluX21vZCwgLiB+IC4gLSBMdW5naGV6emEgLSBDcmFuaW8pDQoNCnN1bW1hcnkobm9ubGluX21vZDIpDQoNCmthYmxlKGNhcjo6dmlmKG5vbmxpbl9tb2QyKSwgY2FwdGlvbj0iVmFyaWFuY2UgSW5mbGF0aW9uIGZhY3RvcnMgb2YgdGhlIG5vbmxpbmVhciBtb2RlbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpBZnRlciByZW1vdmluZyB0aGUgdmFyaWFibGVzIGxlbmd0aCBhbmQgY3JhbmlhbCBjaXJjdW1mZXJlbmNlIHRoZSB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yIGlzIHN0aWxsIGhpZ2ggZm9yIHRoZSB0d28gY3ViaWMgdmFyaWFibGVzLiBGb3IgdGhpcyByZWFzb24gd2UgcmVtb3ZlIGZyb20gdGhlIG1vZGVsIGFsc28gdGhlIHZhcmlhYmxlICRMdW5naGV6emFcdGltZXMgQ3JhbmlvXjIkIHdoaWNoIHJlc3VsdHMgdGhlIGxlc3MgaW5mbHVlbnRpYWwuIA0KDQpgYGB7cn0NCm5vbmxpbl9tb2QzIDwtIHVwZGF0ZShub25saW5fbW9kMiwgLiB+IC4gLSBMdW5naGV6emF4Q3JhbmlvMikNCg0Kc3VtbWFyeShub25saW5fbW9kMykNCg0Ka2FibGUoY2FyOjp2aWYobm9ubGluX21vZDMpLCBjYXB0aW9uPSJWYXJpYW5jZSBJbmZsYXRpb24gZmFjdG9ycyBvZiB0aGUgbm9ubGluZWFyIG1vZGVsIikgJT4lDQogIGthYmxlX3N0eWxpbmcoKQ0KDQprYWJsZShCSUMobW9kMiwgbm9ubGluX21vZCwgbm9ubGluX21vZDIsIG5vbmxpbl9tb2QzKSwgY2FwdGlvbj0iQmF5ZXNpYW4gaW5mb3JtYXRpb24gZmFjdG9yIG9mIHRoZSBsaW5lYXIgbW9kZSB2cyBub25saW5lYXIgb25lcyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCg0KDQpgYGANCg0KVGhlIGZpbmFsIG1vZGVsIHByZWRpY3RzIHRoZSB3ZWlnaHQgb2YgdGhlIG5ldyBib3JuIGJhc2VkIG9uIGdlc3RhdGlvbiB0aW1lLCBzcXVhcmVkIGxlbmd0aCB0aW1lcyBjcmFuaXVtIGNpcmN1bWZlcmVuY2UsIHNleCBhbmQgbnVtYmVyIG9mIGRlbGl2ZXJpZXMuIFRoZSBtb2RlbCBoYXMgYSBCSUMgdmFsdWUgbGFyZ2VyIHRoYW4gdGhlIGJlc3QgbW9kZWwgd2hpY2ggaW5jbHVkZWQgYWxsIHRoZSBwb3NzaWJsZSBub25saW5lYXIgdGVybXMgYnV0IHNtYWxsZXIgdGhhbiB0aGUgYmVzdCBtb2RlbCB0aGF0IGluY2x1ZGVkIG9ubHkgbGluZWFyIHRlcm1zIGluIHRoZSB2YXJpYWJsZXMuIE1vcmVvdmVyLCBkaWZmZXJlbnRseSBmcm9tIHRoZSBiZXN0IG1vZGVsIHRoYXQgaW5jbHVkZWQgYWxsIHRoZSBwb3NzaWJsZSBub25saW5lYXIgdGVybXMsIHRoZSBtb2RlbCAibm9ubGluX21vZDMiIGhhcyBhbGwgdGhlIHZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3JzIHNtYWxsZXIgdGhhbiA1Lg0KDQpXZSBwb2ludCBvdXQgdGhhdCB0aGUgbW9kZWwgIm5vbmxpbl9tb2QzIiwgaW5jbHVkaW5nIHRoZSBtaXhlZCB0ZXJtICRsZW5ndGheMlx0aW1lcyBjcmFuaWFsX2NpcmN1bWZlcmVjZSQgYnV0IG5vdCB0aGUgbGluZWFyIHRlcm1zIGluIHRoZSB2YXJpYWJsZXMgJGxlbmd0aCQgYWQgJGNyYW5pYWxfY2lyY3VtZmVyZWNlJCwgbWF5IGJyZWFrIHRoZSBwcmluY2lwbGUgb2YgbWFyZ2luYWxpdHkuIEhvd2V2ZXIgd2UgaGlnaGxpZ2h0IHRoYXQgdGhlIG1vZGVsIGlzIGFsc28gcGh5c2ljYWxseSBtb3JlIG1lYW5pbmdmdWxsIHRoYXQgb25lIGluY2x1ZGluZyB0aGUgbGluZWFyIHRlcm1zIGluIHRoZSB2YXJpYWJsZXMgJGxlbmd0aCQgYW5kICRjcmFuaWFsX2NpcmN1bWZlcmVuY2UkLg0KRm9yIHRoZXNlIHJlYXNvbnMgd2UgYWNjZXB0IHRoaXMgbW9kZWwgYW5kIGFuYWx5emUgaXQuDQpXZSBzdGFydCBhbmFseXppbmcgaXRzIGNvZWZmaWNpZW50cyANCg0KYGBge3J9DQprYWJsZShjb2VmKG5vbmxpbl9tb2QzKSwgY2FwdGlvbj0iQ29lZmZpY2llbnRzIG9mIGZpbmFsIG5vbmxpbmVhciBtb2RlbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KVGhlIGNvZWZmaWNpZW50cyBzYXkgdGhhdCBmb3IgZWFjaCB1bml0IGluY3JlbWVudCBpbiB0aGUgZ2VzdGF0aW9uIHRpbWUgKDEgd2Vlaykgd2Ugb2JzZXJ2ZSBhbiBpbmNyZW1lbnQgb2YgYXBwcm94aW1hdGVseSA0MGdyIGluIHRoZSB3ZWlndGggb2YgdGhlIG5ld2Jvcm4uIFNpbWlsYXJseSBmb3IgZWFjaCAxbW1eMyBpbmNyZW1lbnQgaW4gdGhlIHZvbHVtZSB0ZXJtICgkTGVuZ3RoXjIgXHRpbWVzIENyYW5pYWxfY2lyY2Z1bWZlcmVuY2UkKSB3ZSByZWdpc3RlciBhbiBpbmNyZW1lbnQgb2YgJDMuNSoxMF4tNSQgKGdyKSBpbiB0aGUgd2VpZ2h0IG9mIHRoZSBuZXdib3JuLiBGaW5hbGx5IHdlIG9ic2VydmUgdGhhdCB0aGUgbWFsZXMgd2lsbCByZWdpc3RlciBhcHByb3hpbWF0ZWx5IDdnciBtb3JlIHRoYW4gZmVtYWxlcyBhbmQgY2hpbGQgYWZ0ZXIgdGhlIGZpcnN0IG9uZSB3ZWlnaHQgYXBwcm94aW1hdGVseSA0Z3IgbW9yZSB0aGFuIGEgZmlyc3QgY2hpbGQuDQoNCk5leHQgd2UgYW5hbHl6ZSB0aGUgcmVzaWR1YWxzLCBvdXRsaWVycyBhbmQgbGV2ZXJhZ2VzIG9mIHRoZSBtb2RlbC4NCg0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KG5vbmxpbl9tb2QzKQ0KDQpsbXRlc3Q6OmJwdGVzdChub25saW5fbW9kMykNCmxtdGVzdDo6ZHd0ZXN0KG5vbmxpbl9tb2QzKQ0Kc2hhcGlyby50ZXN0KG5vbmxpbl9tb2QzJHJlc2lkdWFscykNCmBgYA0KDQpUaGUgYW5hbHlzaXMgc2hvd3MgdGhhdCB0aGVyZSBpcyBubyBhdXRvY29ycmVsYXRpb24gb2YgdGhlIHJlc2lkdWFscyAocC12YWx1ZSBvZiB0aGUgRHVyYmluLVdhdHNvbiB0ZXN0ID4gMC4wNSkuIEhvd2V2ZXIgd2Ugb2JzZXJ2ZSB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCAocC12YWx1ZSBvZiB0aGUgU2hhcGlyby1XaWxrIHRlc3QgPDAuMDUpIGFuZCB0aGF0IHRoZXJlIGlzIG5vIGhldGVyb3NjZWRhc3RpY2l0eSAocC12YWx1ZSBvZiB0aGUgQnJldXNjaC1QYWdhbiA8IDAuMDUpLiBBZGRpdGlvbmFsbHkgd2Ugb2JzZXJ2ZSB0aGF0IHRoZXJlIGFyZSBzb21lIGhpZ2hseSBpbmZsdWVudGlhbCBvdXRsaWVyIGRhdGFwb2ludHMgaGF2aW5nIENvb2sgZGlzdGFuY2UgY2xvc2UgdG8gMC41Lg0KDQpTbyB3ZSBpbnZlc3RpZ2F0ZSBkZWVwZXIgdGhlIGxldmVyYWdlcyBhbmQgb3V0bGllcnMuIFdlIGNvbnNpZGVyIGxldmVyYWdlcyB0aGUgcG9pbnRzIGhhdmluZyBsZXZlcmFnZSB2YWx1ZSBsYXJnZXIgdGhhbiB0aGUgdGhyZXNob2xkLiBUaGlzIGlzIHNldCBlcXVhbCB0byAzIHRpbWVzIHRoZSBsZXZlcmFnZSBtZWFuLCBpLmUuIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBvZiB0aGUgbW9kZWwgb3ZlciB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YXNldC4gTW9yZW92ZXIgdGhlIG91dGxpZXJzIGFyZSBpZGVudGlmaWVkIGJ5IGEgQm9uZmVycm9uaSB0ZXN0IG9uIHRoZSBzdHVkZW50aXplZCByZXNpZHVhbHMgb2YgdGhlIG1vZGVsLg0KDQpgYGB7cn0NCg0KI2xldmVyYWdlDQpsZXY8LWhhdHZhbHVlcyhub25saW5fbW9kMykNCnBsb3QobGV2KQ0KcDwtc3VtKGxldikNCm48LWxlbmd0aChsZXYpDQpzb2dsaWE9MypwL24NCmFibGluZShoPXNvZ2xpYSxjb2w9MikNCnRpdGxlKG1haW4gPSAiTGV2ZXJhZ2UgdmFsdWVzIG9mIG1vZGVsIG1vZDEiKQ0KbGV2ZXJhZ2VzPXdoaWNoKGxldj5zb2dsaWEpDQpjYXQoIlRoZXJlIGFyZSIsIGxlbmd0aChsZXZlcmFnZXMpLCAibGV2ZXJhZ2VzXG4iKQ0KDQojb3V0bGllcnMNCnBsb3QocnN0dWRlbnQobm9ubGluX21vZDMpKQ0KYWJsaW5lKGg9YygtMiwyKSkNCnRpdGxlKG1haW4gPSAiU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIG9mIG1vZGVsIG1vZDEiKQ0KDQoNCmNhdCgiVGhlIG91dGxpZXJzIGFyZTpcbiIpDQpjYXI6Om91dGxpZXJUZXN0KG5vbmxpbl9tb2QzKQ0KDQoNCiNkaXN0YW56YSBkaSBjb29rDQpjb29rPC1jb29rcy5kaXN0YW5jZShub25saW5fbW9kMykNCnBsb3QoY29vayx5bGltID0gYygwLDEpKSANCnRpdGxlKG1haW4gPSAiQ29vayBkaXN0YW5jZSBvZiByZXNpZHVhbHMgb2YgbW9kZWwgbW9kMSIpDQpgYGANCg0KV2Ugbm90ZSB0aGF0IHRoZSBtb3N0IGluZmx1ZW50aWFsIG91dGxpZXIsIHRoZSBkYXRhcG9pbnQgMTU0OSwgd2l0aCBhIENvb2sgZGlzdGFuY2UgYXJvdW5kIDAuMiwgY291bGQgaGF2ZSBhIHJlbGV2YW50IGVmZmVjdCBvbiB0aGUgZXN0aW1hdGUgb2YgdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWwgYnV0IG5vdCBkcmFzdGljYWwuIEJ5IGludmVzdGlnYXRpbmcgdGhpcyBkYXRhcG9pbnQgd2UgZmluZCB0aGF0IHRoaXMgaXMgdGhlIHNhbWUgZGF0YXBvaW50IHRoYXQgd2UgaGF2ZSBhbHJlYWR5IG1ldCBiZWZvcmUgd2hpY2ggaGFzIGEgbGVuZ3RoIG11Y2ggbG93ZXIgdGhhbiBleHBlY3RlZCwgcHJvYmFibHkgZm9yIGEgdHlwbyBpbiBpdHMgdHJhbnNwb3NpdGlvbi4NCg0KDQpgYGB7cn0NCmthYmxlKHQoZGF0YXNldFsxNTQ5LF0pLCBjYXB0aW9uPSJPdXRsaWVyIGRhdGFwb2ludCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCg0KYGBgDQoNCg0KTmV4dCB3ZSBldmFsdWF0ZSB0aGUgcXVhbGl0eSBvZiB0aGUgbW9kZWwgaW4gdGVybXMgb2YgdGhlICRSXjIkIGFuZCBSTVNFIG1ldHJpY3MuDQpgYGB7cn0NCiMgUl4yIDoNCnN1bW1hcnlfbW9kIDwtIHN1bW1hcnkobm9ubGluX21vZDMpDQpSMiA8LSBzdW1tYXJ5X21vZCRyLnNxdWFyZWQNCmNhdCgiVGhlIFJeMiB2YWx1ZSBvZiB0aGUgbW9kZWwgaXMgZXF1YWwgdG8iLCBSMiwgIiwgc28gdGhlIG1vZGVsIGlzIGV4cGxhaW5pbmcgYWJvdXQgNzMlIG9mIHRoZSB2YXJpYW5jZSBvZiB0aGUgdmFyaWFibGUgV2VpZ2h0LiBcbiIpDQoNCiMgUk1TRToNCnByZWRpY3Rpb25zIDwtIHByZWRpY3Qobm9ubGluX21vZDMsIG5ld2RhdGEgPSBkYXRhc2V0KQ0KcmVzaWR1YWxzIDwtIGRhdGFzZXQkUGVzbyAtIHByZWRpY3Rpb25zDQpSTVNFIDwtIHNxcnQobWVhbihyZXNpZHVhbHNeMikpDQpwZXJSTVNFPVJNU0UvbWVhbihkYXRhc2V0JFBlc28pDQpjYXQoIlRoZSBSTVNFIHZhbHVlIG9mIHRoZSBtb2RlbCBpcyBlcXVhbCB0byIsIFJNU0UsICIuXG4iKSANCmNhdCgiVGhpcyBtZWFucyB0aGF0IHRoZSBtb2RlbCBoYXMgYSBtZWFuIGVycm9yIG9mIHByZWRpY3Rpb24gb2YiICwgcGFzdGUwKHJvdW5kKHBlclJNU0UsMiksIiUiKSwid2l0aCByZXNwZWN0IHRvIHRoZSBtZWFuIG9mIHRoZSB2YXJpYWJsZSB3ZWlnaHQuXG4iKQ0KY2F0KCJNb3Jlb3ZlciB0aGlzIG1lYW5zIHRoYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSBwcmVkaWN0aW9uIGVycm9ycyBvZiB0aGUgbW9kZWwgaXMgYWJvdXQiLCByb3VuZChSTVNFL3NkKGRhdGFzZXQkUGVzbyksMiksICJvZiB0aGUgdmFyaWFuY2Ugb2YgdGhlIHZhcmlhYmxlIHdlaWdodCIpDQpgYGANCiMjIFVzaW5nIHRoZSBtb2RlbCB0byBtYWtlIHByZWRpY3Rpb25zDQpBc3N1bWUgdG8gaGF2ZSBhIGZlbWFsZSBiYWJ5IHRoYXQgd2lsbCBiZSBkZWxpdmVyZWQgYXQgdGhlIDM5dGggd2VlayBhbmQgd2hvc2UgbW90aGVyIGhhcyBhbHJlYWR5IGRlbGl2ZXJlZCB0d28gY2hpbGRyZW4uIFRoZW4gd2UgY2FuIHByZWRpY3QgaGVyIHdlaWdodC4gDQpGb3IgdGhlIG5ldyBiYWJ5IHdlIGFzc3VtZSBhIGxlbmd0aCBhbmQgY3JhbmlhbCBjaXJjdW1mZXJlbmNlIGVxdWFsIHRvIHRoZSBtZWFuIHdlaWdodCBhbmQgY3JhbmlhbCBjaXJjdW1mZXJlbmUgb2YgZmVtYWxlIG5ld2Jvcm5zIGF0IHRoZSAzOXRoIGdlc3RhdGlvbiB3ZWVrLg0KDQoNCmBgYHtyfQ0KbGVuZ3RoPSBtZWFuKHN1YnNldChkYXRhc2V0LCBTZXNzbyA9PSAiRiIgJiBHZXN0YXppb25lPT0zOSkkTHVuZ2hlenphKQ0KY3Jhbml1bT0gbWVhbihzdWJzZXQoZGF0YXNldCwgU2Vzc28gPT0gIkYiICYgR2VzdGF6aW9uZT09MzkpJENyYW5pbykNCg0KbmV3X2RhdGEgPC0gZGF0YS5mcmFtZSgiTi5ncmF2aWRhbnplIiA9IGMoMiksDQogICAgICAgICAgICAgICAgICAgICAgICJHZXN0YXppb25lIj1jKDM5KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIkx1bmdoZXp6YSI9YyhsZW5ndGgpLA0KICAgICAgICAgICAgICAgICAgICAgICAiQ3JhbmlvIj1jKGNyYW5pdW0pLA0KICAgICAgICAgICAgICAgICAgICAgICAiU2Vzc28iPWMoIkYiKQ0KICAgICAgICAgICAgICAgICAgICAgICApDQoNCm5ld19kYXRhJEx1bmdoZXp6YTJ4Q3JhbmlvID0gKG5ld19kYXRhJEx1bmdoZXp6YSleMioobmV3X2RhdGEkQ3JhbmlvKQ0KbmV3X2RhdGEkQ3JhbmlvMiA8LSAobmV3X2RhdGEkQ3JhbmlvKV4yDQpuZXdfZGF0YSROLmdyYXZpZGFuemVfQ0wgPC0gY3V0KG5ld19kYXRhJE4uZ3JhdmlkYW56ZSwgDQogICAgICAgICAgICAgICAgICAgICBicmVha3M9YygwLDEsMTAwKSwgDQogICAgICAgICAgICAgICAgICAgICByaWdodD1GQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMCIsIjErIikpDQoNCg0Kd2VpZ2h0X3ByZWRpY3Rpb24gPC0gcm91bmQocHJlZGljdChub25saW5fbW9kMywgbmV3ZGF0YSA9IG5ld19kYXRhKSkNCg0KDQpjYXQoIlRoZSByZWdyZXNzaW9uIG1vZGVsIHByZWRpY3RzIGEgd2VpZ2h0IGVxdWFsIHRvIiwgcGFzdGUwKHdlaWdodF9wcmVkaWN0aW9uLCJnciIpLCAiLlxuIikgDQpgYGANCg0KDQojIyBGaW5hbGx5IHdlIGRpc3BsYXkgdGhlIHJlbGF0aW9ucyBjYXB0dXJlZCBpbiB0aGUgbW9kZWwuDQoNCjwhLS0gYGBge3J9IC0tPg0KDQo8IS0tIGdncGxvdChkYXRhPWRhdGFzZXRfY2xlYW4sIC0tPg0KPCEtLSAgICAgICAgYWVzKHggPSBMdW5naGV6emEsICAtLT4NCjwhLS0gICAgICAgICAgICB5ID0gQ3JhbmlvLCAgLS0+DQo8IS0tICAgICAgICAgICAgY29sb3IgPSBQZXNvKSkgKyAtLT4NCjwhLS0gICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMuNSkgKyAtLT4NCjwhLS0gICAgICAgIGxhYnModGl0bGUgPSAiV2VpZ2h0IGFzIGEgZnVuY3Rpb24gb2YgTGVuZ3RoIGFuZCBDcmFuaWFsIGNpcmN1bWZlcmVuY2UiLCAtLT4NCjwhLS0gICAgICAgICAgICAgeCA9ICJMZW5ndGgiLCAtLT4NCjwhLS0gICAgICAgICAgICAgeSA9ICJDcmFuaWFsIGNpcmN1bWZlcmVuY2UiLCAgLS0+DQo8IS0tICAgICAgICAgICAgIGNvbG9yID0gIldlaWdodCIpKyAtLT4NCjwhLS0gICAgICAgdGhlbWVfY2xhc3NpYygpKyAtLT4NCjwhLS0gICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLCAtLT4NCjwhLS0gICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHIgPSAxMCkpKSAtLT4NCg0KPCEtLSBgYGAgLS0+DQogDQoNCkZpcnN0IHdlIGRpc3BsYXkgaG93IHRoZSB3ZWlnaHQgaXMgZGlzdHJpYnV0ZWQgYWNjb3JkaW5nIHRvIHRoZSBzZXggYW5kIG51bWJlciBvZiBkZWxpdmVyaWVzIHdpdGggcmVzcGVjdCB0byB0aGUgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwsIGkuZS4gJGdlc3RhdGlvblxfdGltZSQgYW5kICQobGVuZ3RoKV4yXHRpbWVzIGNyYW5pYWxcX2NpcmN1bWZlcmVuY2UkLg0KDQoNCmBgYHtyLCBmaWcud2lkdGg9MTEuNSwgZmlnLmhlaWdodD01fQ0KDQpwbG90X2Zpbl8xID0gZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHg9R2VzdGF6aW9uZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9UGVzbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1TZXNzbykscG9zaXRpb24gPSAiaml0dGVyIikrDQogICAgICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKGFlcyh4PUdlc3RhemlvbmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PVBlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9U2Vzc28pLCBzZT1GLCBtZXRob2QgPSAibG0iKSsNCiAgICAgICAgICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBzZXhfY29sb3JzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTSI9ICJNYWxlIiwgIkYiPSJGZW1hbGUiKSkrDQogICAgICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9ICJXZWlnaHQgYXMgYSBmdW5jdGlvbiBvZiBHZXN0YXRpb24gdGltZSIsDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gImdlc3RhdGlvbiB0aW1lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAid2VpZ2h0IChncikiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpDQoNCg0KbGVnZW5kIDwtIGFzX2dncGxvdChnZXRfbGVnZW5kKHBsb3RfZmluXzEpKQ0KDQpwbG90X2Zpbl8xID0gZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHg9R2VzdGF6aW9uZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9UGVzbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1TZXNzbykscG9zaXRpb24gPSAiaml0dGVyIikrDQogICAgICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKGFlcyh4PUdlc3RhemlvbmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PVBlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9U2Vzc28pLCBzZT1GLCBtZXRob2QgPSAibG0iKSsNCiAgICAgICAgICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBzZXhfY29sb3JzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTSI9ICJNYWxlIiwgIkYiPSJGZW1hbGUiKSkrDQogICAgICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9ICJXZWlnaHQgYXMgYSBmdW5jdGlvbiBvZiBHZXN0YXRpb24gdGltZSIsDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gImdlc3RhdGlvbiB0aW1lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAid2VpZ2h0IChncikiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQoNCiBwbG90X2Zpbl8zID0gZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHg9THVuZ2hlenphMnhDcmFuaW8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PVBlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9U2Vzc28pKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9zbW9vdGgoYWVzKHg9THVuZ2hlenphMnhDcmFuaW8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PVBlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9U2Vzc28pLCBzZT1GLCBtZXRob2QgPSAibG0iKSsNCiAgICAgICAgICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBzZXhfY29sb3JzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTSI9ICJNYWxlIiwgIkYiPSJGZW1hbGUiKSkrDQogICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSAiV2VpZ2h0IGFzIGEgZnVuY3Rpb24gb2YgXG4gTGVuZ3ReMiB4IGNyYW5pYWwgY2lyY3VtZmVyZW5jZSIsDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gIkxlbmd0XjIgeCBjcmFuaWFsIGNpcmN1bWZlcmVuY2UgKG1tXjMpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAid2VpZ2h0IChncikiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQogIA0KICANCmNvbWJfZmluIDwtIHBsb3RfZmluXzEgKyBwbG90X2Zpbl8zICsgbGVnZW5kICsgcGxvdF9sYXlvdXQobmNvbD0zLCBoZWlnaHRzID0gYyg1KSwgd2lkdGhzPSBjKDUsNSwxLjUpKQ0KDQpwcmludChjb21iX2ZpbikNCmBgYA0KDQoNCg0KYGBge3IsIGZpZy53aWR0aD0xMS41LCBmaWcuaGVpZ2h0PTV9DQoNCnBsb3RfZmluXzEgPSBnZ3Bsb3QoZGF0YT1kYXRhc2V0KSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoeD1HZXN0YXppb25lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT1QZXNvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPU4uZ3JhdmlkYW56ZV9DTCkscG9zaXRpb24gPSAiaml0dGVyIikrDQogICAgICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKGFlcyh4PUdlc3RhemlvbmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PVBlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9Ti5ncmF2aWRhbnplX0NMKSwgc2U9RiwgbWV0aG9kID0gImxtIikrDQogICAgICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9ICJXZWlnaHQgYXMgYSBmdW5jdGlvbiBvZiBHZXN0YXRpb24gdGltZSIsDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gImdlc3RhdGlvbiB0aW1lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAid2VpZ2h0IChncikiLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiTsKwIHByZXZpb3VzIGRlbGl2ZXJpZXMiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpDQoNCg0KbGVnZW5kIDwtIGFzX2dncGxvdChnZXRfbGVnZW5kKHBsb3RfZmluXzEpKQ0KDQpwbG90X2Zpbl8xID0gZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHg9R2VzdGF6aW9uZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9UGVzbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1OLmdyYXZpZGFuemVfQ0wpLHBvc2l0aW9uID0gImppdHRlciIpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3Ntb290aChhZXMoeD1HZXN0YXppb25lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT1QZXNvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPU4uZ3JhdmlkYW56ZV9DTCksIHNlPUYsIG1ldGhvZCA9ICJsbSIpKw0KICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSAiV2VpZ2h0IGFzIGEgZnVuY3Rpb24gb2YgR2VzdGF0aW9uIHRpbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJnZXN0YXRpb24gdGltZSIsDQogICAgICAgICAgICAgICAgICAgICAgICB5ID0gIndlaWdodCAoZ3IpIikrDQogICAgICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KICANCiBwbG90X2Zpbl8zID0gZ2dwbG90KGRhdGE9ZGF0YXNldCkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHg9THVuZ2hlenphMnhDcmFuaW8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PVBlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9Ti5ncmF2aWRhbnplX0NMKSkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKGFlcyh4PUx1bmdoZXp6YTJ4Q3JhbmlvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT1QZXNvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPU4uZ3JhdmlkYW56ZV9DTCksIHNlPUYsIG1ldGhvZCA9ICJsbSIpKw0KICAgICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlID0gIldlaWdodCBhcyBhIGZ1bmN0aW9uIG9mIFxuIExlbmd0XjIgeCBjcmFuaWFsIGNpcmN1bWZlcmVuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJMZW5ndF4yIHggY3JhbmlhbCBjaXJjdW1mZXJlbmNlIChtbV4zKSIsDQogICAgICAgICAgICAgICAgICAgICAgICB5ID0gIndlaWdodCAoZ3IpIikrDQogICAgICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KICANCiAgDQpjb21iX2ZpbiA8LSBwbG90X2Zpbl8xICsgcGxvdF9maW5fMyArIGxlZ2VuZCArIHBsb3RfbGF5b3V0KG5jb2w9MywgaGVpZ2h0cyA9IGMoNSksIHdpZHRocz0gYyg1LDUsMS41KSkNCg0KcHJpbnQoY29tYl9maW4pDQpgYGANCg0KVGhlIHBsb3RzIGNvbmZpcm0gdGhhdCB0aGUgd2VpZ2h0IGluY3JlYXNlcyB3aGlsZSBpbmNyZWFzaW5nIGFsbCB0aGUgb3RoZXIgdmFyaWFibGVzIGluIHRoZSBtb2RlbC4gTW9yZW92ZXIgd2Ugb2JzZXJ2ZSB0aGF0IHRoZSB3ZWlnaHQgb2JzZXJ2ZWQgaW4gbWFsZXMgaXMgYWx3YXlzIGhpZ2hlciB0aGFuIHRoZSB3ZWlnaHQgb2JzZXJ2ZWQgaW4gZmVtYWxlcy4gRGlmZmVyZW50bHkgdGhlIGRpZmZlcmVuY2UgaW4gd2VpZ2h0IGJldHdlZW4gZmlyc3QgY2hpbGRyZW4gYW5kIG5vdCBmaXJzdCBjaGlsZHJlbiBpcyBvbmx5IG9ic2VydmFibGUgd2hlbiB2YXJ5aW5nIHRoZSB2YXJpYWJsZSBnZXN0YXRpb24gdGltZS4gSW4gdGhpcyBjYXNlIHdlIG9ic2VydmUgdGhhdCB0aGUgd2VpZ2h0IG9mIGZpcnN0IGNoaWxkcmVuIGlzIHNsaWdodGx5IHNtYWxsZXIgdGhhbiB0aGUgd2VpZ2h0IG9mIG5vdCBmaXJzdCBjaGlsZHJlbi4gVGhlIG9ic2VydmF0aW9ucyBhcmUgaW4gYWdyZWVtZW50cyB3aXRoIHRoZSBkaXNjdXNzaW9uIGFib3V0IHRoZSAkXGJldGEkIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWwuIA0KDQo=