1 Introduction

As discussed in the previous analysis of flight delays, [I’m Going to Miss My Flight!], air travel is a pillar of our globalized world, upholding trade, movement of persons, and facilitating economic growth. As outlined by the Air Transport Action Group, in 2023 alone, a total of 1,138 airlines were operating a total of 29,039 planes across 67,300 global routes, connecting 21,000 unique pairs of cities. The vast size of this transit network presents unique challenges when working to analyze flight data with the goal of reducing flight delays.

It is near impossible to ensure standardized data collection formats globally, resulting in incomplete data sets with missing values. In order to properly analyze flight data to successfully reduce flight delay times, considerations must be taken to predict said missing values, essentially filling in the gaps. In the world of data science, filling in these data gaps is known as imputation. The following imputations were analyzed to determine the best fit approach:

  • Hotdeck (k-nearest neighbor)
  • Linear Regression Models
  • Amelia Bootstrap Imputation
  • MICE Regression

Once the best imputation method was selected, the data was then analysed for any potential skew, and subsequently normalized to reduce skew & prepare the data for predictive modeling using min-max normalization. The normalized data was then analyzed for redundancies and only the high impact variables retained through RFE feature selection. Once fully pruned the data was then subjected to k-Nearest Neighbor cluster based feature extraction. This final extracted clustered data is then ready for use in various predictive models to identify and predict flight delays before they happen based on previously collected global flight data.

1.1 Data Details

The Flight delay data as collected from Applied Analytics through Case Studies Using SAS and R, Deepti Gupta by APress, ISBN - 978-1-4842-3525-6 as utilized in the previous analysis of flight delays, [I’m Going to Miss My Flight!], was utilized for this analysis. In order to properly assess imputation models, the data set [delay.clean] was extracted from the previous analysis after outliers were removed, the data was binned, and missing values were forced. To review the processes utilized to create this data set, more information can be found at the above link.

The flight variables as captured in delay.clean include:

Variable Name Variable Type Details
Carrier Categorical Airline (Carrier)
Airport Distance Numeric Distance (miles) between departure and arrival airport
Number of Flights Numeric Total number of flights at arrival airport
Weather Numeric A ranking of delays due to weather condition (0: Mild to 10: Extreme)
Support Crew Numeric Total number of support crew available
Baggage Loading Time Numeric [Binned] Total time for baggage loading (minutes)
Late Plane Arrival Numeric Delay time for plane arrival prior to flight (minutes)
Cleaning Numeric [Binned] Time required to clean plane after arrival prior to passenger loading (minutes)
Fueling Numeric Time required to fuel aircraft (minutes)
Secutiry Numeric Time required for security checks (minutes)
Arrival Delay Numeric Total flight delay time (minutes)

Table 1 Note: numeric variables (Weather & Cleaning) were binned to allow for categorical styled handling

delay.clean <- read.csv("https://nlepera.github.io/sta552/HW02/data/delay_clean.csv")
delay.clean$Cleaning_o <- factor(delay.clean$Cleaning_o, levels = (c("Immediate", "Quick",  "Moderate", "Slow", "NA")))
delay.clean$Baggage_loading_time <- factor(delay.clean$Baggage_loading_time, levels = c("Fast", "Moderate", "Slow"))

2 Missing Data Imputation

Prior to completing any analysis, the missing values in the airline delay data must be imputed to ensure no gaps in the data. In the past analysis missing values were forced in this synthetic data set to imitate the realities of data collection and standardization across this globally diverse array of airlines and airports. A summary of the missing values across each variable are included in the below Figure 1, Table 2, and Table 3. As demonstrated by the p value in Little’s MCAR Test (p = 0.6126116) the result is not statistically significant indicating that the missing values are at random and not cross correlated between variables. This highlights that the missing values between variables are not related to one another (ex: a missing value for Weather does not imply that there will also be a missing value in Cleaning Time).

plot_pattern(delay.clean, rotate = TRUE, caption = TRUE) +
  theme(axis.title.x.top = element_blank())

Figure 1
Missing value pattern visualization

kable(misty_test$result$little, col.names = c("# of Obs.", "# Missing Values", "# of Patterns in Missing Vals", "Statistic", "DF", "p Value"), caption = "Table 2:
      <br>
Little's MCAR Test") %>% 
  kable_styling()
Table 2:
Little’s MCAR Test
# of Obs. # Missing Values # of Patterns in Missing Vals Statistic DF p Value
3593 20 5 36.85526 40 0.6126116
kable(sapply(delay.clean[c(4,5,7,8)], function (x) sum(is.na(x))), col.names = c("Variable", "Count of Missing Values"), caption = 'Table 3
<br>
Summary count of missing values in each variable of the airline delay data. Variables with no missing values omitted.') %>% 
  kable_styling()
Table 3
Summary count of missing values in each variable of the airline delay data. Variables with no missing values omitted.
Variable Count of Missing Values
Weather 5
Support_Crew_Available 4
Late_Arrival_o 6
Cleaning_o 5

2.1 VIM k-Nearest Neighbor Imputation

The kNN function as found in the VIM package was utilized to impute the missing data. In order to utilize this approach the best fit k value was determined using a WSS and Silhouette graph (Figure 1). This calculation for k allows for determination of the number of nearby points that should be utilized when creating the new prediction value to fill in the blank caused by the missing value. As determined in figure 1 below, the best fit k value was determined to be 2.

With this determined k value the missing data was imputed to remove all missing variables. While this data set does not include missing categorical variable observations at this point in time, the VIM kNN imputation process utilized will successfully impute any missing categorical values should they arise in any future data collection.

delay.clust <- delay.clean[, -(c(1, 6, 8))] #creates numeric only data subset to be used multiple times

clust1 <- fviz_nbclust(delay.clust, FUN = hcut, method = "wss")
clust2 <- fviz_nbclust(delay.clust, FUN = hcut, method = "silhouette")
grid.arrange(
  arrangeGrob(clust1, clust2, ncol = 2,nrow = 1),
  top = textGrob("Optimal k Value Determination
                 ", gp = gpar(fontsize = 20, fontface = "bold")),
  bottom = textGrob("Figure 2
  
  Optimal k value determination using WSS and Silhouette methods"))

delay.kNN <- impute_knn(delay.clean, Late_Arrival_o + Weather + Support_Crew_Available + Cleaning_o ~ .)

delay.kNN <- dplyr::select(delay.kNN, !ends_with("_imp"))

kable(sapply(delay.kNN[c(4,5,7,8)], function (x) sum(is.na(x))), col.names = c("Variable", "Count of Missing Values"), caption = 'Table 4
<br>
Summary count of missing values in each variable of the airline delay data after kNN imputation. Variables with no missing values in the original airline delay data omitted.') %>% 
  kable_styling()
Table 4
Summary count of missing values in each variable of the airline delay data after kNN imputation. Variables with no missing values in the original airline delay data omitted.
Variable Count of Missing Values
Weather 0
Support_Crew_Available 0
Late_Arrival_o 0
Cleaning_o 0

2.2 Linear Regression Model Imputation

In order to develop alternative imputation methods for potential missing observations across the data set, three simple linear regression models were built. A unique regression model was built to help fill in the gaps and predict missing values the numeric variables Late Incoming Plane Arrival, Weather and Support Crews Available. Prior to completing these regression model builds, the data was subset to include only numeric variables:

  • Airport Distance
  • Number of Flights
  • Weather
  • Support Crews Available
  • Late Incoming Plane Arrival
  • Fueling Times
  • Security Times
  • Total Flight Delay

The correlation coefficients of all numeric variables were compared to select those with the highest correlation for the proposed regression prediction models. This allows for inclusion of only the variables that correlate with the response variable.

kable(cor(delay.clust[], delay.clust[c(5, 3, 4)], use = "complete.obs"), col.names = c("Variable Names", "Late Incoming Planes", "Weather", "Support Crews Available"),  caption = 'Table 5:
<br>
Calculated correlation coefficients between late incoming planes and other variables') %>% 
  kable_styling()
Table 5:
Calculated correlation coefficients between late incoming planes and other variables
Variable Names Late Incoming Planes Weather Support Crews Available
Airport_Distance 0.3189880 0.1492136 -0.1706441
Number_of_flights 0.5706051 0.2476546 -0.3019891
Weather 0.2207013 1.0000000 -0.1089384
Support_Crew_Available -0.2440386 -0.1089384 1.0000000
Late_Arrival_o 1.0000000 0.2207013 -0.2440386
Fueling_o -0.0209795 0.0112277 0.0379385
Security_o 0.0806220 0.0349761 -0.0155536
Arr_Delay 0.6670593 0.3272111 -0.3622332

2.2.1 Late Incoming Planes Prediction

As the late incoming planes variable included the largest number of missing values of the three numeric variables with missing values, late incoming planes was chosen as the response variable for the first regression model. The three variables with the largest correlation coefficients as demonstrated in the above Table 4 were selected for use in the regression model. The proposed linear regression model for late incoming plane imputation is as follows:

\[ Late \ Incoming \ Plane \ Arrival \sim Overall \ Flight \ Delay + Number \ of \ Flights + Airport \ Distance \]

delay.linear <- delay.clust #create dataset for linear imputation to not screw up clean data
late.arr.lr <- lm(Late_Arrival_o ~ Arr_Delay + Number_of_flights + Airport_Distance, data = delay.linear) #linear regression model for late incoming plane arrival

late.arr.imp <- which(is.na(delay.linear$Late_Arrival_o))
delay.linear$Late_Arrival_o[late.arr.imp] <- predict(late.arr.lr, newdata = delay.linear[late.arr.imp,])
kable(summary(late.arr.lr)$r.squared, caption = "Table 6.a:
      <br>
R.squared value of Late Incoming Plane Arrival linear regression prediction model", col.names = "R.squared Value") %>% 
  kable_styling()
Table 6.a:
R.squared value of Late Incoming Plane Arrival linear regression prediction model
R.squared Value
0.4462921

2.2.2 Weather Imputation

An additional simple linear regression equation was crafted to impute any missing weather values. The three variables with highest correlation to the weather variable were selected by again utilizing the correlation values as listed above in Table 4. The proposed linear regression model for weather imputation is as follows:

\[ Weather \sim Overall \ Flight \ Delay + Number \ of \ flights + Late \ Incoming \ Plane \ Arrival\]

weather.lr <- lm(Weather ~ Arr_Delay + Number_of_flights + Late_Arrival_o, data = delay.linear) #linear regression model for late incoming plane arrival

weather.imp <- which(is.na(delay.linear$Weather))
delay.linear$Weather[weather.imp] <- predict(weather.lr, newdata = delay.linear[weather.imp,])
kable(summary(weather.lr)$r.squared, caption = "Table 6.b:
<br>      
R.squared value of Weather linear regression imputation model", col.names = "R.squared Value") %>% 
  kable_styling()
Table 6.b:

R.squared value of Weather linear regression imputation model
R.squared Value
0.1081162

2.2.3 Available Support Crews Imputation

Finally, a third simple linear regression equation was constructed to impute the missing values for the support crews available variable. Again the variables as listed in Table 4 with the greatest correlation coefficients were selected for inclusion in the imputation model. The proposed linear regression model for support crews available imputation is as follows:

\[ Support \ Crews \ Available \sim Overall \ Flight \ Delay + Number \ of \ flights + Late \ Incoming \ Plane \ Arrival\]

sup.crew.lr <- lm(Support_Crew_Available ~ Late_Arrival_o + Number_of_flights + Arr_Delay, data = delay.linear) #linear regression model for late incoming plane arrival

sup.crew.imp <- which(is.na(delay.linear$Support_Crew_Available))
delay.linear$Support_Crew_Available[sup.crew.imp] <- predict(late.arr.lr, newdata = delay.linear[sup.crew.imp,])
kable(summary(sup.crew.lr)$r.squared, caption = "Table 6.c:
      <br>
      R.squared value of Support Crews Available linear regression imputation model", col.names = "R.squared Value") %>% 
  kable_styling()
Table 6.c:
R.squared value of Support Crews Available linear regression imputation model
R.squared Value
0.1310952

2.2.4 Linear Regression Imputation Analysis

As demonstrated by the \(R^2\) values in the above sections, all three linear regression models prove to be a rather poor fit for imputing missing values. The linear regression imputation model for Late Incoming Plane Arrival imputation demonstrated the best fit of the three, with an \(R^2\) value of 0.4462921. The closer a regression model’s \(R^2\) value is to 1, the closer fit there is between the model (imputation model in this case) and the actual data. The linear regression imputation models created for Weather and Support Crews Available were both less than 0.14 indicating very poor fits. Ideally these imputation models should not be used for practical imputation approaches when handling missing values in global airline data analysis.

2.3 Multiple Imputation

While the data set utilized for this analysis had a small number of missing values, manually knocked out of the data set for the purposes of this analysis and data manipulation, real world flight data contains multitudes of gaps. In order to accurately close these missing gaps it is wiser to run an imputation model multiple times to then calculate the predicted missing values based on the multiple imputation runs, rather than relying on a single model to accurately complete all predictions, flawlessly, each time. By utilizing this approach, known as a multiple imputation model, analysts can better close the gap in airline data while accurately reflecting the model’s uncertainty based on the current quantity and pattern of missing observations. Through a multiple imputation model the variance between each imputation run can be compared to capture a measure of the model’s uncertainty.

2.3.1 Amelia Bootstap Imputation Model

A bootstrap imputation model was created using the Amelia package to impute missing values across all variables with missing values (Weather, Support Crews Available, Late Incoming Plane Arrival, & Cleaning). For ease of utilization and reduction in overall system memory usage, 5 imputation runs were completed for each variable. While a larger number of imputation runs would result in more accurate imputation models, the number of runs was limited to 5 for simplicity of analysis and reduction of overall computing power required.

par(mfrow = c(2,2))
compare.density(amelia_imp, var = 3, legend = TRUE)
compare.density(amelia_imp, var = 4, legend = TRUE)
compare.density(amelia_imp, var = 6, legend = TRUE)
compare.density(amelia_imp, var = 7, legend = TRUE)

Figure 3
Distribution of average imputed values utilizing Amelia bootstrap imputation.

2.3.2 Combining Imputation Runs with Ruben’s Rule

In order to merge all 5 imputation runs into a single workable data frame for use and analysis, Ruben’s rule was utilized. This allowed for sample means and variance of each imputation run as well as the variance between each imputation run to be combined into a working parameters to describe the combinations of the multiple imputation model. By working with these combined parameters it allows for a generalized comparison point to compare efficacy of the various imputation models, allowing for an analyst to determine the best model for the current need.

\[ \begin{align*} Mean \ of \ Quanitity \ Of \ Interest's \ Means-> \ \overline{Q} &= \sum _{i=1} ^{m} \frac{Q_i}{m} \\ Average \ Standard \ of \ Error \ of \ Quant. \ of \ Interest-> \ \overline{U} &= \sum _{i=1} ^{m} \frac{U_i}{m} \\ Variance \ of \ Mean \ of \ Quant. \ of \ Interest-> \ B &= \frac{1}{m-1}\sum _{i=1} ^{m} (Q_i - \overline{Q})^2 \\ Estimated \ Total \ Variance \ of \ \overline{Q}-> \ T &= \overline{U} + \frac{m + 1}{m}B \end{align*} \]

n.amelia <- length(amelia_imp$imputations)

qweather <- c(mean(amelia_imp$imputations$imp1$Weather), mean(amelia_imp$imputations$imp2$Weather), mean(amelia_imp$imputations$imp3$Weather),
              mean(amelia_imp$imputations$imp4$Weather), mean(amelia_imp$imputations$imp5$Weather))
uweather <- c(std.error(amelia_imp$imputations$imp1$Weather), std.error(amelia_imp$imputations$imp2$Weather),
              std.error(amelia_imp$imputations$imp3$Weather), std.error(amelia_imp$imputations$imp4$Weather),
              std.error(amelia_imp$imputations$imp5$Weather))
q.bar.weather <- mean(qweather)
u.bar.weather <- mean(uweather)
b.weather <- var(qweather)
t.weather <- (q.bar.weather + ((n.amelia + 1)/n.amelia)*b.weather)
se.weather <- std.error(qweather)


qsupcrew <- c(mean(amelia_imp$imputations$imp1$Support_Crew_Available), mean(amelia_imp$imputations$imp2$Support_Crew_Available),
              mean(amelia_imp$imputations$imp3$Support_Crew_Available), mean(amelia_imp$imputations$imp4$Support_Crew_Available),
              mean(amelia_imp$imputations$imp5$Support_Crew_Available))
usupcrew <- c(std.error(amelia_imp$imputations$imp1$Support_Crew_Available), std.error(amelia_imp$imputations$imp2$Support_Crew_Available), 
              std.error(amelia_imp$imputations$imp3$Support_Crew_Available), std.error(amelia_imp$imputations$imp4$Support_Crew_Available), 
              std.error(amelia_imp$imputations$imp5$Support_Crew_Available))
q.bar.supcrew <- mean(qsupcrew)
u.bar.supcrew <- mean(usupcrew)
b.supcrew <- var(qsupcrew)
t.supcrew <- (q.bar.supcrew + ((n.amelia + 1)/n.amelia)*b.supcrew)
se.supcrew <- std.error(qsupcrew)


qlateincom <- c(mean(amelia_imp$imputations$imp1$Late_Arrival_o), mean(amelia_imp$imputations$imp2$Late_Arrival_o),
                mean(amelia_imp$imputations$imp3$Late_Arrival_o), mean(amelia_imp$imputations$imp4$Late_Arrival_o),
                mean(amelia_imp$imputations$imp5$Late_Arrival_o))
ulateincom <- c(std.error(amelia_imp$imputations$imp1$Late_Arrival_o), std.error(amelia_imp$imputations$imp2$Late_Arrival_o), 
                std.error(amelia_imp$imputations$imp3$Late_Arrival_o), std.error(amelia_imp$imputations$imp4$Late_Arrival_o), 
                std.error(amelia_imp$imputations$imp5$Late_Arrival_o))
q.bar.lateincom <- mean(qlateincom)
u.bar.lateincom <- mean(ulateincom)
b.lateincom <- var(qlateincom)
t.lateincom <- (q.bar.lateincom + ((n.amelia + 1)/n.amelia)*b.lateincom)
se.lateincom <- std.error(qlateincom)


qclean <- c(mean(amelia_imp$imputations$imp1$Cleaning_o), mean(amelia_imp$imputations$imp2$Cleaning_o),
                mean(amelia_imp$imputations$imp3$Cleaning_o), mean(amelia_imp$imputations$imp4$Cleaning_o),
                mean(amelia_imp$imputations$imp5$Cleaning_o))
uclean <- c(std.error(amelia_imp$imputations$imp1$Cleaning_o), std.error(amelia_imp$imputations$imp2$Cleaning_o), 
                std.error(amelia_imp$imputations$imp3$Cleaning_o), std.error(amelia_imp$imputations$imp4$Cleaning_o), 
                std.error(amelia_imp$imputations$imp5$Cleaning_o))
q.bar.clean <- mean(qclean)
u.bar.clean <- mean(uclean)
b.clean <- var(qclean)
t.clean <- (q.bar.clean + ((n.amelia + 1)/n.amelia)*b.clean)
se.clean <- std.error(qclean)


am.imp.stat <- data.frame(
  Statistic = c("Q.bar", "U.bar", "B.var", "T", "Std.Err"),
  am.weather = c(q.bar.weather, u.bar.weather, b.weather, t.weather, se.weather),
  am.sup.crew = c(q.bar.supcrew, u.bar.supcrew, b.supcrew, t.supcrew, se.supcrew),
  am.late.incoming = c(q.bar.lateincom, u.bar.lateincom, b.lateincom, t.lateincom, se.lateincom),
  am.clean = c(q.bar.clean, u.bar.clean, b.clean, t.clean, se.clean)
)
kable(am.imp.stat, caption = "Table 7:
      <br>
      Summary Statistics of Amelia Imputation Runs for Weather, Support Crews Available, Late Incoming Planes, & Cleaning Time <br> (n = 5)") %>% 
  kable_styling()
Table 7:
Summary Statistics of Amelia Imputation Runs for Weather, Support Crews Available, Late Incoming Planes, & Cleaning Time
(n = 5)
Statistic am.weather am.sup.crew am.late.incoming am.clean
Q.bar 5.3535059 84.9753369 18.7399812 2.0899498
U.bar 0.0079766 0.6916249 0.0132382 0.0103787
B.var 0.0000000 0.0006319 0.0000002 0.0000002
T 5.3535060 84.9760952 18.7399814 2.0899500
Std.Err 0.0000815 0.0112421 0.0001797 0.0001859

2.3.3 MICE Imputation Approach

A mice imputation model was then created as a comparison to the above created Amelia bootstrap multiple imputation model to allow for increased flexibility in the arrival delay predictions and to allow for better preservation of relationships between variables that may not have been observed in the initial exploratory data analysis steps. This process allowed for further improved predictive power when applying these models to real world data with a larger variety of missing data patterns than observed above in Figure 1.

No imputation method was dictated to the MICE package to allow the package to preliminary evaluate each variable with missing data and determine the appropriate imputation approach for the individual variable. This allows for increased flexibility in imputing the data, and prevents accidentally building a model that only works with the current set of data. By allowing for this variable specific flexibility, a re-determination of the best fit imputation model is completed each time the imputation model is run. Therefore the imputation model can employ best fit approach based on the currently available data, each time the imputation is run regardless of the pattern and quantity of missing observations present. This allows for real world data tuning as new flight delay data is obtained globally. Distributions of the imputed values in each of the 5 imputation runs utilized in the MICE imputation model can be seen below in Figure 3.

m.delay <- delay.clean[-1]
m.delay$Cleaning_o <- as.numeric(m.delay$Cleaning_o)
m.delay$Baggage_loading_time <-  as.numeric(m.delay$Baggage_loading_time)

mice1 <- NULL
mice1 <- mice(m.delay, m = 5, maxit = 10, seed = 123, print = FALSE)
plot(mice1, main = "MICE Model Imputation", sub = "Figure 4:
A visual representation of the mean and standard deviation of
each imputation run utilizing the MICE imputation package.

A total of 5 imputation runs were completed with 
a maximum of 10 iterations per imputation")

kable(mice1$method, col.names = c("Variable Name", "Imputation Method"), caption = "Table 8:
      <br>Imputation methods utilized for each variable.
      <br>Blank values indicate no missing observations were identified for the variable and thus no imputation was required.") %>% 
  kable_styling()
Table 8:
Imputation methods utilized for each variable.
Blank values indicate no missing observations were identified for the variable and thus no imputation was required.
Variable Name Imputation Method
Airport_Distance
Number_of_flights
Weather pmm
Support_Crew_Available pmm
Baggage_loading_time
Late_Arrival_o pmm
Cleaning_o pmm
Fueling_o
Security_o
Arr_Delay

2.3.3.1 Comparrison of MICE Imputation to Manually Created & Amelia Models

While the MICE imputation package automatically combines the imputation runs into a single best fit imputed data set, the imputation statistics for each run were calculated using the previously outlined Ruben’s rules for ease of comparison with the previously obtained Amelia bootstrap imputation model. Once these summary statistics were calculated, they were utilized to compare both imputation approaches to determine the best fit imputation model. As demonstrated below in Table 9, comparing the standard error values for each variable imputed in both models, the Amelia bootstrap imputation approach proves to be a better fit for imputing missing data.

n.mice <- length(mice1$imp$Weather)

qweather.m <- c(mean(mice1$imp$Weather[,1]), mean(mice1$imp$Weather[,2]), mean(mice1$imp$Weather[,3]), mean(mice1$imp$Weather[,4]), 
                     mean(mice1$imp$Weather[,5]))
uweather.m <- c(std.error(mice1$imp$Weather[,1]), std.error(mice1$imp$Weather[,2]), std.error(mice1$imp$Weather[,3]), 
                std.error(mice1$imp$Weather[,4]), std.error(mice1$imp$Weather[,5]))
q.bar.weather.m <- mean(qweather.m)
u.bar.weather.m <- mean(uweather.m)
b.weather.m <- var(qweather.m)
t.weather.m <- (q.bar.weather.m + ((n.mice + 1)/n.mice)*b.weather.m)
se.weather.m <- std.error(qweather.m)


qsupcrew.m <- c(mean(mice1$imp$Support_Crew_Available[,1]), mean(mice1$imp$Support_Crew_Available[,2]), mean(mice1$imp$Support_Crew_Available[,3]), 
                mean(mice1$imp$Support_Crew_Available[,4]), mean(mice1$imp$Support_Crew_Available[,5]))
usupcrew.m <- c(std.error(mice1$imp$Support_Crew_Available[,1]), std.error(mice1$imp$Support_Crew_Available[,2]), 
                std.error(mice1$imp$Support_Crew_Available[,3]), std.error(mice1$imp$Support_Crew_Available[,4]), 
                std.error(mice1$imp$Support_Crew_Available[,5]))
q.bar.supcrew.m <- mean(qsupcrew.m)
u.bar.supcrew.m <- mean(usupcrew.m)
b.supcrew.m <- var(qsupcrew.m)
t.supcrew.m <- (q.bar.supcrew.m + ((n.mice + 1)/n.mice)*b.supcrew.m)
se.supcrew.m <- std.error(qsupcrew.m)

qlateincom.m <- c(mean(mice1$imp$Late_Arrival_o[,1]), mean(mice1$imp$Late_Arrival_o[,2]), mean(mice1$imp$Late_Arrival_o[,3]), 
                  mean(mice1$imp$Late_Arrival_o[,4]), mean(mice1$imp$Late_Arrival_o[,5]))
ulateincom.m <- c(std.error(mice1$imp$Late_Arrival_o[,1]), std.error(mice1$imp$Late_Arrival_o[,2]), std.error(mice1$imp$Late_Arrival_o[,3]), 
                  std.error(mice1$imp$Late_Arrival_o[,4]), std.error(mice1$imp$Late_Arrival_o[,5]))
q.bar.lateincom.m <- mean(qlateincom.m)
u.bar.lateincom.m <- mean(ulateincom.m)
b.lateincom.m <- var(qlateincom.m)
t.lateincom.m <- (q.bar.lateincom.m + ((n.mice + 1)/n.mice)*b.lateincom.m)
se.lateincom.m <- std.error(qlateincom.m)

qclean.m <- c(mean(mice1$imp$Cleaning_o[,1]), mean(mice1$imp$Cleaning_o[,2]), mean(mice1$imp$Cleaning_o[,3]), mean(mice1$imp$Cleaning_o[,4]), 
                     mean(mice1$imp$Cleaning_o[,5]))
uclean.m <- c(std.error(mice1$imp$Cleaning_o[,1]), std.error(mice1$imp$Cleaning_o[,2]), std.error(mice1$imp$Cleaning_o[,3]), 
                std.error(mice1$imp$Cleaning_o[,4]), std.error(mice1$imp$Cleaning_o[,5]))
q.bar.clean.m <- mean(qclean.m)
u.bar.clean.m <- mean(uclean.m)
b.clean.m <- var(qclean.m)
t.clean.m <- (q.bar.clean.m + ((n.mice + 1)/n.mice)*b.clean.m)
se.clean.m <- std.error(qclean.m)


mice.merge <- am.imp.stat
mice.merge$mice.weather <- c(q.bar.weather.m, u.bar.weather.m, b.weather.m, t.weather.m, se.weather.m)
mice.merge$mice.sup.crew <- c(q.bar.supcrew.m, u.bar.supcrew.m, b.supcrew.m, t.supcrew.m, se.supcrew.m)
mice.merge$mice.late.incoming<- c(q.bar.lateincom.m, u.bar.lateincom.m, b.lateincom.m, t.lateincom.m, se.lateincom.m)
mice.merge$mice.clean<- c(q.bar.clean.m, u.bar.clean.m, b.clean.m, t.clean.m, se.clean.m)

mice.merge <- dplyr::select(mice.merge, Statistic, am.weather, mice.weather, am.sup.crew, mice.sup.crew, am.late.incoming, mice.late.incoming, am.clean, mice.clean)
kable(mice.merge, caption = "Table 9:
      <br>Comparrison of Amelia bootstrap imputation model to MICE imputation model
      <br>Prefix [am.] indicates amelia model statistics while prefix [mice.] indicates MICE cart model statistics") %>% 
  kable_styling() %>% 
  scroll_box(width = "100%")
Table 9:
Comparrison of Amelia bootstrap imputation model to MICE imputation model
Prefix [am.] indicates amelia model statistics while prefix [mice.] indicates MICE cart model statistics
Statistic am.weather mice.weather am.sup.crew mice.sup.crew am.late.incoming mice.late.incoming am.clean mice.clean
Q.bar 5.3535059 5.4000000 84.9753369 98.650000 18.7399812 18.6333333 2.0899498 2.0800000
U.bar 0.0079766 0.2269694 0.6916249 21.472021 0.0132382 0.3032956 0.0103787 0.2554809
B.var 0.0000000 0.0400000 0.0006319 369.675000 0.0000002 0.0055556 0.0000002 0.0520000
T 5.3535060 5.4480000 84.9760952 542.260000 18.7399814 18.6400000 2.0899500 2.1424000
Std.Err 0.0000815 0.0894427 0.0112421 8.598546 0.0001797 0.0333333 0.0001859 0.1019804

2.4 Linear Regression Model From Amelia Imputation for Arrival Delay Prediction

As demonstrated above in Table 9, the Amelia bootstrap imputation model proved to be the best fit approach for imputing missing flight data, therefore this imputation model was utilized to create a linear regression model for analysis of the other utilized imputation methods. Utilizing the analysis output as found above in the B.var row of Table 7 it was determined that the variance between each imputation run was negligible, allowing a single imputation run to be utilized for proposed model creation. The best fit linear regression model was created utilizing the step() function. This process was utilized to allow for an automated selection of the best fit linear regression model rather than manually assuming the best model by comparing correlation values as completed above in the Linear Regression Imputation section.

This regression model was built in order to utilize the newly imputed data to create a regression model allowing for prediction of potential overall flight arrival delays based on the available global flight data.

am.reg.full <- lm(Arr_Delay ~., data = amelia_imp$imputations$imp1)
kable(step(am.reg.full, direction = "both", trace = 0)$coef, col.names = c("Variable Names", "Coefficients"), caption = "Table 10:
      <br>Linear Regression coefficients calculated utilzing the STEP imputation process") %>% 
  kable_styling()
Table 10:
Linear Regression coefficients calculated utilzing the STEP imputation process
Variable Names Coefficients
(Intercept) -451.7150165
Airport_Distance 0.2123756
Number_of_flights 0.0052688
Weather 5.4674566
Support_Crew_Available -0.0564633
Baggage_loading_time 11.9203541
Late_Arrival_o 8.1911827

\[ \begin{align*} Arrival \ Delay &\sim Airport \ Distance + \ Number \ of \ flights + Weather \\ & \ \ \ + Support \ Crew \ Available + Baggage \ Loading \ Time + Late \ Incoming \ Plane \ Arrival \\ Arrival \ Delay &\sim 2.118e^{-1}x \ + \ 5.269e^{-3}x \ + \ 5.498x \ - \ 5.700e^{-2}x \ + 1.191e^{1}x \ + \ 8.180x \ - \ 4.514e^{2} \end{align*} \]

2.4.1 Linear Regression Model Prediction for Arrival Delay

The above created linear regression model was run on all 4 created imputation data sets to further analyze which imputation model works best for a linear regression model designed to predict total flight arrival delays based on the available flight data. To note, as the previously created linear regression imputation model was only applicable for numerical data, the Baggage Loading Time variable was removed from the regression equation only for the linear regression imputation model. This restriction of input data already implies that the linear regression imputation model is likely not the best fit approach for imputing data to then use for flight delay predictions.

am.reg <- lm(Arr_Delay ~ Airport_Distance + Number_of_flights + Weather + Support_Crew_Available + Baggage_loading_time + Late_Arrival_o, data = amelia_imp$imputations$imp1)
kNN.reg <- lm(Arr_Delay ~ Airport_Distance + Number_of_flights + Weather + Support_Crew_Available + Baggage_loading_time + Late_Arrival_o, data = delay.kNN)
lin.reg <- lm(Arr_Delay ~ Airport_Distance + Number_of_flights + Weather + Support_Crew_Available + Late_Arrival_o, data = delay.linear)
mice.reg <- lm(Arr_Delay ~ Airport_Distance + Number_of_flights + Weather + Support_Crew_Available + Baggage_loading_time + Late_Arrival_o, data = m.delay)
reg.compare <- data.frame(
  model = c("am.reg", "kNN.reg", "lin.reg", "mice.reg"),
  r.squared = c(summary(am.reg)$r.squared, summary(kNN.reg)$r.squared, summary(lin.reg)$r.squared, summary(mice.reg)$r.squared)
)

kable(reg.compare, col.names = c("Imputation Model", 'R$^2$ Value'), caption = "Table 11:
      <br>Comparrison of utilizing various imputation models for linear regression model prediction of flight delays") %>% 
  kable_styling()
Table 11:
Comparrison of utilizing various imputation models for linear regression model prediction of flight delays
Imputation Model R\(^2\) Value
am.reg 0.7888451
kNN.reg 0.8095868
lin.reg 0.7709490
mice.reg 0.7889214

As demonstrated above, the k Nearest Neighbor imputation model resulted in the highest R\(^2\) value indicating that the k Nearest Neighbor (delay.kNN) imputation model worked best for predicting overall flight delays when utilizing the step-wise created linear regression model. This was followed up by the MICE imputation model and the Amelia bootstrap imputation model in a near tie for second place. This allows for selection of the best imputation approach for imputing any missing global flight data prior to predicting total flight delays. Due to the high R\(^2\) value the k Nearest Neighbor imputation approach will be utilized going forward for analysis.

3 Feature Transformation

In the last analysis of this data set the total support crew members available, total arrival delays for planes needed for outgoing flights, baggage loading times, and fueling times were analyzed for their impacts on the overall flight delays. These variables were re-assessed alongside the remaining variables to determine if these three variables previously selected were truly the best predictor variables for flight delays. The following variables not investigated in the first analysis were reviewed alongside the previously mentioned variables:

  • Airport Distance
  • Number of Flights
  • Cleaning Time
  • Security Time
  • Airline Carrier
  • Weather

3.1 Skew & Distribution Check

In order to determine if any of the variable data is skewed, the continuous variable distributions were plotted with an overlay indicating the standard normal distribution as calculated with the respective variable’s calculated mean and standard deviation. This check was completed to ensure the data as available will cooperate with the prediction models utilized later in the analysis. As a majority of prediction models require the assumption of a normal distribution, the calculated normal distribution curve for each respective variable was overlaid as a comparison reference. In order to properly leverage the prediction models to optimize airport planning to reduce flight delays, it is imperative to ensure the data fits within the assumptions of the prediction model utilized.

kNN.analysis <- delay.kNN
kNN.analysis$Weather <- as.numeric(kNN.analysis$Weather)
kNN.analysis$Support_Crew_Available <- as.numeric(kNN.analysis$Support_Crew_Available)
kNN.analysis$Late_Arrival_o <- as.numeric(kNN.analysis$Late_Arrival_o)

dist_dens <- ggplot(kNN.analysis, aes(x = Airport_Distance)) +
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Airport_Distance), sd = sd(kNN.analysis$Airport_Distance)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Airport_Distance)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Airport_Distance)), color = "black", linetype = "dashed") +
  labs(title = "Distance Between Departure-Arrival Airports",
       x = "Distance (miles)",
       y = "Density")

numflight_dens <- ggplot(kNN.analysis, aes(x = Number_of_flights)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Number_of_flights), sd = sd(kNN.analysis$Number_of_flights)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Number_of_flights)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Number_of_flights)), color = "black", linetype = "dashed") +
  labs(title = "Total Flights per Airport *",
       x = "Flights per Airport",
       y = NULL)

supcrew_dens <- ggplot(kNN.analysis, aes(x = Support_Crew_Available)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Support_Crew_Available), sd = sd(kNN.analysis$Support_Crew_Available, na.rm = TRUE)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Support_Crew_Available)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Support_Crew_Available)), color = "black", linetype = "dashed") +
  labs(title = "Total Support Crew Members Available *",
       x = "Support Crew Available",
       y = NULL)

fueling_dens <- ggplot(kNN.analysis, aes(x = Fueling_o)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Fueling_o), sd = sd(kNN.analysis$Fueling_o)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Fueling_o)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Fueling_o)), color = "black", linetype = "dashed") +
  labs(title = "Plane Fueling Times",
       x = "Fueling Time (minutes)",
       y = NULL) 

security_dens <- ggplot(kNN.analysis, aes(x = Security_o)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Security_o), sd = sd(kNN.analysis$Security_o)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Security_o)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Security_o)), color = "black", linetype = "dashed") +
  labs(title = "Total Security Times",
       x = "Time (minutes)",
       y = "Density")  

arrival_dens <- ggplot(kNN.analysis, aes(x = Arr_Delay)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Arr_Delay), sd = sd(kNN.analysis$Arr_Delay)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Arr_Delay)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Arr_Delay)), color = "black", linetype = "dashed") +
  labs(title = "Total Flight Delays",
       x = "Duration (minutes)",
       y = "Density")  

lateincom_dens <- ggplot(kNN.analysis, aes(x = Late_Arrival_o)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Late_Arrival_o), sd = sd(kNN.analysis$Late_Arrival_o)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Late_Arrival_o)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Late_Arrival_o)), color = "black", linetype = "dashed") +
  labs(title = "Late Incomig Plane Delays *",
       x = "Duration (minutes)",
       y = "Density")  

weather_dens <- ggplot(kNN.analysis, aes(x = Weather)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(kNN.analysis$Weather), sd = sd(kNN.analysis$Weather)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(kNN.analysis$Weather)), color = "black") +
  geom_vline(aes(xintercept = median(kNN.analysis$Weather)), color = "black", linetype = "dashed") +
  labs(title = "Weather Conditions",
       x = "Ranking of Weather Severity",
       y = "Density")  
kNN.analysis$Baggage_loading_time <- factor(kNN.analysis$Baggage_loading_time, levels = c("Slow", "Moderate", "Fast"))
kNN.analysis$Cleaning_o <- factor(kNN.analysis$Cleaning_o, levels = c("Slow", "Moderate", "Quick", "Immediate"))

baggage_bar <- ggplot(kNN.analysis, aes(x = Baggage_loading_time)) + 
  geom_bar(fill = "blue", alpha = 0.5) +
  labs(title = "Baggage Loading Times *",
       x = "Loading Duration (slowest to fastest from left to right)",
       y = "Frequency")

cleaning_bar <- ggplot(kNN.analysis, aes(x = Cleaning_o)) + 
  geom_bar(fill = "blue", alpha = 0.5) +
  labs(title = "Plane Cleaning Times *",
       x = "Cleaning Duration (slowest to fastest from left to right)",
       y = NULL)
grid.arrange(
  arrangeGrob(arrival_dens, numflight_dens, dist_dens, fueling_dens, security_dens, supcrew_dens, lateincom_dens, weather_dens, baggage_bar, cleaning_bar, ncol = 2,nrow = 5),
  top = textGrob("Distribution of Flight Variables
                 ", gp = gpar(fontsize = 20, fontface = "bold")),
  bottom = textGrob("Figure 5
  
  Graphs marked with one asterisk (*) indicate skewed data
  Black solid line: mean of variable data | Black dashed line: median of variable data"))

As demonstrated in the above Figure 4, the distance between airports and total security times hold close to the normal distribution curves. Meanwhile the total delay times for incoming planes from previous flights, fueling times, and weather severity illustrate non-normal distributions. At a cursory review these variables appear to hold to multimodal and Bernoulli distributions. Meanwhile the number of flights and total support crews available demonstrated skewed normal distributions.

3.1.1 Understanding the Skew

In order to confirm the visually identified data skews the means and medians of the following variables were compared:

  • Number of Flights
  • Support Crews Available
  • Late Incoming Planes
  • Baggage Loading Time
  • Cleaning

Upon comparison of the mean and median of the numeric variables it was identified that the late incoming planes variable was not skewed as the median and mean were identical. Frequency counts for the categorical variables also identified that the mode of these variables corroborated a normal distribution. This left the number of flights and support crews available as the two truly skewed variables requiring transformation.

kable(summary(round(kNN.analysis[c(3,5,7)]), digits = 1), caption = "Table 12:
<br>Summary statistics for variables with skewed distributions. Rounded to whole numbers as the fractions of flights and fractions of support crew members cannot exist naturally.", col.names = c("Total Flights", "Support Crews Available", "Late Incoming Planes")) %>% 
  kable_styling()
Table 12:
Summary statistics for variables with skewed distributions. Rounded to whole numbers as the fractions of flights and fractions of support crew members cannot exist naturally.
Total Flights Support Crews Available Late Incoming Planes
Min. :29475 Min. : 0 Min. :15
1st Qu.:41634 1st Qu.: 56 1st Qu.:18
Median :43424 Median : 83 Median :19
Mean :43311 Mean : 85 Mean :19
3rd Qu.:45140 3rd Qu.:112 3rd Qu.:19
Max. :53461 Max. :222 Max. :22
kable(summary(kNN.analysis[c(6, 8)]), caption = "Table 13:
<br>Frequency count to capture the mode of categorical variables with skewed distributions.", col.names = c("Baggage Loading Times", "Plane Cleaning Duration")) %>% 
  kable_styling()
Table 13:
Frequency count to capture the mode of categorical variables with skewed distributions.
Baggage Loading Times Plane Cleaning Duration
Slow : 11 Slow : 30
Moderate:2833 Moderate : 782
Fast : 749 Quick :2264
NA Immediate: 517

3.2 Variable Normalization

In order to normalize the variables for future predictive model use, min-max normalization was employed to reduce all variables to a range of [0,1]. The categorical variables were already encoded as factor variables, allowing for easy label encoding to allow for cooperation with the min-max standardization. The following equation was applied to all numeric variables (both originally numeric and forced numeric from factor categorical variables).

\[x_{standardized} = \frac{x - min(x)}{max(x) - min(x)}\]

kNN.analysis$cleaning <- as.numeric(kNN.analysis$Cleaning_o)
kNN.analysis$baggage <- as.numeric(kNN.analysis$Baggage_loading_time)

delay.std <- data.frame(
  airport_dist = (kNN.analysis$Airport_Distance - min(kNN.analysis$Airport_Distance)) / (max(kNN.analysis$Airport_Distance) - min(kNN.analysis$Airport_Distance)),
  num_flights = (kNN.analysis$Number_of_flights - min(kNN.analysis$Number_of_flights)) / (max(kNN.analysis$Number_of_flights) - min(kNN.analysis$Number_of_flights)),
  weather = (kNN.analysis$Weather - min(kNN.analysis$Weather)) / (max(kNN.analysis$Weather) - min(kNN.analysis$Weather)),
  sup_crew = (kNN.analysis$Support_Crew_Available - min(kNN.analysis$Support_Crew_Available)) / (max(kNN.analysis$Support_Crew_Available) - min(kNN.analysis$Support_Crew_Available)),
  late_incoming = (kNN.analysis$Late_Arrival_o - min(kNN.analysis$Late_Arrival_o)) / (max(kNN.analysis$Late_Arrival_o) - min(kNN.analysis$Late_Arrival_o)),
  fueling = (kNN.analysis$Fueling_o - min(kNN.analysis$Fueling_o)) / (max(kNN.analysis$Fueling_o) - min(kNN.analysis$Fueling_o)),
  security = (kNN.analysis$Security_o - min(kNN.analysis$Security_o)) / (max(kNN.analysis$Security_o) - min(kNN.analysis$Security_o)),
  arr_delay = (kNN.analysis$Arr_Delay - min(kNN.analysis$Arr_Delay)) / (max(kNN.analysis$Arr_Delay) - min(kNN.analysis$Arr_Delay)),
  cleaning = (kNN.analysis$cleaning - min(kNN.analysis$cleaning)) / (max(kNN.analysis$cleaning) - min(kNN.analysis$cleaning)),
  baggage = (kNN.analysis$baggage - min(kNN.analysis$baggage)) / (max(kNN.analysis$baggage) - min(kNN.analysis$baggage))
)

kable(summary(delay.std), caption = "Table 14:
      <br>Summary statistics of normalized flight data") %>% 
  kable_styling() %>% 
  scroll_box(width = "100%")
Table 14:
Summary statistics of normalized flight data
airport_dist num_flights weather sup_crew late_incoming fueling security arr_delay cleaning baggage
Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
1st Qu.:0.4472 1st Qu.:0.5069 1st Qu.:0.0000 1st Qu.:0.2523 1st Qu.:0.4286 1st Qu.:0.4348 1st Qu.:0.3800 1st Qu.:0.2722 1st Qu.:0.6667 1st Qu.:0.5000
Median :0.5447 Median :0.5815 Median :0.0000 Median :0.3739 Median :0.5714 Median :0.5217 Median :0.4800 Median :0.3889 Median :0.6667 Median :0.5000
Mean :0.5395 Mean :0.5768 Mean :0.3532 Mean :0.3828 Mean :0.5342 Mean :0.5222 Mean :0.4817 Mean :0.3878 Mean :0.6365 Mean :0.6027
3rd Qu.:0.6341 3rd Qu.:0.6531 3rd Qu.:1.0000 3rd Qu.:0.5045 3rd Qu.:0.5714 3rd Qu.:0.6087 3rd Qu.:0.5800 3rd Qu.:0.5000 3rd Qu.:0.6667 3rd Qu.:0.5000
Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000

Min-max normalization was chosen to allow for future use with feature magnitude sensitive algorithms, such as k Nearest Neighbors cluster analysis.

3.3 Feature Selection

In order to simplify the data for analysis and predictive modeling, Recursive Feature Elimination (RFE) was employed utilizing the caret package. This allows for removal of redundancy in the data set through a random forest model using 10 iterations of cross validation to determine which variables are most essential for creating prediction models for flight delays. Reducing redundancy not only reduces the computational burden of predictive models while accounting for any potential feature interactions between variables. In order to prevent over fitting using RFE, an 80:20 training:test set was created using delay.caret to identify the most important variables for prediction of overall flight delays (arr_delay).

A visualization of the importance of each variable in potential prediction of overall flight delays is included below in Figure 6. As demonstrated below there is a steep drop off in importance for total cleaning time (cleaning), fueling times (fueling), & security times (security); therefore these variables will be omitted from future analysis to reduce computational burden and prevent redundancies.

x.caret <- delay.std[,-8]
y.caret <- delay.std[,8]

set.seed(12345)
inTrain <- createDataPartition(y.caret, p = 0.80, list = FALSE)[,1]

x.caret.train <- x.caret[inTrain, ]
x.caret.test <- x.caret[-inTrain, ]
y.caret.train <- y.caret[inTrain]
y.caret.test <- y.caret[-inTrain]


control <- rfeControl(functions = rfFuncs, method = "cv", repeats = 10, number = 15)

delay.caret <- rfe(x = x.caret.train, y.caret.train , sizes = c(1:5), rfeControl = control)
delay.caret.var <- data.frame(feature = row.names(varImp(delay.caret))[1:9],
                              importance = varImp(delay.caret)[1:9, 1])

accuracy.caret <- postResample(predict(delay.caret, x.caret.test), y.caret.test)

ggplot(delay.caret.var, aes(x = reorder(feature, -importance), y = importance, fill = feature)) +
  geom_bar(stat="identity") +
  labs(x = "Features", y = "Variable Importance", title = "Variable Importance for All Features", caption = "Figure 6:
       Importance of each variable in global flight data for predicting flight delays") + 
  geom_text(aes(label = round(importance, 2)), vjust = 0, color = "black", size = 4) +
  theme_bw() + 
  theme(legend.position = "none",
        plot.caption = element_text(hjust = 0.5))

3.4 Feature Creation

In order to properly utilize the 6 most important variables for prediction of overall flight delays as outlined above in Figure 6 a two step feature creation process was utilized. Initially a principal component analysis was conducted to combine these 6 variables into principal components that capture a combination of these 6 variables. Utilizing the PCA approach allows for a more streamlined approach for creating prediction models for overall flight delay times. Once transformed into principal components, a k-means cluster analysis was then conducted to add for an additional feature to utilize when creating flight delay prediction models.

3.4.1 Principal Component Analysis

Utilizing the most important variables as identified in figure 6 (number of flights per airport, late incoming plane, baggage loading time, distance between airports, support crews available, and weather) a principal component analysis was conducted to further reduce redundancies. An initial scree plot was utilized to determine the “elbow” cutoff of PC1 & PC2 where the variance steeply cuts off. Upon calculation of the total percentage of variance, it was found that in order to create a model that accounts for >90% of the variance in the total flight delay data, the first 5 PCs must be utilized.

delay.std.km <- delay.std[-c(6, 7:9)]

pca.delay <- prcomp(delay.std.km, scale = TRUE)
pca.delay.1 <- cbind(delay.std.km, pca.delay$x)

pca.var <- apply(pca.delay$x, 2, var)
plot(pca.delay, type ="l", ylim=c(0,4), xlim=c(1,6.5), main = NULL)
text((1:10)+0.15, pca.var+0.3, as.character(round(pca.var, 3)), cex = 0.7)
title(main = "Variance of Individual PCs", xlab ="Principal Components", sub = "Figure 7: Scree Plot to determine number of PCs to use")
points(x = 2, y = (pca.delay$sdev[2])^2, pch = 19, col = "purple", cex = 1.5)
text(x = 2, y = 0.5, "61.938%
     of Var.", col = "purple", cex = 0.6)
points(x = 5, y = (pca.delay$sdev[5])^2, pch = 19, col = "darkred", cex = 1.5)
text(x = 5, y = 0.2, "91.953%
     of Var.", col = "darkred", cex = 0.6)

kable(pca.delay$rotation[,c(1:5)], caption = "Table 15: 
      <br>Principal Component Coefficients (rotation matrix)") %>% 
  kable_styling()
Table 15:
Principal Component Coefficients (rotation matrix)
PC1 PC2 PC3 PC4 PC5
airport_dist 0.3707838 0.0943230 -0.4560362 0.7932004 -0.0346455
num_flights 0.5154782 0.0538654 -0.1096925 -0.1820407 -0.0161357
weather 0.2581120 -0.8924380 0.3276163 0.1706329 0.0040928
sup_crew -0.2988248 -0.4332730 -0.8057221 -0.2618513 0.0513166
late_incoming 0.4677295 0.0212573 -0.1274563 -0.3872228 -0.6697073
baggage -0.4712883 -0.0598225 0.0850466 0.3001186 -0.7398523

3.4.2 k-Means Clustering Feature Creation

Utilizing the 5 created principal components, k-Means clustering was completed to create clusters to aid in total flight delay prediction. In order to determine the best fit number of clusters, a wss and silhouette plots were created as an initial analysis of the PCA data. As demonstrated below in Figure 7 was determined that the best fit number of clusters for this transformed PCA data was 2 clusters. Utilizing that pre-determined number of clusters, the PCA data was then clustered into 2 groups, using a total of 25 run throughs of the clustering process to best determine the center point of the clusters and reduce overall cluster overlap.

pca.delay.2 <- pca.delay.1 %>% 
  dplyr::select("PC1", "PC2", "PC3", "PC4", "PC5") 

kmeans.wss <- fviz_nbclust(pca.delay.2, FUN = hcut, method = "wss")
kmeans.sil <- fviz_nbclust(pca.delay.2, FUN = hcut, method = "silhouette")
grid.arrange(
  arrangeGrob(kmeans.wss, kmeans.sil, ncol = 2,nrow = 1),
  top = textGrob("Optimal Cluster Determination", gp = gpar(fontsize = 20, fontface = "bold")),
  bottom = textGrob("Figure 7
  
  Determination of optimal number of clusters for PCA overall flight data"))

set.seed(12345)
delay.kmean <- kmeans(pca.delay.2, centers =  2, nstart = 25)
pca.delay.2$cluster <- as.factor(delay.kmean$cluster)
pca.delay.final <- pca.delay.2
pca.delay.final$arr_delay <- delay.std$arr_delay
eigen <- round(get_eigenvalue(pca.delay), 1)
var.eigen <- eigen$variance.percent
cluster_centers <- aggregate(pca.delay.2, by=list(cluster = pca.delay.2$cluster), FUN = mean) %>% 
  dplyr::select(-7)
ggscatter(pca.delay.2, x = "PC1", y = "PC2",
          color = "cluster",
          palette = "npg",
          ellipse = TRUE,
          ellipse.type = "convex",
          legend = "right",
          xlab = paste("PC1 (",var.eigen[1],"%)"),
          ylab = paste("PC2 (", var.eigen[2],"%)")) +
  geom_point(data = cluster_centers, shape = 18, size = 4, color = "black") +
  labs(title = "Cluster Analysis", caption = "Figure 8
  Clustering of PC1 & PC2 using k-Means Cluster analysis. Black diamonds represent cluster means.") + 
  theme_bw() +
  theme(plot.caption = element_text(hjust = 0.5))

4 Conclusions

In a further analysis of global airline data, it has been determined that in order to best create real time prediction models for overall flight delay, the raw global flight data must undergo a litany of preparation steps. First the missing data must be imputed utilizing the best fit approach for the current pattern of missing observations. Once the best fit imputation process was determined, the imputed data must undergo feature transformation to normalize any skew to prevent any unnecessary weighting or skew in prediction models. Normalization is imperative for global flight data as the variables contain widely different units and scales that would prevent a clean analysis without normalization. For example, without normalization number of flights at the departing airport and fueling times would not cooperate in a prediction model due to the massive difference in minimums and maximums of these variables.

After normalization the data then must be analyzed to identify and remove redundancy to prevent over-fitting of prediction models and reduce overall computational burden during the model creation process. As the amount of global flight data is robust, this step is essential in creating both quick and accurate prediction models should they be intended for real time flight delay prediction. Lastly, prior to model creation, the data must undergo additional feature creation such as principal component analysis (PCA) and k-Means clustering to combine the numerous variables into a reduced data set without loss of actual data. The creation of an additional categorical variable through k-Means clustering utilizing the aggregate PCA data by providing the future prediction models with meaningful pre-analysis data to improve prediction accuracy while further preventing over-fitting.

LS0tDQp0aXRsZTogIldpbGwgV2UgTGFuZCBPbiBUaW1lPyA8aW1nIHNyYz1cImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUxL0hXMDEvaW1nL3Blbmd1aW5fY3V0ZS5wbmdcIiBzdHlsZT1cImZsb2F0OiByaWdodDsgd2lkdGg6IDEyJVwiLz4iDQpzdWJ0aXRsZTogIlByZXBhcmF0aW9uIG9mIEdsb2JhbCBGbGlnaHQgRGF0YSBmb3IgUHJlZGljdGl2ZSBGbGlnaHQgRGVsYXkgTW9kZWxpbmciDQphdXRob3I6DQotIG5hbWU6IE5hdGFsaWUgTGVQZXJhDQogIGFmZmlsaWF0aW9uOiBXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSB8IFNUQTU1MiAtIEhXIDAyDQpkYXRlOiAiMTkgRmViIDIwMjUiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICB0b2NfY29sbGFwc2U6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGZpZ19hbGlnbjogY2VudGVyDQogICAgZGZfcHJpbnQ6IGthYmxlDQotLS0NCg0KYGBge2NzcywgZWNobyA9IEZBTFNFfQ0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgY29sb3I6IGRhcmttYWdlbnRhIDsNCn0NCmgxLnN1YnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtd2VpZ2h0OmJvbGQ7DQogIGNvbG9yOiBkYXJrbWFnZW50YSA7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7DQogIGJhY2tncm91bmQtY29sb3I6d2hpdGU7DQp9DQoNCi5oaWdobGlnaHRtZSB7IA0KICBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgDQp9DQoNCnAgeyANCiAgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgDQp9DQoNCmg1IHsNCiAgY29sb3I6IG5hdnk7DQp9DQoNCi5pZnJhbWUgew0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmE6bGluayB7DQogIGNvbG9yOiBkYXJrbWFnZW50YTsNCn0NCg0KLmZpZ2xhYmVsIHsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBjb2xvcjogc2xhdGVncmF5Ow0KICBmb250LXN0eWxlOiBpdGFsaWM7DQogIGZvbnQtc2l6ZTogMTg7DQp9DQoNCi50ZDEgew0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KdGgsIHRkIHsNCiAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNkZGQ7DQogIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCnRyOmhvdmVyIHtiYWNrZ3JvdW5kLWNvbG9yOiBjb3JhbDt9DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJkcGx5ciIpDQp9DQppZiAoIXJlcXVpcmUoIm1pc3R5IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtaXN0eSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtaXN0eSIpDQp9DQoNCmlmICghcmVxdWlyZSgicGx5ciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwbHlyIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJzdHJpbmdyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzdHJpbmdyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInN0cmluZ3IiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBsb3RseSIpDQp9DQoNCmlmICghcmVxdWlyZSgicGFuZG9jIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kb2MiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGFuZG9jIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJncmlkRXh0cmEiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJncmlkRXh0cmEiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90cml4IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90cml4IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBsb3RyaXgiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdyaWQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyaWQiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ3JpZCIpDQp9DQppZiAoIXJlcXVpcmUoInJhc3RlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicmFzdGVyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJhc3RlciIpDQp9DQppZiAoIXJlcXVpcmUoImRic2NhbiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZGJzY2FuIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImRic2NhbiIpDQp9DQppZiAoIXJlcXVpcmUoInBST0MiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicFJPQyIpDQp9DQppZiAoIXJlcXVpcmUoImdncmlkZ2VzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ3JpZGdlcyIpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJrbml0ciIpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIkdHYWxseSIpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dscG90MiIpDQp9DQppZiAoIXJlcXVpcmUoImNsdXN0ZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY2x1c3RlciIpDQp9DQppZiAoIXJlcXVpcmUoImthYmxlRXh0cmEiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImthYmxlRXh0cmEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgia2FibGVFeHRyYSIpDQp9DQppZiAoIXJlcXVpcmUoImZvcmNhdHMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImZvcmNhdHMiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZm9yY2F0cyIpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJycGFydCIpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicnBhcnQucGxvdCIpDQp9DQppZiAoIXJlcXVpcmUoIm1ldGFuIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtZXRhbiIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtZXRhbiIpDQp9DQogaWYgKCFyZXF1aXJlKCJmYWN0b2V4dHJhIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgiZmFjdG9leHRyYSIpDQogfQ0KIGlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJwYW5kZXIiKQ0KIH0NCg0KIGlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoImdncGxvdDIiKQ0KIH0NCg0KIGlmICghcmVxdWlyZSgiTUFTUyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoIk1BU1MiKQ0KIH0NCiBpZiAoIXJlcXVpcmUoInNpbXB1dGF0aW9uIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInNpbXB1dGF0aW9uIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoInNpbXB1dGF0aW9uIikNCiB9DQogaWYgKCFyZXF1aXJlKCJtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgibWljZSIpDQogfQ0KIGlmICghcmVxdWlyZSgiZ2dtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImdnbWljZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJnZ21pY2UiKQ0KIH0NCmlmICghcmVxdWlyZSgiQW1lbGlhIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiQW1lbGlhIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgbGlicmFyeSgiQW1lbGlhIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgbGlicmFyeSgiY2FyZXQiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdncHViciIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImdncHViciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkoImdncHViciIpDQp9DQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicpDQoNCm9wdGlvbnMoRFQub3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUsIHNjcm9sbFggPSBUUlVFKSkNCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQpBcyBkaXNjdXNzZWQgaW4gdGhlIHByZXZpb3VzIGFuYWx5c2lzIG9mIGZsaWdodCBkZWxheXMsIDxhIGhyZWYgPSAiaHR0cHM6Ly9ubGVwZXJhLmdpdGh1Yi5pby9zdGE1NTIvSFcwMS9pbmRleCI+W0knbSBHb2luZyB0byBNaXNzIE15IEZsaWdodCFdPC9hPiwgYWlyIHRyYXZlbCBpcyBhIHBpbGxhciBvZiBvdXIgZ2xvYmFsaXplZCB3b3JsZCwgdXBob2xkaW5nIHRyYWRlLCBtb3ZlbWVudCBvZiBwZXJzb25zLCBhbmQgZmFjaWxpdGF0aW5nIGVjb25vbWljIGdyb3d0aC4gQXMgb3V0bGluZWQgYnkgdGhlIDxhIGhyZWY9Imh0dHBzOi8vYXRhZy5vcmcvZmFjdHMtZmlndXJlcyI+QWlyIFRyYW5zcG9ydCBBY3Rpb24gR3JvdXA8L2E+LCBpbiAyMDIzIGFsb25lLCBhIHRvdGFsIG9mIDEsMTM4IGFpcmxpbmVzIHdlcmUgb3BlcmF0aW5nIGEgdG90YWwgb2YgMjksMDM5IHBsYW5lcyBhY3Jvc3MgNjcsMzAwIGdsb2JhbCByb3V0ZXMsIGNvbm5lY3RpbmcgMjEsMDAwIHVuaXF1ZSBwYWlycyBvZiBjaXRpZXMuICBUaGUgdmFzdCBzaXplIG9mIHRoaXMgdHJhbnNpdCBuZXR3b3JrIHByZXNlbnRzIHVuaXF1ZSBjaGFsbGVuZ2VzIHdoZW4gd29ya2luZyB0byBhbmFseXplIGZsaWdodCBkYXRhIHdpdGggdGhlIGdvYWwgb2YgcmVkdWNpbmcgZmxpZ2h0IGRlbGF5cy4gIA0KDQpJdCBpcyBuZWFyIGltcG9zc2libGUgdG8gZW5zdXJlIHN0YW5kYXJkaXplZCBkYXRhIGNvbGxlY3Rpb24gZm9ybWF0cyBnbG9iYWxseSwgcmVzdWx0aW5nIGluIGluY29tcGxldGUgZGF0YSBzZXRzIHdpdGggbWlzc2luZyB2YWx1ZXMuICBJbiBvcmRlciB0byBwcm9wZXJseSBhbmFseXplIGZsaWdodCBkYXRhIHRvIHN1Y2Nlc3NmdWxseSByZWR1Y2UgZmxpZ2h0IGRlbGF5IHRpbWVzLCBjb25zaWRlcmF0aW9ucyBtdXN0IGJlIHRha2VuIHRvIHByZWRpY3Qgc2FpZCBtaXNzaW5nIHZhbHVlcywgZXNzZW50aWFsbHkgZmlsbGluZyBpbiB0aGUgZ2Fwcy4gSW4gdGhlIHdvcmxkIG9mIGRhdGEgc2NpZW5jZSwgZmlsbGluZyBpbiB0aGVzZSBkYXRhIGdhcHMgaXMga25vd24gYXMgaW1wdXRhdGlvbi4gIFRoZSBmb2xsb3dpbmcgaW1wdXRhdGlvbnMgd2VyZSBhbmFseXplZCB0byBkZXRlcm1pbmUgdGhlIGJlc3QgZml0IGFwcHJvYWNoOg0KDQogIC0gSG90ZGVjayAoay1uZWFyZXN0IG5laWdoYm9yKQ0KICAtIExpbmVhciBSZWdyZXNzaW9uIE1vZGVscw0KICAtIEFtZWxpYSBCb290c3RyYXAgSW1wdXRhdGlvbg0KICAtIE1JQ0UgUmVncmVzc2lvbg0KICANCk9uY2UgdGhlIGJlc3QgaW1wdXRhdGlvbiBtZXRob2Qgd2FzIHNlbGVjdGVkLCB0aGUgZGF0YSB3YXMgdGhlbiBhbmFseXNlZCBmb3IgYW55IHBvdGVudGlhbCBza2V3LCBhbmQgc3Vic2VxdWVudGx5IG5vcm1hbGl6ZWQgdG8gcmVkdWNlIHNrZXcgJiBwcmVwYXJlIHRoZSBkYXRhIGZvciBwcmVkaWN0aXZlIG1vZGVsaW5nIHVzaW5nIG1pbi1tYXggbm9ybWFsaXphdGlvbi4gIFRoZSBub3JtYWxpemVkIGRhdGEgd2FzIHRoZW4gYW5hbHl6ZWQgZm9yIHJlZHVuZGFuY2llcyBhbmQgb25seSB0aGUgaGlnaCBpbXBhY3QgdmFyaWFibGVzIHJldGFpbmVkIHRocm91Z2ggUkZFIGZlYXR1cmUgc2VsZWN0aW9uLiAgT25jZSBmdWxseSBwcnVuZWQgdGhlIGRhdGEgd2FzIHRoZW4gc3ViamVjdGVkIHRvIGstTmVhcmVzdCBOZWlnaGJvciBjbHVzdGVyIGJhc2VkIGZlYXR1cmUgZXh0cmFjdGlvbi4gIFRoaXMgZmluYWwgZXh0cmFjdGVkIGNsdXN0ZXJlZCBkYXRhIGlzIHRoZW4gcmVhZHkgZm9yIHVzZSBpbiB2YXJpb3VzIHByZWRpY3RpdmUgbW9kZWxzIHRvIGlkZW50aWZ5IGFuZCBwcmVkaWN0IGZsaWdodCBkZWxheXMgYmVmb3JlIHRoZXkgaGFwcGVuIGJhc2VkIG9uIHByZXZpb3VzbHkgY29sbGVjdGVkIGdsb2JhbCBmbGlnaHQgZGF0YS4gDQogIA0KDQojIyBEYXRhIERldGFpbHMgDQoNClRoZSBGbGlnaHQgZGVsYXkgZGF0YSBhcyBjb2xsZWN0ZWQgZnJvbSA8aT5BcHBsaWVkIEFuYWx5dGljcyB0aHJvdWdoIENhc2UgU3R1ZGllcyBVc2luZyBTQVMgYW5kIFIsIERlZXB0aSBHdXB0YSBieSBBUHJlc3MsIElTQk4gLSA5NzgtMS00ODQyLTM1MjUtNjwvaT4gYXMgdXRpbGl6ZWQgaW4gdGhlIHByZXZpb3VzIGFuYWx5c2lzIG9mIGZsaWdodCBkZWxheXMsIDxhIGhyZWYgPSAiaHR0cHM6Ly9ubGVwZXJhLmdpdGh1Yi5pby9zdGE1NTIvSFcwMS9pbmRleCI+W0knbSBHb2luZyB0byBNaXNzIE15IEZsaWdodCFdPC9hPiwgd2FzIHV0aWxpemVkIGZvciB0aGlzIGFuYWx5c2lzLiAgSW4gb3JkZXIgdG8gcHJvcGVybHkgYXNzZXNzIGltcHV0YXRpb24gbW9kZWxzLCB0aGUgZGF0YSBzZXQgYFtkZWxheS5jbGVhbl1gIHdhcyBleHRyYWN0ZWQgZnJvbSB0aGUgcHJldmlvdXMgYW5hbHlzaXMgYWZ0ZXIgb3V0bGllcnMgd2VyZSByZW1vdmVkLCB0aGUgZGF0YSB3YXMgYmlubmVkLCBhbmQgbWlzc2luZyB2YWx1ZXMgd2VyZSBmb3JjZWQuICBUbyByZXZpZXcgdGhlIHByb2Nlc3NlcyB1dGlsaXplZCB0byBjcmVhdGUgdGhpcyBkYXRhIHNldCwgbW9yZSBpbmZvcm1hdGlvbiBjYW4gYmUgZm91bmQgYXQgdGhlIGFib3ZlIGxpbmsuIA0KDQpUaGUgZmxpZ2h0IHZhcmlhYmxlcyBhcyBjYXB0dXJlZCBpbiBgZGVsYXkuY2xlYW5gIGluY2x1ZGU6DQoNCjx0YWJsZSBzdHlsZT0id2lkdGg6MTAwJSI+DQo8dGhlYWQ+PHRyPg0KPHRoPlZhcmlhYmxlIE5hbWU8L3RoPg0KPHRoPlZhcmlhYmxlIFR5cGU8L3RoPg0KPHRoPkRldGFpbHM8L3RoPg0KPC90cj48L3RoZWFkPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkNhcnJpZXI8L3RkPjx0ZD5DYXRlZ29yaWNhbDwvdGQ+PHRkPkFpcmxpbmUgKENhcnJpZXIpPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkFpcnBvcnQgRGlzdGFuY2U8L3RkPjx0ZD5OdW1lcmljPC90ZD48dGQ+RGlzdGFuY2UgKG1pbGVzKSBiZXR3ZWVuIGRlcGFydHVyZSBhbmQgYXJyaXZhbCBhaXJwb3J0PC90ZD4NCjx0cj48dGQgY2xhc3MgPSAidGQxIj5OdW1iZXIgb2YgRmxpZ2h0czwvdGQ+PHRkPk51bWVyaWM8L3RkPjx0ZD5Ub3RhbCBudW1iZXIgb2YgZmxpZ2h0cyBhdCBhcnJpdmFsIGFpcnBvcnQ8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+V2VhdGhlcjwvdGQ+PHRkPk51bWVyaWM8L3RkPjx0ZD5BIHJhbmtpbmcgb2YgZGVsYXlzIGR1ZSB0byB3ZWF0aGVyIGNvbmRpdGlvbiAoMDogTWlsZCB0byAxMDogRXh0cmVtZSk8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+U3VwcG9ydCBDcmV3PC90ZD48dGQ+TnVtZXJpYzwvdGQ+PHRkPlRvdGFsIG51bWJlciBvZiBzdXBwb3J0IGNyZXcgYXZhaWxhYmxlPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkJhZ2dhZ2UgTG9hZGluZyBUaW1lPC90ZD48dGQ+TnVtZXJpYyBbQmlubmVkXTwvdGQ+PHRkPlRvdGFsIHRpbWUgZm9yIGJhZ2dhZ2UgbG9hZGluZyAobWludXRlcyk8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+TGF0ZSBQbGFuZSBBcnJpdmFsPC90ZD48dGQ+TnVtZXJpYzwvdGQ+PHRkPkRlbGF5IHRpbWUgZm9yIHBsYW5lIGFycml2YWwgcHJpb3IgdG8gZmxpZ2h0IChtaW51dGVzKTwvdGQ+PC90cj4NCjx0cj48dGQgY2xhc3MgPSAidGQxIj5DbGVhbmluZzwvdGQ+PHRkPk51bWVyaWMgW0Jpbm5lZF08L3RkPjx0ZD5UaW1lIHJlcXVpcmVkIHRvIGNsZWFuIHBsYW5lIGFmdGVyIGFycml2YWwgcHJpb3IgdG8gcGFzc2VuZ2VyIGxvYWRpbmcgKG1pbnV0ZXMpPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkZ1ZWxpbmc8L3RkPjx0ZD5OdW1lcmljPC90ZD48dGQ+VGltZSByZXF1aXJlZCB0byBmdWVsIGFpcmNyYWZ0IChtaW51dGVzKTwvdGQ+PC90cj4NCjx0cj48dGQgY2xhc3MgPSAidGQxIj5TZWN1dGlyeTwvdGQ+PHRkPk51bWVyaWM8L3RkPjx0ZD5UaW1lIHJlcXVpcmVkIGZvciBzZWN1cml0eSBjaGVja3MgKG1pbnV0ZXMpPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkFycml2YWwgRGVsYXk8L3RkPjx0ZD5OdW1lcmljPC90ZD48dGQ+VG90YWwgZmxpZ2h0IGRlbGF5IHRpbWUgKG1pbnV0ZXMpPC90ZD48L3RyPg0KPC90YWJsZT4NCg0KPGZvbnQgY2xhc3MgPSAiZmlnbGFiZWwiPlRhYmxlIDENCk5vdGU6IG51bWVyaWMgdmFyaWFibGVzIChXZWF0aGVyICYgQ2xlYW5pbmcpIHdlcmUgYmlubmVkIHRvIGFsbG93IGZvciBjYXRlZ29yaWNhbCBzdHlsZWQgaGFuZGxpbmc8L2ZvbnQ+DQoNCmBgYHtyfQ0KZGVsYXkuY2xlYW4gPC0gcmVhZC5jc3YoImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUyL0hXMDIvZGF0YS9kZWxheV9jbGVhbi5jc3YiKQ0KZGVsYXkuY2xlYW4kQ2xlYW5pbmdfbyA8LSBmYWN0b3IoZGVsYXkuY2xlYW4kQ2xlYW5pbmdfbywgbGV2ZWxzID0gKGMoIkltbWVkaWF0ZSIsICJRdWljayIsICAiTW9kZXJhdGUiLCAiU2xvdyIsICJOQSIpKSkNCmRlbGF5LmNsZWFuJEJhZ2dhZ2VfbG9hZGluZ190aW1lIDwtIGZhY3RvcihkZWxheS5jbGVhbiRCYWdnYWdlX2xvYWRpbmdfdGltZSwgbGV2ZWxzID0gYygiRmFzdCIsICJNb2RlcmF0ZSIsICJTbG93IikpDQpgYGANCg0KDQojIE1pc3NpbmcgRGF0YSBJbXB1dGF0aW9uDQoNClByaW9yIHRvIGNvbXBsZXRpbmcgYW55IGFuYWx5c2lzLCB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGFpcmxpbmUgZGVsYXkgZGF0YSBtdXN0IGJlIGltcHV0ZWQgdG8gZW5zdXJlIG5vIGdhcHMgaW4gdGhlIGRhdGEuICBJbiB0aGUgcGFzdCBhbmFseXNpcyBtaXNzaW5nIHZhbHVlcyB3ZXJlIGZvcmNlZCBpbiB0aGlzIHN5bnRoZXRpYyBkYXRhIHNldCB0byBpbWl0YXRlIHRoZSByZWFsaXRpZXMgb2YgZGF0YSBjb2xsZWN0aW9uIGFuZCBzdGFuZGFyZGl6YXRpb24gYWNyb3NzIHRoaXMgZ2xvYmFsbHkgZGl2ZXJzZSBhcnJheSBvZiBhaXJsaW5lcyBhbmQgYWlycG9ydHMuICBBIHN1bW1hcnkgb2YgdGhlIG1pc3NpbmcgdmFsdWVzIGFjcm9zcyBlYWNoIHZhcmlhYmxlIGFyZSBpbmNsdWRlZCBpbiB0aGUgYmVsb3cgRmlndXJlIDEsIFRhYmxlIDIsIGFuZCBUYWJsZSAzLiAgQXMgZGVtb25zdHJhdGVkIGJ5IHRoZSBwIHZhbHVlIGluIExpdHRsZSdzIE1DQVIgVGVzdCA8aT4ocCA9IDAuNjEyNjExNik8L2k+IHRoZSByZXN1bHQgaXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW5kaWNhdGluZyB0aGF0IHRoZSBtaXNzaW5nIHZhbHVlcyBhcmUgYXQgcmFuZG9tIGFuZCBub3QgY3Jvc3MgY29ycmVsYXRlZCBiZXR3ZWVuIHZhcmlhYmxlcy4gIFRoaXMgaGlnaGxpZ2h0cyB0aGF0IHRoZSBtaXNzaW5nIHZhbHVlcyBiZXR3ZWVuIHZhcmlhYmxlcyBhcmUgbm90IHJlbGF0ZWQgdG8gb25lIGFub3RoZXIgKGV4OiBhIG1pc3NpbmcgdmFsdWUgZm9yIFdlYXRoZXIgZG9lcyBub3QgaW1wbHkgdGhhdCB0aGVyZSB3aWxsIGFsc28gYmUgYSBtaXNzaW5nIHZhbHVlIGluIENsZWFuaW5nIFRpbWUpLiANCg0KYGBge3IsIGNvbnNvbGUgPSBGQUxTRX0NCnBsb3RfcGF0dGVybihkZWxheS5jbGVhbiwgcm90YXRlID0gVFJVRSwgY2FwdGlvbiA9IFRSVUUpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54LnRvcCA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQo8Zm9udCBjbGFzcyA9ICJmaWdsYWJlbCI+RmlndXJlIDENCjxicj5NaXNzaW5nIHZhbHVlIHBhdHRlcm4gdmlzdWFsaXphdGlvbjwvZm9udD4NCg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCm1pc3R5X3Rlc3QgPC0gbmEudGVzdChkZWxheS5jbGVhbikNCmBgYA0KDQpgYGB7cn0NCmthYmxlKG1pc3R5X3Rlc3QkcmVzdWx0JGxpdHRsZSwgY29sLm5hbWVzID0gYygiIyBvZiBPYnMuIiwgIiMgTWlzc2luZyBWYWx1ZXMiLCAiIyBvZiBQYXR0ZXJucyBpbiBNaXNzaW5nIFZhbHMiLCAiU3RhdGlzdGljIiwgIkRGIiwgInAgVmFsdWUiKSwgY2FwdGlvbiA9ICJUYWJsZSAyOg0KICAgICAgPGJyPg0KTGl0dGxlJ3MgTUNBUiBUZXN0IikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpgYGB7cn0NCmthYmxlKHNhcHBseShkZWxheS5jbGVhbltjKDQsNSw3LDgpXSwgZnVuY3Rpb24gKHgpIHN1bShpcy5uYSh4KSkpLCBjb2wubmFtZXMgPSBjKCJWYXJpYWJsZSIsICJDb3VudCBvZiBNaXNzaW5nIFZhbHVlcyIpLCBjYXB0aW9uID0gJ1RhYmxlIDMNCjxicj4NClN1bW1hcnkgY291bnQgb2YgbWlzc2luZyB2YWx1ZXMgaW4gZWFjaCB2YXJpYWJsZSBvZiB0aGUgYWlybGluZSBkZWxheSBkYXRhLiBWYXJpYWJsZXMgd2l0aCBubyBtaXNzaW5nIHZhbHVlcyBvbWl0dGVkLicpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0KYGBgDQoNCiMjIFZJTSBrLU5lYXJlc3QgTmVpZ2hib3IgSW1wdXRhdGlvbg0KDQpUaGUgYGtOTmAgZnVuY3Rpb24gYXMgZm91bmQgaW4gdGhlIGBWSU1gIHBhY2thZ2Ugd2FzIHV0aWxpemVkIHRvIGltcHV0ZSB0aGUgbWlzc2luZyBkYXRhLiAgSW4gb3JkZXIgdG8gdXRpbGl6ZSB0aGlzIGFwcHJvYWNoIHRoZSBiZXN0IGZpdCBrIHZhbHVlIHdhcyBkZXRlcm1pbmVkIHVzaW5nIGEgV1NTIGFuZCBTaWxob3VldHRlIGdyYXBoIChGaWd1cmUgMSkuICBUaGlzIGNhbGN1bGF0aW9uIGZvciBrIGFsbG93cyBmb3IgZGV0ZXJtaW5hdGlvbiBvZiB0aGUgbnVtYmVyIG9mIG5lYXJieSBwb2ludHMgdGhhdCBzaG91bGQgYmUgdXRpbGl6ZWQgd2hlbiBjcmVhdGluZyB0aGUgbmV3IHByZWRpY3Rpb24gdmFsdWUgdG8gZmlsbCBpbiB0aGUgYmxhbmsgY2F1c2VkIGJ5IHRoZSBtaXNzaW5nIHZhbHVlLiBBcyBkZXRlcm1pbmVkIGluIGZpZ3VyZSAxIGJlbG93LCB0aGUgYmVzdCBmaXQgayB2YWx1ZSB3YXMgZGV0ZXJtaW5lZCB0byBiZSA8Yj48dT4yPC91PjwvYj4uDQoNCldpdGggdGhpcyBkZXRlcm1pbmVkIGsgdmFsdWUgdGhlIG1pc3NpbmcgZGF0YSB3YXMgaW1wdXRlZCB0byByZW1vdmUgYWxsIG1pc3NpbmcgdmFyaWFibGVzLiAgV2hpbGUgdGhpcyBkYXRhIHNldCBkb2VzIG5vdCBpbmNsdWRlIG1pc3NpbmcgY2F0ZWdvcmljYWwgdmFyaWFibGUgb2JzZXJ2YXRpb25zIGF0IHRoaXMgcG9pbnQgaW4gdGltZSwgdGhlIFZJTSBrTk4gaW1wdXRhdGlvbiBwcm9jZXNzIHV0aWxpemVkIHdpbGwgc3VjY2Vzc2Z1bGx5IGltcHV0ZSBhbnkgbWlzc2luZyBjYXRlZ29yaWNhbCB2YWx1ZXMgc2hvdWxkIHRoZXkgYXJpc2UgaW4gYW55IGZ1dHVyZSBkYXRhIGNvbGxlY3Rpb24uIA0KDQpgYGB7cn0NCmRlbGF5LmNsdXN0IDwtIGRlbGF5LmNsZWFuWywgLShjKDEsIDYsIDgpKV0gI2NyZWF0ZXMgbnVtZXJpYyBvbmx5IGRhdGEgc3Vic2V0IHRvIGJlIHVzZWQgbXVsdGlwbGUgdGltZXMNCg0KY2x1c3QxIDwtIGZ2aXpfbmJjbHVzdChkZWxheS5jbHVzdCwgRlVOID0gaGN1dCwgbWV0aG9kID0gIndzcyIpDQpjbHVzdDIgPC0gZnZpel9uYmNsdXN0KGRlbGF5LmNsdXN0LCBGVU4gPSBoY3V0LCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpDQoNCmBgYA0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGggPSAxMCwgb3V0LndpZHRoPSAiMTAwJSJ9DQpncmlkLmFycmFuZ2UoDQogIGFycmFuZ2VHcm9iKGNsdXN0MSwgY2x1c3QyLCBuY29sID0gMixucm93ID0gMSksDQogIHRvcCA9IHRleHRHcm9iKCJPcHRpbWFsIGsgVmFsdWUgRGV0ZXJtaW5hdGlvbg0KICAgICAgICAgICAgICAgICAiLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpKSwNCiAgYm90dG9tID0gdGV4dEdyb2IoIkZpZ3VyZSAyDQogIA0KICBPcHRpbWFsIGsgdmFsdWUgZGV0ZXJtaW5hdGlvbiB1c2luZyBXU1MgYW5kIFNpbGhvdWV0dGUgbWV0aG9kcyIpKQ0KYGBgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KDQpkZWxheS5rTk4gPC0gaW1wdXRlX2tubihkZWxheS5jbGVhbiwgTGF0ZV9BcnJpdmFsX28gKyBXZWF0aGVyICsgU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSArIENsZWFuaW5nX28gfiAuKQ0KDQpkZWxheS5rTk4gPC0gZHBseXI6OnNlbGVjdChkZWxheS5rTk4sICFlbmRzX3dpdGgoIl9pbXAiKSkNCg0Ka2FibGUoc2FwcGx5KGRlbGF5LmtOTltjKDQsNSw3LDgpXSwgZnVuY3Rpb24gKHgpIHN1bShpcy5uYSh4KSkpLCBjb2wubmFtZXMgPSBjKCJWYXJpYWJsZSIsICJDb3VudCBvZiBNaXNzaW5nIFZhbHVlcyIpLCBjYXB0aW9uID0gJ1RhYmxlIDQNCjxicj4NClN1bW1hcnkgY291bnQgb2YgbWlzc2luZyB2YWx1ZXMgaW4gZWFjaCB2YXJpYWJsZSBvZiB0aGUgYWlybGluZSBkZWxheSBkYXRhIGFmdGVyIGtOTiBpbXB1dGF0aW9uLiBWYXJpYWJsZXMgd2l0aCBubyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgb3JpZ2luYWwgYWlybGluZSBkZWxheSBkYXRhIG9taXR0ZWQuJykgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQojIyBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCBJbXB1dGF0aW9uDQoNCkluIG9yZGVyIHRvIGRldmVsb3AgYWx0ZXJuYXRpdmUgaW1wdXRhdGlvbiBtZXRob2RzIGZvciBwb3RlbnRpYWwgbWlzc2luZyBvYnNlcnZhdGlvbnMgYWNyb3NzIHRoZSBkYXRhIHNldCwgdGhyZWUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyB3ZXJlIGJ1aWx0LiBBIHVuaXF1ZSByZWdyZXNzaW9uIG1vZGVsIHdhcyBidWlsdCB0byBoZWxwIGZpbGwgaW4gdGhlIGdhcHMgYW5kIHByZWRpY3QgbWlzc2luZyB2YWx1ZXMgdGhlIG51bWVyaWMgdmFyaWFibGVzIGBMYXRlIEluY29taW5nIFBsYW5lIEFycml2YWxgLCBgV2VhdGhlcmAgYW5kIGBTdXBwb3J0IENyZXdzIEF2YWlsYWJsZWAuIFByaW9yIHRvIGNvbXBsZXRpbmcgdGhlc2UgcmVncmVzc2lvbiBtb2RlbCBidWlsZHMsIHRoZSBkYXRhIHdhcyBzdWJzZXQgdG8gaW5jbHVkZSBvbmx5IG51bWVyaWMgdmFyaWFibGVzOg0KDQogIC0gQWlycG9ydCBEaXN0YW5jZQ0KICAtIE51bWJlciBvZiBGbGlnaHRzDQogIC0gV2VhdGhlcg0KICAtIFN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlDQogIC0gTGF0ZSBJbmNvbWluZyBQbGFuZSBBcnJpdmFsDQogIC0gRnVlbGluZyBUaW1lcw0KICAtIFNlY3VyaXR5IFRpbWVzDQogIC0gVG90YWwgRmxpZ2h0IERlbGF5DQoNClRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgb2YgYWxsIG51bWVyaWMgdmFyaWFibGVzIHdlcmUgY29tcGFyZWQgdG8gc2VsZWN0IHRob3NlIHdpdGggdGhlIGhpZ2hlc3QgY29ycmVsYXRpb24gZm9yIHRoZSBwcm9wb3NlZCByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWxzLiAgVGhpcyBhbGxvd3MgZm9yIGluY2x1c2lvbiBvZiA8aT5vbmx5PC9pPiB0aGUgdmFyaWFibGVzIHRoYXQgY29ycmVsYXRlIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiANCg0KYGBge3J9DQprYWJsZShjb3IoZGVsYXkuY2x1c3RbXSwgZGVsYXkuY2x1c3RbYyg1LCAzLCA0KV0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwgY29sLm5hbWVzID0gYygiVmFyaWFibGUgTmFtZXMiLCAiTGF0ZSBJbmNvbWluZyBQbGFuZXMiLCAiV2VhdGhlciIsICJTdXBwb3J0IENyZXdzIEF2YWlsYWJsZSIpLCAgY2FwdGlvbiA9ICdUYWJsZSA1Og0KPGJyPg0KQ2FsY3VsYXRlZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYmV0d2VlbiBsYXRlIGluY29taW5nIHBsYW5lcyBhbmQgb3RoZXIgdmFyaWFibGVzJykgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQojIyMgTGF0ZSBJbmNvbWluZyBQbGFuZXMgUHJlZGljdGlvbg0KDQpBcyB0aGUgbGF0ZSBpbmNvbWluZyBwbGFuZXMgdmFyaWFibGUgaW5jbHVkZWQgdGhlIGxhcmdlc3QgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIG9mIHRoZSB0aHJlZSBudW1lcmljIHZhcmlhYmxlcyB3aXRoIG1pc3NpbmcgdmFsdWVzLCBsYXRlIGluY29taW5nIHBsYW5lcyB3YXMgY2hvc2VuIGFzIHRoZSByZXNwb25zZSB2YXJpYWJsZSBmb3IgdGhlIGZpcnN0IHJlZ3Jlc3Npb24gbW9kZWwuIFRoZSB0aHJlZSB2YXJpYWJsZXMgd2l0aCB0aGUgbGFyZ2VzdCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYXMgZGVtb25zdHJhdGVkIGluIHRoZSBhYm92ZSBUYWJsZSA0IHdlcmUgc2VsZWN0ZWQgZm9yIHVzZSBpbiB0aGUgcmVncmVzc2lvbiBtb2RlbC4gVGhlIHByb3Bvc2VkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciBsYXRlIGluY29taW5nIHBsYW5lIGltcHV0YXRpb24gaXMgYXMgZm9sbG93czogDQoNCiQkIExhdGUgXCBJbmNvbWluZyBcIFBsYW5lIFwgQXJyaXZhbCBcc2ltIE92ZXJhbGwgXCBGbGlnaHQgXCBEZWxheSArIE51bWJlciBcIG9mIFwgRmxpZ2h0cyArIEFpcnBvcnQgXCBEaXN0YW5jZSAkJA0KDQpgYGB7cn0NCmRlbGF5LmxpbmVhciA8LSBkZWxheS5jbHVzdCAjY3JlYXRlIGRhdGFzZXQgZm9yIGxpbmVhciBpbXB1dGF0aW9uIHRvIG5vdCBzY3JldyB1cCBjbGVhbiBkYXRhDQpsYXRlLmFyci5sciA8LSBsbShMYXRlX0Fycml2YWxfbyB+IEFycl9EZWxheSArIE51bWJlcl9vZl9mbGlnaHRzICsgQWlycG9ydF9EaXN0YW5jZSwgZGF0YSA9IGRlbGF5LmxpbmVhcikgI2xpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciBsYXRlIGluY29taW5nIHBsYW5lIGFycml2YWwNCg0KbGF0ZS5hcnIuaW1wIDwtIHdoaWNoKGlzLm5hKGRlbGF5LmxpbmVhciRMYXRlX0Fycml2YWxfbykpDQpkZWxheS5saW5lYXIkTGF0ZV9BcnJpdmFsX29bbGF0ZS5hcnIuaW1wXSA8LSBwcmVkaWN0KGxhdGUuYXJyLmxyLCBuZXdkYXRhID0gZGVsYXkubGluZWFyW2xhdGUuYXJyLmltcCxdKQ0KYGBgDQoNCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KGxhdGUuYXJyLmxyKSRyLnNxdWFyZWQsIGNhcHRpb24gPSAiVGFibGUgNi5hOg0KICAgICAgPGJyPg0KUi5zcXVhcmVkIHZhbHVlIG9mIExhdGUgSW5jb21pbmcgUGxhbmUgQXJyaXZhbCBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsIiwgY29sLm5hbWVzID0gIlIuc3F1YXJlZCBWYWx1ZSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KIyMjIFdlYXRoZXIgSW1wdXRhdGlvbg0KDQpBbiBhZGRpdGlvbmFsIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBlcXVhdGlvbiB3YXMgY3JhZnRlZCB0byBpbXB1dGUgYW55IG1pc3Npbmcgd2VhdGhlciB2YWx1ZXMuICBUaGUgdGhyZWUgdmFyaWFibGVzIHdpdGggaGlnaGVzdCBjb3JyZWxhdGlvbiB0byB0aGUgd2VhdGhlciB2YXJpYWJsZSB3ZXJlIHNlbGVjdGVkIGJ5IGFnYWluIHV0aWxpemluZyB0aGUgY29ycmVsYXRpb24gdmFsdWVzIGFzIGxpc3RlZCBhYm92ZSBpbiBUYWJsZSA0LiAgVGhlIHByb3Bvc2VkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciB3ZWF0aGVyIGltcHV0YXRpb24gaXMgYXMgZm9sbG93czoNCg0KJCQgV2VhdGhlciBcc2ltIE92ZXJhbGwgXCBGbGlnaHQgXCBEZWxheSArIE51bWJlciBcIG9mIFwgZmxpZ2h0cyArIExhdGUgXCBJbmNvbWluZyBcIFBsYW5lIFwgQXJyaXZhbCQkDQoNCmBgYHtyfQ0Kd2VhdGhlci5sciA8LSBsbShXZWF0aGVyIH4gQXJyX0RlbGF5ICsgTnVtYmVyX29mX2ZsaWdodHMgKyBMYXRlX0Fycml2YWxfbywgZGF0YSA9IGRlbGF5LmxpbmVhcikgI2xpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciBsYXRlIGluY29taW5nIHBsYW5lIGFycml2YWwNCg0Kd2VhdGhlci5pbXAgPC0gd2hpY2goaXMubmEoZGVsYXkubGluZWFyJFdlYXRoZXIpKQ0KZGVsYXkubGluZWFyJFdlYXRoZXJbd2VhdGhlci5pbXBdIDwtIHByZWRpY3Qod2VhdGhlci5sciwgbmV3ZGF0YSA9IGRlbGF5LmxpbmVhclt3ZWF0aGVyLmltcCxdKQ0KYGBgDQoNCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KHdlYXRoZXIubHIpJHIuc3F1YXJlZCwgY2FwdGlvbiA9ICJUYWJsZSA2LmI6DQo8YnI+ICAgICAgDQpSLnNxdWFyZWQgdmFsdWUgb2YgV2VhdGhlciBsaW5lYXIgcmVncmVzc2lvbiBpbXB1dGF0aW9uIG1vZGVsIiwgY29sLm5hbWVzID0gIlIuc3F1YXJlZCBWYWx1ZSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIyMgQXZhaWxhYmxlIFN1cHBvcnQgQ3Jld3MgSW1wdXRhdGlvbg0KDQpGaW5hbGx5LCBhIHRoaXJkIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBlcXVhdGlvbiB3YXMgY29uc3RydWN0ZWQgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlIHN1cHBvcnQgY3Jld3MgYXZhaWxhYmxlIHZhcmlhYmxlLiAgQWdhaW4gdGhlIHZhcmlhYmxlcyBhcyBsaXN0ZWQgaW4gVGFibGUgNCB3aXRoIHRoZSBncmVhdGVzdCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgd2VyZSBzZWxlY3RlZCBmb3IgaW5jbHVzaW9uIGluIHRoZSBpbXB1dGF0aW9uIG1vZGVsLiAgVGhlIHByb3Bvc2VkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciBzdXBwb3J0IGNyZXdzIGF2YWlsYWJsZSBpbXB1dGF0aW9uIGlzIGFzIGZvbGxvd3M6DQoNCiQkIFN1cHBvcnQgXCBDcmV3cyBcIEF2YWlsYWJsZSBcc2ltIE92ZXJhbGwgXCBGbGlnaHQgXCBEZWxheSArIE51bWJlciBcIG9mIFwgZmxpZ2h0cyArIExhdGUgXCBJbmNvbWluZyBcIFBsYW5lIFwgQXJyaXZhbCQkDQoNCmBgYHtyfQ0Kc3VwLmNyZXcubHIgPC0gbG0oU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSB+IExhdGVfQXJyaXZhbF9vICsgTnVtYmVyX29mX2ZsaWdodHMgKyBBcnJfRGVsYXksIGRhdGEgPSBkZWxheS5saW5lYXIpICNsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBmb3IgbGF0ZSBpbmNvbWluZyBwbGFuZSBhcnJpdmFsDQoNCnN1cC5jcmV3LmltcCA8LSB3aGljaChpcy5uYShkZWxheS5saW5lYXIkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSkpDQpkZWxheS5saW5lYXIkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVtzdXAuY3Jldy5pbXBdIDwtIHByZWRpY3QobGF0ZS5hcnIubHIsIG5ld2RhdGEgPSBkZWxheS5saW5lYXJbc3VwLmNyZXcuaW1wLF0pDQpgYGANCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KHN1cC5jcmV3LmxyKSRyLnNxdWFyZWQsIGNhcHRpb24gPSAiVGFibGUgNi5jOg0KICAgICAgPGJyPg0KICAgICAgUi5zcXVhcmVkIHZhbHVlIG9mIFN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlIGxpbmVhciByZWdyZXNzaW9uIGltcHV0YXRpb24gbW9kZWwiLCBjb2wubmFtZXMgPSAiUi5zcXVhcmVkIFZhbHVlIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQojIyMgTGluZWFyIFJlZ3Jlc3Npb24gSW1wdXRhdGlvbiBBbmFseXNpcw0KDQpBcyBkZW1vbnN0cmF0ZWQgYnkgdGhlICRSXjIkIHZhbHVlcyBpbiB0aGUgYWJvdmUgc2VjdGlvbnMsIGFsbCB0aHJlZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgcHJvdmUgdG8gYmUgYSByYXRoZXIgcG9vciBmaXQgZm9yIGltcHV0aW5nIG1pc3NpbmcgdmFsdWVzLiAgVGhlIGxpbmVhciByZWdyZXNzaW9uIGltcHV0YXRpb24gbW9kZWwgZm9yIGBMYXRlIEluY29taW5nIFBsYW5lIEFycml2YWxgIGltcHV0YXRpb24gZGVtb25zdHJhdGVkIHRoZSBiZXN0IGZpdCBvZiB0aGUgdGhyZWUsIHdpdGggYW4gJFJeMiQgdmFsdWUgb2YgPGI+MC40NDYyOTIxPC9iPi4gIFRoZSBjbG9zZXIgYSByZWdyZXNzaW9uIG1vZGVsJ3MgJFJeMiQgdmFsdWUgaXMgdG8gMSwgdGhlIGNsb3NlciBmaXQgdGhlcmUgaXMgYmV0d2VlbiB0aGUgbW9kZWwgKGltcHV0YXRpb24gbW9kZWwgaW4gdGhpcyBjYXNlKSBhbmQgdGhlIGFjdHVhbCBkYXRhLiAgVGhlIGxpbmVhciByZWdyZXNzaW9uIGltcHV0YXRpb24gbW9kZWxzIGNyZWF0ZWQgZm9yIGBXZWF0aGVyYCBhbmQgYFN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlYCB3ZXJlIGJvdGggbGVzcyB0aGFuIDAuMTQgaW5kaWNhdGluZyB2ZXJ5IHBvb3IgZml0cy4gIElkZWFsbHkgdGhlc2UgaW1wdXRhdGlvbiBtb2RlbHMgc2hvdWxkIDxpPm5vdDwvaT4gYmUgdXNlZCBmb3IgcHJhY3RpY2FsIGltcHV0YXRpb24gYXBwcm9hY2hlcyB3aGVuIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzIGluIGdsb2JhbCBhaXJsaW5lIGRhdGEgYW5hbHlzaXMuIA0KDQojIyBNdWx0aXBsZSBJbXB1dGF0aW9uDQoNCldoaWxlIHRoZSBkYXRhIHNldCB1dGlsaXplZCBmb3IgdGhpcyBhbmFseXNpcyBoYWQgYSBzbWFsbCBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMsIG1hbnVhbGx5IGtub2NrZWQgb3V0IG9mIHRoZSBkYXRhIHNldCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgYW5hbHlzaXMgYW5kIGRhdGEgbWFuaXB1bGF0aW9uLCByZWFsIHdvcmxkIGZsaWdodCBkYXRhIGNvbnRhaW5zIG11bHRpdHVkZXMgb2YgZ2Fwcy4gIEluIG9yZGVyIHRvIGFjY3VyYXRlbHkgY2xvc2UgdGhlc2UgbWlzc2luZyBnYXBzIGl0IGlzIHdpc2VyIHRvIHJ1biBhbiBpbXB1dGF0aW9uIG1vZGVsIG11bHRpcGxlIHRpbWVzIHRvIHRoZW4gY2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgbWlzc2luZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG11bHRpcGxlIGltcHV0YXRpb24gcnVucywgcmF0aGVyIHRoYW4gcmVseWluZyBvbiBhIHNpbmdsZSBtb2RlbCB0byBhY2N1cmF0ZWx5IGNvbXBsZXRlIGFsbCBwcmVkaWN0aW9ucywgZmxhd2xlc3NseSwgZWFjaCB0aW1lLiAgQnkgdXRpbGl6aW5nIHRoaXMgYXBwcm9hY2gsIGtub3duIGFzIGEgbXVsdGlwbGUgaW1wdXRhdGlvbiBtb2RlbCwgYW5hbHlzdHMgY2FuIGJldHRlciBjbG9zZSB0aGUgZ2FwIGluIGFpcmxpbmUgZGF0YSB3aGlsZSBhY2N1cmF0ZWx5IHJlZmxlY3RpbmcgdGhlIG1vZGVsJ3MgdW5jZXJ0YWludHkgYmFzZWQgb24gdGhlIGN1cnJlbnQgcXVhbnRpdHkgYW5kIHBhdHRlcm4gb2YgbWlzc2luZyBvYnNlcnZhdGlvbnMuICBUaHJvdWdoIGEgbXVsdGlwbGUgaW1wdXRhdGlvbiBtb2RlbCB0aGUgdmFyaWFuY2UgYmV0d2VlbiBlYWNoIGltcHV0YXRpb24gcnVuIGNhbiBiZSBjb21wYXJlZCB0byBjYXB0dXJlIGEgbWVhc3VyZSBvZiB0aGUgbW9kZWwncyB1bmNlcnRhaW50eS4gDQoNCiMjIyBBbWVsaWEgQm9vdHN0YXAgSW1wdXRhdGlvbiBNb2RlbA0KDQpBIGJvb3RzdHJhcCBpbXB1dGF0aW9uIG1vZGVsIHdhcyBjcmVhdGVkIHVzaW5nIHRoZSBgQW1lbGlhYCBwYWNrYWdlIHRvIGltcHV0ZSBtaXNzaW5nIHZhbHVlcyBhY3Jvc3MgYWxsIHZhcmlhYmxlcyB3aXRoIG1pc3NpbmcgdmFsdWVzIChgV2VhdGhlcmAsIGBTdXBwb3J0IENyZXdzIEF2YWlsYWJsZWAsIGBMYXRlIEluY29taW5nIFBsYW5lIEFycml2YWxgLCAmIGBDbGVhbmluZ2ApLiBGb3IgZWFzZSBvZiB1dGlsaXphdGlvbiBhbmQgcmVkdWN0aW9uIGluIG92ZXJhbGwgc3lzdGVtIG1lbW9yeSB1c2FnZSwgNSBpbXB1dGF0aW9uIHJ1bnMgd2VyZSBjb21wbGV0ZWQgZm9yIGVhY2ggdmFyaWFibGUuICBXaGlsZSBhIGxhcmdlciBudW1iZXIgb2YgaW1wdXRhdGlvbiBydW5zIHdvdWxkIHJlc3VsdCBpbiBtb3JlIGFjY3VyYXRlIGltcHV0YXRpb24gbW9kZWxzLCB0aGUgbnVtYmVyIG9mIHJ1bnMgd2FzIGxpbWl0ZWQgdG8gNSBmb3Igc2ltcGxpY2l0eSBvZiBhbmFseXNpcyBhbmQgcmVkdWN0aW9uIG9mIG92ZXJhbGwgY29tcHV0aW5nIHBvd2VyIHJlcXVpcmVkLiANCg0KYGBge3IsIGNvbnNvbGUgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQ0KZGVsYXkuYW1lbGlhIDwtIGRlbGF5LmNsZWFuWy0xXQ0KZGVsYXkuYW1lbGlhJEJhZ2dhZ2VfbG9hZGluZ190aW1lIDwtIGFzLm51bWVyaWMoZGVsYXkuYW1lbGlhJEJhZ2dhZ2VfbG9hZGluZ190aW1lKQ0KZGVsYXkuYW1lbGlhJENsZWFuaW5nX28gPC0gYXMubnVtZXJpYyhkZWxheS5hbWVsaWEkQ2xlYW5pbmdfbykNCmFtZWxpYV9pbXAgPC0gYW1lbGlhKGRlbGF5LmFtZWxpYSkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDEwLCBvdXQuaGVpZ2h0PSIxMDAlIn0NCnBhcihtZnJvdyA9IGMoMiwyKSkNCmNvbXBhcmUuZGVuc2l0eShhbWVsaWFfaW1wLCB2YXIgPSAzLCBsZWdlbmQgPSBUUlVFKQ0KY29tcGFyZS5kZW5zaXR5KGFtZWxpYV9pbXAsIHZhciA9IDQsIGxlZ2VuZCA9IFRSVUUpDQpjb21wYXJlLmRlbnNpdHkoYW1lbGlhX2ltcCwgdmFyID0gNiwgbGVnZW5kID0gVFJVRSkNCmNvbXBhcmUuZGVuc2l0eShhbWVsaWFfaW1wLCB2YXIgPSA3LCBsZWdlbmQgPSBUUlVFKQ0KYGBgDQoNCjxmb250IGNsYXNzID0gImZpZ2xhYmVsIj5GaWd1cmUgMw0KPGJyPkRpc3RyaWJ1dGlvbiBvZiBhdmVyYWdlIGltcHV0ZWQgdmFsdWVzIHV0aWxpemluZyBBbWVsaWEgYm9vdHN0cmFwIGltcHV0YXRpb24uPC9mb250Pg0KDQojIyMgQ29tYmluaW5nIEltcHV0YXRpb24gUnVucyB3aXRoIFJ1YmVuJ3MgUnVsZQ0KDQpJbiBvcmRlciB0byBtZXJnZSBhbGwgNSBpbXB1dGF0aW9uIHJ1bnMgaW50byBhIHNpbmdsZSB3b3JrYWJsZSBkYXRhIGZyYW1lIGZvciB1c2UgYW5kIGFuYWx5c2lzLCBSdWJlbidzIHJ1bGUgd2FzIHV0aWxpemVkLiAgVGhpcyBhbGxvd2VkIGZvciBzYW1wbGUgbWVhbnMgYW5kIHZhcmlhbmNlIG9mIGVhY2ggaW1wdXRhdGlvbiBydW4gYXMgd2VsbCBhcyB0aGUgdmFyaWFuY2UgYmV0d2VlbiBlYWNoIGltcHV0YXRpb24gcnVuIHRvIGJlIGNvbWJpbmVkIGludG8gYSB3b3JraW5nIHBhcmFtZXRlcnMgdG8gZGVzY3JpYmUgdGhlIGNvbWJpbmF0aW9ucyBvZiB0aGUgbXVsdGlwbGUgaW1wdXRhdGlvbiBtb2RlbC4gIEJ5IHdvcmtpbmcgd2l0aCB0aGVzZSBjb21iaW5lZCBwYXJhbWV0ZXJzIGl0IGFsbG93cyBmb3IgYSBnZW5lcmFsaXplZCBjb21wYXJpc29uIHBvaW50IHRvIGNvbXBhcmUgZWZmaWNhY3kgb2YgdGhlIHZhcmlvdXMgaW1wdXRhdGlvbiBtb2RlbHMsIGFsbG93aW5nIGZvciBhbiBhbmFseXN0IHRvIGRldGVybWluZSB0aGUgYmVzdCBtb2RlbCBmb3IgdGhlIGN1cnJlbnQgbmVlZC4gIA0KDQokJCANClxiZWdpbnthbGlnbip9DQpNZWFuIFwgb2YgXCBRdWFuaXRpdHkgXCBPZiBcIEludGVyZXN0J3MgXCBNZWFucy0+IFwgXG92ZXJsaW5le1F9ICY9IFxzdW0gX3tpPTF9IF57bX0gXGZyYWN7UV9pfXttfSBcXA0KQXZlcmFnZSBcIFN0YW5kYXJkIFwgb2YgXCBFcnJvciBcIG9mIFwgUXVhbnQuIFwgb2YgXCBJbnRlcmVzdC0+IFwgXG92ZXJsaW5le1V9ICY9IFxzdW0gX3tpPTF9IF57bX0gXGZyYWN7VV9pfXttfSBcXA0KVmFyaWFuY2UgXCBvZiBcIE1lYW4gXCBvZiBcIFF1YW50LiBcIG9mIFwgSW50ZXJlc3QtPiBcIEIgJj0gXGZyYWN7MX17bS0xfVxzdW0gX3tpPTF9IF57bX0gKFFfaSAtIFxvdmVybGluZXtRfSleMiBcXA0KRXN0aW1hdGVkIFwgVG90YWwgXCBWYXJpYW5jZSBcIG9mIFwgXG92ZXJsaW5le1F9LT4gXCBUICY9IFxvdmVybGluZXtVfSArIFxmcmFje20gKyAxfXttfUINClxlbmR7YWxpZ24qfQ0KJCQNCg0KYGBge3J9DQpuLmFtZWxpYSA8LSBsZW5ndGgoYW1lbGlhX2ltcCRpbXB1dGF0aW9ucykNCg0KcXdlYXRoZXIgPC0gYyhtZWFuKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wMSRXZWF0aGVyKSwgbWVhbihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDIkV2VhdGhlciksIG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAzJFdlYXRoZXIpLA0KICAgICAgICAgICAgICBtZWFuKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wNCRXZWF0aGVyKSwgbWVhbihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDUkV2VhdGhlcikpDQp1d2VhdGhlciA8LSBjKHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDEkV2VhdGhlciksIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDIkV2VhdGhlciksDQogICAgICAgICAgICAgIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDMkV2VhdGhlciksIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDQkV2VhdGhlciksDQogICAgICAgICAgICAgIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDUkV2VhdGhlcikpDQpxLmJhci53ZWF0aGVyIDwtIG1lYW4ocXdlYXRoZXIpDQp1LmJhci53ZWF0aGVyIDwtIG1lYW4odXdlYXRoZXIpDQpiLndlYXRoZXIgPC0gdmFyKHF3ZWF0aGVyKQ0KdC53ZWF0aGVyIDwtIChxLmJhci53ZWF0aGVyICsgKChuLmFtZWxpYSArIDEpL24uYW1lbGlhKSpiLndlYXRoZXIpDQpzZS53ZWF0aGVyIDwtIHN0ZC5lcnJvcihxd2VhdGhlcikNCg0KDQpxc3VwY3JldyA8LSBjKG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAxJFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpLCBtZWFuKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wMiRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSwNCiAgICAgICAgICAgICAgbWVhbihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDMkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSksIG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXA0JFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpLA0KICAgICAgICAgICAgICBtZWFuKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wNSRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSkNCnVzdXBjcmV3IDwtIGMoc3RkLmVycm9yKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wMSRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSwgc3RkLmVycm9yKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wMiRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSwgDQogICAgICAgICAgICAgIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDMkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSksIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDQkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSksIA0KICAgICAgICAgICAgICBzdGQuZXJyb3IoYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXA1JFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpKQ0KcS5iYXIuc3VwY3JldyA8LSBtZWFuKHFzdXBjcmV3KQ0KdS5iYXIuc3VwY3JldyA8LSBtZWFuKHVzdXBjcmV3KQ0KYi5zdXBjcmV3IDwtIHZhcihxc3VwY3JldykNCnQuc3VwY3JldyA8LSAocS5iYXIuc3VwY3JldyArICgobi5hbWVsaWEgKyAxKS9uLmFtZWxpYSkqYi5zdXBjcmV3KQ0Kc2Uuc3VwY3JldyA8LSBzdGQuZXJyb3IocXN1cGNyZXcpDQoNCg0KcWxhdGVpbmNvbSA8LSBjKG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAxJExhdGVfQXJyaXZhbF9vKSwgbWVhbihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDIkTGF0ZV9BcnJpdmFsX28pLA0KICAgICAgICAgICAgICAgIG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAzJExhdGVfQXJyaXZhbF9vKSwgbWVhbihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDQkTGF0ZV9BcnJpdmFsX28pLA0KICAgICAgICAgICAgICAgIG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXA1JExhdGVfQXJyaXZhbF9vKSkNCnVsYXRlaW5jb20gPC0gYyhzdGQuZXJyb3IoYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAxJExhdGVfQXJyaXZhbF9vKSwgc3RkLmVycm9yKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wMiRMYXRlX0Fycml2YWxfbyksIA0KICAgICAgICAgICAgICAgIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDMkTGF0ZV9BcnJpdmFsX28pLCBzdGQuZXJyb3IoYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXA0JExhdGVfQXJyaXZhbF9vKSwgDQogICAgICAgICAgICAgICAgc3RkLmVycm9yKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wNSRMYXRlX0Fycml2YWxfbykpDQpxLmJhci5sYXRlaW5jb20gPC0gbWVhbihxbGF0ZWluY29tKQ0KdS5iYXIubGF0ZWluY29tIDwtIG1lYW4odWxhdGVpbmNvbSkNCmIubGF0ZWluY29tIDwtIHZhcihxbGF0ZWluY29tKQ0KdC5sYXRlaW5jb20gPC0gKHEuYmFyLmxhdGVpbmNvbSArICgobi5hbWVsaWEgKyAxKS9uLmFtZWxpYSkqYi5sYXRlaW5jb20pDQpzZS5sYXRlaW5jb20gPC0gc3RkLmVycm9yKHFsYXRlaW5jb20pDQoNCg0KcWNsZWFuIDwtIGMobWVhbihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDEkQ2xlYW5pbmdfbyksIG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAyJENsZWFuaW5nX28pLA0KICAgICAgICAgICAgICAgIG1lYW4oYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAzJENsZWFuaW5nX28pLCBtZWFuKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wNCRDbGVhbmluZ19vKSwNCiAgICAgICAgICAgICAgICBtZWFuKGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wNSRDbGVhbmluZ19vKSkNCnVjbGVhbiA8LSBjKHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDEkQ2xlYW5pbmdfbyksIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDIkQ2xlYW5pbmdfbyksIA0KICAgICAgICAgICAgICAgIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDMkQ2xlYW5pbmdfbyksIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDQkQ2xlYW5pbmdfbyksIA0KICAgICAgICAgICAgICAgIHN0ZC5lcnJvcihhbWVsaWFfaW1wJGltcHV0YXRpb25zJGltcDUkQ2xlYW5pbmdfbykpDQpxLmJhci5jbGVhbiA8LSBtZWFuKHFjbGVhbikNCnUuYmFyLmNsZWFuIDwtIG1lYW4odWNsZWFuKQ0KYi5jbGVhbiA8LSB2YXIocWNsZWFuKQ0KdC5jbGVhbiA8LSAocS5iYXIuY2xlYW4gKyAoKG4uYW1lbGlhICsgMSkvbi5hbWVsaWEpKmIuY2xlYW4pDQpzZS5jbGVhbiA8LSBzdGQuZXJyb3IocWNsZWFuKQ0KDQoNCmFtLmltcC5zdGF0IDwtIGRhdGEuZnJhbWUoDQogIFN0YXRpc3RpYyA9IGMoIlEuYmFyIiwgIlUuYmFyIiwgIkIudmFyIiwgIlQiLCAiU3RkLkVyciIpLA0KICBhbS53ZWF0aGVyID0gYyhxLmJhci53ZWF0aGVyLCB1LmJhci53ZWF0aGVyLCBiLndlYXRoZXIsIHQud2VhdGhlciwgc2Uud2VhdGhlciksDQogIGFtLnN1cC5jcmV3ID0gYyhxLmJhci5zdXBjcmV3LCB1LmJhci5zdXBjcmV3LCBiLnN1cGNyZXcsIHQuc3VwY3Jldywgc2Uuc3VwY3JldyksDQogIGFtLmxhdGUuaW5jb21pbmcgPSBjKHEuYmFyLmxhdGVpbmNvbSwgdS5iYXIubGF0ZWluY29tLCBiLmxhdGVpbmNvbSwgdC5sYXRlaW5jb20sIHNlLmxhdGVpbmNvbSksDQogIGFtLmNsZWFuID0gYyhxLmJhci5jbGVhbiwgdS5iYXIuY2xlYW4sIGIuY2xlYW4sIHQuY2xlYW4sIHNlLmNsZWFuKQ0KKQ0KDQpgYGANCg0KYGBge3J9DQprYWJsZShhbS5pbXAuc3RhdCwgY2FwdGlvbiA9ICJUYWJsZSA3Og0KICAgICAgPGJyPg0KICAgICAgU3VtbWFyeSBTdGF0aXN0aWNzIG9mIEFtZWxpYSBJbXB1dGF0aW9uIFJ1bnMgZm9yIFdlYXRoZXIsIFN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlLCBMYXRlIEluY29taW5nIFBsYW5lcywgJiBDbGVhbmluZyBUaW1lIDxicj4gKG4gPSA1KSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIyMgTUlDRSBJbXB1dGF0aW9uIEFwcHJvYWNoDQoNCkEgbWljZSBpbXB1dGF0aW9uIG1vZGVsIHdhcyB0aGVuIGNyZWF0ZWQgYXMgYSBjb21wYXJpc29uIHRvIHRoZSBhYm92ZSBjcmVhdGVkIEFtZWxpYSBib290c3RyYXAgbXVsdGlwbGUgaW1wdXRhdGlvbiBtb2RlbCB0byBhbGxvdyBmb3IgaW5jcmVhc2VkIGZsZXhpYmlsaXR5IGluIHRoZSBhcnJpdmFsIGRlbGF5IHByZWRpY3Rpb25zIGFuZCB0byBhbGxvdyBmb3IgYmV0dGVyIHByZXNlcnZhdGlvbiBvZiByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIHRoYXQgbWF5IG5vdCBoYXZlIGJlZW4gb2JzZXJ2ZWQgaW4gdGhlIGluaXRpYWwgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyBzdGVwcy4gIFRoaXMgcHJvY2VzcyBhbGxvd2VkIGZvciBmdXJ0aGVyIGltcHJvdmVkIHByZWRpY3RpdmUgcG93ZXIgd2hlbiBhcHBseWluZyB0aGVzZSBtb2RlbHMgdG8gcmVhbCB3b3JsZCBkYXRhIHdpdGggYSBsYXJnZXIgdmFyaWV0eSBvZiBtaXNzaW5nIGRhdGEgcGF0dGVybnMgdGhhbiBvYnNlcnZlZCBhYm92ZSBpbiBgRmlndXJlIDFgLiANCg0KTm8gaW1wdXRhdGlvbiBtZXRob2Qgd2FzIGRpY3RhdGVkIHRvIHRoZSBNSUNFIHBhY2thZ2UgdG8gYWxsb3cgdGhlIHBhY2thZ2UgdG8gcHJlbGltaW5hcnkgZXZhbHVhdGUgZWFjaCB2YXJpYWJsZSB3aXRoIG1pc3NpbmcgZGF0YSBhbmQgZGV0ZXJtaW5lIHRoZSBhcHByb3ByaWF0ZSBpbXB1dGF0aW9uIGFwcHJvYWNoIGZvciB0aGUgaW5kaXZpZHVhbCB2YXJpYWJsZS4gIFRoaXMgYWxsb3dzIGZvciBpbmNyZWFzZWQgZmxleGliaWxpdHkgaW4gaW1wdXRpbmcgdGhlIGRhdGEsIGFuZCBwcmV2ZW50cyBhY2NpZGVudGFsbHkgYnVpbGRpbmcgYSBtb2RlbCB0aGF0IG9ubHkgd29ya3Mgd2l0aCB0aGUgY3VycmVudCBzZXQgb2YgZGF0YS4gIEJ5IGFsbG93aW5nIGZvciB0aGlzIHZhcmlhYmxlIHNwZWNpZmljIGZsZXhpYmlsaXR5LCBhIHJlLWRldGVybWluYXRpb24gb2YgdGhlIGJlc3QgZml0IGltcHV0YXRpb24gbW9kZWwgaXMgY29tcGxldGVkIGVhY2ggdGltZSB0aGUgaW1wdXRhdGlvbiBtb2RlbCBpcyBydW4uICBUaGVyZWZvcmUgdGhlIGltcHV0YXRpb24gbW9kZWwgY2FuIGVtcGxveSBiZXN0IGZpdCBhcHByb2FjaCBiYXNlZCBvbiB0aGUgY3VycmVudGx5IGF2YWlsYWJsZSBkYXRhLCBlYWNoIHRpbWUgdGhlIGltcHV0YXRpb24gaXMgcnVuIHJlZ2FyZGxlc3Mgb2YgdGhlIHBhdHRlcm4gYW5kIHF1YW50aXR5IG9mIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHByZXNlbnQuICBUaGlzIGFsbG93cyBmb3IgcmVhbCB3b3JsZCBkYXRhIHR1bmluZyBhcyBuZXcgZmxpZ2h0IGRlbGF5IGRhdGEgaXMgb2J0YWluZWQgZ2xvYmFsbHkuICBEaXN0cmlidXRpb25zIG9mIHRoZSBpbXB1dGVkIHZhbHVlcyBpbiBlYWNoIG9mIHRoZSA1IGltcHV0YXRpb24gcnVucyB1dGlsaXplZCBpbiB0aGUgTUlDRSBpbXB1dGF0aW9uIG1vZGVsIGNhbiBiZSBzZWVuIGJlbG93IGluIEZpZ3VyZSAzLiAgDQoNCg0KYGBge3J9DQptLmRlbGF5IDwtIGRlbGF5LmNsZWFuWy0xXQ0KbS5kZWxheSRDbGVhbmluZ19vIDwtIGFzLm51bWVyaWMobS5kZWxheSRDbGVhbmluZ19vKQ0KbS5kZWxheSRCYWdnYWdlX2xvYWRpbmdfdGltZSA8LSAgYXMubnVtZXJpYyhtLmRlbGF5JEJhZ2dhZ2VfbG9hZGluZ190aW1lKQ0KDQptaWNlMSA8LSBOVUxMDQptaWNlMSA8LSBtaWNlKG0uZGVsYXksIG0gPSA1LCBtYXhpdCA9IDEwLCBzZWVkID0gMTIzLCBwcmludCA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChtaWNlMSwgbWFpbiA9ICJNSUNFIE1vZGVsIEltcHV0YXRpb24iLCBzdWIgPSAiRmlndXJlIDQ6DQpBIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mDQplYWNoIGltcHV0YXRpb24gcnVuIHV0aWxpemluZyB0aGUgTUlDRSBpbXB1dGF0aW9uIHBhY2thZ2UuDQoNCkEgdG90YWwgb2YgNSBpbXB1dGF0aW9uIHJ1bnMgd2VyZSBjb21wbGV0ZWQgd2l0aCANCmEgbWF4aW11bSBvZiAxMCBpdGVyYXRpb25zIHBlciBpbXB1dGF0aW9uIikNCmthYmxlKG1pY2UxJG1ldGhvZCwgY29sLm5hbWVzID0gYygiVmFyaWFibGUgTmFtZSIsICJJbXB1dGF0aW9uIE1ldGhvZCIpLCBjYXB0aW9uID0gIlRhYmxlIDg6DQogICAgICA8YnI+SW1wdXRhdGlvbiBtZXRob2RzIHV0aWxpemVkIGZvciBlYWNoIHZhcmlhYmxlLg0KICAgICAgPGJyPkJsYW5rIHZhbHVlcyBpbmRpY2F0ZSBubyBtaXNzaW5nIG9ic2VydmF0aW9ucyB3ZXJlIGlkZW50aWZpZWQgZm9yIHRoZSB2YXJpYWJsZSBhbmQgdGh1cyBubyBpbXB1dGF0aW9uIHdhcyByZXF1aXJlZC4iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpgYGANCg0KIyMjIyBDb21wYXJyaXNvbiBvZiBNSUNFIEltcHV0YXRpb24gdG8gTWFudWFsbHkgQ3JlYXRlZCAmIEFtZWxpYSBNb2RlbHMNCg0KV2hpbGUgdGhlIE1JQ0UgaW1wdXRhdGlvbiBwYWNrYWdlIGF1dG9tYXRpY2FsbHkgY29tYmluZXMgdGhlIGltcHV0YXRpb24gcnVucyBpbnRvIGEgc2luZ2xlIGJlc3QgZml0IGltcHV0ZWQgZGF0YSBzZXQsIHRoZSBpbXB1dGF0aW9uIHN0YXRpc3RpY3MgZm9yIGVhY2ggcnVuIHdlcmUgY2FsY3VsYXRlZCB1c2luZyB0aGUgcHJldmlvdXNseSBvdXRsaW5lZCBSdWJlbidzIHJ1bGVzIGZvciBlYXNlIG9mIGNvbXBhcmlzb24gd2l0aCB0aGUgcHJldmlvdXNseSBvYnRhaW5lZCBBbWVsaWEgYm9vdHN0cmFwIGltcHV0YXRpb24gbW9kZWwuICBPbmNlIHRoZXNlIHN1bW1hcnkgc3RhdGlzdGljcyB3ZXJlIGNhbGN1bGF0ZWQsIHRoZXkgd2VyZSB1dGlsaXplZCB0byBjb21wYXJlIGJvdGggaW1wdXRhdGlvbiBhcHByb2FjaGVzIHRvIGRldGVybWluZSB0aGUgYmVzdCBmaXQgaW1wdXRhdGlvbiBtb2RlbC4gIEFzIGRlbW9uc3RyYXRlZCBiZWxvdyBpbiBUYWJsZSA5LCBjb21wYXJpbmcgdGhlIHN0YW5kYXJkIGVycm9yIHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZSBpbXB1dGVkIGluIGJvdGggbW9kZWxzLCB0aGUgQW1lbGlhIGJvb3RzdHJhcCBpbXB1dGF0aW9uIGFwcHJvYWNoIHByb3ZlcyB0byBiZSBhIGJldHRlciBmaXQgZm9yIGltcHV0aW5nIG1pc3NpbmcgZGF0YS4gIA0KDQpgYGB7cn0NCm4ubWljZSA8LSBsZW5ndGgobWljZTEkaW1wJFdlYXRoZXIpDQoNCnF3ZWF0aGVyLm0gPC0gYyhtZWFuKG1pY2UxJGltcCRXZWF0aGVyWywxXSksIG1lYW4obWljZTEkaW1wJFdlYXRoZXJbLDJdKSwgbWVhbihtaWNlMSRpbXAkV2VhdGhlclssM10pLCBtZWFuKG1pY2UxJGltcCRXZWF0aGVyWyw0XSksIA0KICAgICAgICAgICAgICAgICAgICAgbWVhbihtaWNlMSRpbXAkV2VhdGhlclssNV0pKQ0KdXdlYXRoZXIubSA8LSBjKHN0ZC5lcnJvcihtaWNlMSRpbXAkV2VhdGhlclssMV0pLCBzdGQuZXJyb3IobWljZTEkaW1wJFdlYXRoZXJbLDJdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRXZWF0aGVyWywzXSksIA0KICAgICAgICAgICAgICAgIHN0ZC5lcnJvcihtaWNlMSRpbXAkV2VhdGhlclssNF0pLCBzdGQuZXJyb3IobWljZTEkaW1wJFdlYXRoZXJbLDVdKSkNCnEuYmFyLndlYXRoZXIubSA8LSBtZWFuKHF3ZWF0aGVyLm0pDQp1LmJhci53ZWF0aGVyLm0gPC0gbWVhbih1d2VhdGhlci5tKQ0KYi53ZWF0aGVyLm0gPC0gdmFyKHF3ZWF0aGVyLm0pDQp0LndlYXRoZXIubSA8LSAocS5iYXIud2VhdGhlci5tICsgKChuLm1pY2UgKyAxKS9uLm1pY2UpKmIud2VhdGhlci5tKQ0Kc2Uud2VhdGhlci5tIDwtIHN0ZC5lcnJvcihxd2VhdGhlci5tKQ0KDQoNCnFzdXBjcmV3Lm0gPC0gYyhtZWFuKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWywxXSksIG1lYW4obWljZTEkaW1wJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbLDJdKSwgbWVhbihtaWNlMSRpbXAkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVssM10pLCANCiAgICAgICAgICAgICAgICBtZWFuKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWyw0XSksIG1lYW4obWljZTEkaW1wJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbLDVdKSkNCnVzdXBjcmV3Lm0gPC0gYyhzdGQuZXJyb3IobWljZTEkaW1wJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbLDFdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWywyXSksIA0KICAgICAgICAgICAgICAgIHN0ZC5lcnJvcihtaWNlMSRpbXAkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVssM10pLCBzdGQuZXJyb3IobWljZTEkaW1wJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbLDRdKSwgDQogICAgICAgICAgICAgICAgc3RkLmVycm9yKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWyw1XSkpDQpxLmJhci5zdXBjcmV3Lm0gPC0gbWVhbihxc3VwY3Jldy5tKQ0KdS5iYXIuc3VwY3Jldy5tIDwtIG1lYW4odXN1cGNyZXcubSkNCmIuc3VwY3Jldy5tIDwtIHZhcihxc3VwY3Jldy5tKQ0KdC5zdXBjcmV3Lm0gPC0gKHEuYmFyLnN1cGNyZXcubSArICgobi5taWNlICsgMSkvbi5taWNlKSpiLnN1cGNyZXcubSkNCnNlLnN1cGNyZXcubSA8LSBzdGQuZXJyb3IocXN1cGNyZXcubSkNCg0KcWxhdGVpbmNvbS5tIDwtIGMobWVhbihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDFdKSwgbWVhbihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDJdKSwgbWVhbihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDNdKSwgDQogICAgICAgICAgICAgICAgICBtZWFuKG1pY2UxJGltcCRMYXRlX0Fycml2YWxfb1ssNF0pLCBtZWFuKG1pY2UxJGltcCRMYXRlX0Fycml2YWxfb1ssNV0pKQ0KdWxhdGVpbmNvbS5tIDwtIGMoc3RkLmVycm9yKG1pY2UxJGltcCRMYXRlX0Fycml2YWxfb1ssMV0pLCBzdGQuZXJyb3IobWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWywyXSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDNdKSwgDQogICAgICAgICAgICAgICAgICBzdGQuZXJyb3IobWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWyw0XSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDVdKSkNCnEuYmFyLmxhdGVpbmNvbS5tIDwtIG1lYW4ocWxhdGVpbmNvbS5tKQ0KdS5iYXIubGF0ZWluY29tLm0gPC0gbWVhbih1bGF0ZWluY29tLm0pDQpiLmxhdGVpbmNvbS5tIDwtIHZhcihxbGF0ZWluY29tLm0pDQp0LmxhdGVpbmNvbS5tIDwtIChxLmJhci5sYXRlaW5jb20ubSArICgobi5taWNlICsgMSkvbi5taWNlKSpiLmxhdGVpbmNvbS5tKQ0Kc2UubGF0ZWluY29tLm0gPC0gc3RkLmVycm9yKHFsYXRlaW5jb20ubSkNCg0KcWNsZWFuLm0gPC0gYyhtZWFuKG1pY2UxJGltcCRDbGVhbmluZ19vWywxXSksIG1lYW4obWljZTEkaW1wJENsZWFuaW5nX29bLDJdKSwgbWVhbihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssM10pLCBtZWFuKG1pY2UxJGltcCRDbGVhbmluZ19vWyw0XSksIA0KICAgICAgICAgICAgICAgICAgICAgbWVhbihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssNV0pKQ0KdWNsZWFuLm0gPC0gYyhzdGQuZXJyb3IobWljZTEkaW1wJENsZWFuaW5nX29bLDFdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRDbGVhbmluZ19vWywyXSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssM10pLCANCiAgICAgICAgICAgICAgICBzdGQuZXJyb3IobWljZTEkaW1wJENsZWFuaW5nX29bLDRdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRDbGVhbmluZ19vWyw1XSkpDQpxLmJhci5jbGVhbi5tIDwtIG1lYW4ocWNsZWFuLm0pDQp1LmJhci5jbGVhbi5tIDwtIG1lYW4odWNsZWFuLm0pDQpiLmNsZWFuLm0gPC0gdmFyKHFjbGVhbi5tKQ0KdC5jbGVhbi5tIDwtIChxLmJhci5jbGVhbi5tICsgKChuLm1pY2UgKyAxKS9uLm1pY2UpKmIuY2xlYW4ubSkNCnNlLmNsZWFuLm0gPC0gc3RkLmVycm9yKHFjbGVhbi5tKQ0KDQoNCm1pY2UubWVyZ2UgPC0gYW0uaW1wLnN0YXQNCm1pY2UubWVyZ2UkbWljZS53ZWF0aGVyIDwtIGMocS5iYXIud2VhdGhlci5tLCB1LmJhci53ZWF0aGVyLm0sIGIud2VhdGhlci5tLCB0LndlYXRoZXIubSwgc2Uud2VhdGhlci5tKQ0KbWljZS5tZXJnZSRtaWNlLnN1cC5jcmV3IDwtIGMocS5iYXIuc3VwY3Jldy5tLCB1LmJhci5zdXBjcmV3Lm0sIGIuc3VwY3Jldy5tLCB0LnN1cGNyZXcubSwgc2Uuc3VwY3Jldy5tKQ0KbWljZS5tZXJnZSRtaWNlLmxhdGUuaW5jb21pbmc8LSBjKHEuYmFyLmxhdGVpbmNvbS5tLCB1LmJhci5sYXRlaW5jb20ubSwgYi5sYXRlaW5jb20ubSwgdC5sYXRlaW5jb20ubSwgc2UubGF0ZWluY29tLm0pDQptaWNlLm1lcmdlJG1pY2UuY2xlYW48LSBjKHEuYmFyLmNsZWFuLm0sIHUuYmFyLmNsZWFuLm0sIGIuY2xlYW4ubSwgdC5jbGVhbi5tLCBzZS5jbGVhbi5tKQ0KDQptaWNlLm1lcmdlIDwtIGRwbHlyOjpzZWxlY3QobWljZS5tZXJnZSwgU3RhdGlzdGljLCBhbS53ZWF0aGVyLCBtaWNlLndlYXRoZXIsIGFtLnN1cC5jcmV3LCBtaWNlLnN1cC5jcmV3LCBhbS5sYXRlLmluY29taW5nLCBtaWNlLmxhdGUuaW5jb21pbmcsIGFtLmNsZWFuLCBtaWNlLmNsZWFuKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmthYmxlKG1pY2UubWVyZ2UsIGNhcHRpb24gPSAiVGFibGUgOToNCiAgICAgIDxicj5Db21wYXJyaXNvbiBvZiBBbWVsaWEgYm9vdHN0cmFwIGltcHV0YXRpb24gbW9kZWwgdG8gTUlDRSBpbXB1dGF0aW9uIG1vZGVsDQogICAgICA8YnI+UHJlZml4IFthbS5dIGluZGljYXRlcyBhbWVsaWEgbW9kZWwgc3RhdGlzdGljcyB3aGlsZSBwcmVmaXggW21pY2UuXSBpbmRpY2F0ZXMgTUlDRSBjYXJ0IG1vZGVsIHN0YXRpc3RpY3MiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKSAlPiUgDQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KDQoNCiMjIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIEZyb20gQW1lbGlhIEltcHV0YXRpb24gZm9yIEFycml2YWwgRGVsYXkgUHJlZGljdGlvbg0KDQpBcyBkZW1vbnN0cmF0ZWQgYWJvdmUgaW4gYFRhYmxlIDlgLCB0aGUgQW1lbGlhIGJvb3RzdHJhcCBpbXB1dGF0aW9uIG1vZGVsIHByb3ZlZCB0byBiZSB0aGUgYmVzdCBmaXQgYXBwcm9hY2ggZm9yIGltcHV0aW5nIG1pc3NpbmcgZmxpZ2h0IGRhdGEsIHRoZXJlZm9yZSB0aGlzIGltcHV0YXRpb24gbW9kZWwgd2FzIHV0aWxpemVkIHRvIGNyZWF0ZSBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciBhbmFseXNpcyBvZiB0aGUgb3RoZXIgdXRpbGl6ZWQgaW1wdXRhdGlvbiBtZXRob2RzLiAgVXRpbGl6aW5nIHRoZSBhbmFseXNpcyBvdXRwdXQgYXMgZm91bmQgYWJvdmUgaW4gdGhlIEIudmFyIHJvdyBvZiBgVGFibGUgN2AgaXQgd2FzIGRldGVybWluZWQgdGhhdCB0aGUgdmFyaWFuY2UgYmV0d2VlbiBlYWNoIGltcHV0YXRpb24gcnVuIHdhcyBuZWdsaWdpYmxlLCBhbGxvd2luZyBhIHNpbmdsZSBpbXB1dGF0aW9uIHJ1biB0byBiZSB1dGlsaXplZCBmb3IgcHJvcG9zZWQgbW9kZWwgY3JlYXRpb24uICBUaGUgYmVzdCBmaXQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2FzIGNyZWF0ZWQgdXRpbGl6aW5nIHRoZSA8Yj5zdGVwKCk8L2I+IGZ1bmN0aW9uLiBUaGlzIHByb2Nlc3Mgd2FzIHV0aWxpemVkIHRvIGFsbG93IGZvciBhbiBhdXRvbWF0ZWQgc2VsZWN0aW9uIG9mIHRoZSBiZXN0IGZpdCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCByYXRoZXIgdGhhbiBtYW51YWxseSBhc3N1bWluZyB0aGUgYmVzdCBtb2RlbCBieSBjb21wYXJpbmcgY29ycmVsYXRpb24gdmFsdWVzIGFzIGNvbXBsZXRlZCBhYm92ZSBpbiB0aGUgYExpbmVhciBSZWdyZXNzaW9uIEltcHV0YXRpb25gIHNlY3Rpb24uDQoNClRoaXMgcmVncmVzc2lvbiBtb2RlbCB3YXMgYnVpbHQgaW4gb3JkZXIgdG8gdXRpbGl6ZSB0aGUgbmV3bHkgaW1wdXRlZCBkYXRhIHRvIGNyZWF0ZSBhIHJlZ3Jlc3Npb24gbW9kZWwgYWxsb3dpbmcgZm9yIHByZWRpY3Rpb24gb2YgcG90ZW50aWFsIG92ZXJhbGwgZmxpZ2h0IGFycml2YWwgZGVsYXlzIGJhc2VkIG9uIHRoZSBhdmFpbGFibGUgZ2xvYmFsIGZsaWdodCBkYXRhLg0KDQpgYGB7cn0NCmFtLnJlZy5mdWxsIDwtIGxtKEFycl9EZWxheSB+LiwgZGF0YSA9IGFtZWxpYV9pbXAkaW1wdXRhdGlvbnMkaW1wMSkNCmBgYA0KDQpgYGB7cn0NCmthYmxlKHN0ZXAoYW0ucmVnLmZ1bGwsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAwKSRjb2VmLCBjb2wubmFtZXMgPSBjKCJWYXJpYWJsZSBOYW1lcyIsICJDb2VmZmljaWVudHMiKSwgY2FwdGlvbiA9ICJUYWJsZSAxMDoNCiAgICAgIDxicj5MaW5lYXIgUmVncmVzc2lvbiBjb2VmZmljaWVudHMgY2FsY3VsYXRlZCB1dGlsemluZyB0aGUgU1RFUCBpbXB1dGF0aW9uIHByb2Nlc3MiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCg0KJCQNClxiZWdpbnthbGlnbip9DQpBcnJpdmFsIFwgRGVsYXkgJlxzaW0gQWlycG9ydCBcIERpc3RhbmNlICsgXCBOdW1iZXIgXCBvZiBcIGZsaWdodHMgKyBXZWF0aGVyIFxcICYgXCBcIFwgKyBTdXBwb3J0IFwgQ3JldyBcIEF2YWlsYWJsZSArIEJhZ2dhZ2UgXCBMb2FkaW5nIFwgVGltZSArIExhdGUgXCBJbmNvbWluZyBcIFBsYW5lIFwgQXJyaXZhbCBcXA0KQXJyaXZhbCBcIERlbGF5ICZcc2ltIDIuMTE4ZV57LTF9eCBcICsgXCA1LjI2OWVeey0zfXggXCArIFwgNS40OTh4IFwgLSBcIDUuNzAwZV57LTJ9eCBcICsgMS4xOTFlXnsxfXggXCArIFwgOC4xODB4IFwgLSBcIDQuNTE0ZV57Mn0NClxlbmR7YWxpZ24qfQ0KJCQNCg0KIyMjIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIFByZWRpY3Rpb24gZm9yIEFycml2YWwgRGVsYXkNCg0KVGhlIGFib3ZlIGNyZWF0ZWQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2FzIHJ1biBvbiBhbGwgNCBjcmVhdGVkIGltcHV0YXRpb24gZGF0YSBzZXRzIHRvIGZ1cnRoZXIgYW5hbHl6ZSB3aGljaCBpbXB1dGF0aW9uIG1vZGVsIHdvcmtzIGJlc3QgZm9yIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZGVzaWduZWQgdG8gcHJlZGljdCB0b3RhbCBmbGlnaHQgYXJyaXZhbCBkZWxheXMgYmFzZWQgb24gdGhlIGF2YWlsYWJsZSBmbGlnaHQgZGF0YS4gIFRvIG5vdGUsIGFzIHRoZSBwcmV2aW91c2x5IGNyZWF0ZWQgbGluZWFyIHJlZ3Jlc3Npb24gaW1wdXRhdGlvbiBtb2RlbCB3YXMgb25seSBhcHBsaWNhYmxlIGZvciBudW1lcmljYWwgZGF0YSwgdGhlIDx1PkJhZ2dhZ2UgTG9hZGluZyBUaW1lPC91PiB2YXJpYWJsZSB3YXMgcmVtb3ZlZCBmcm9tIHRoZSByZWdyZXNzaW9uIGVxdWF0aW9uIDxpPm9ubHk8L2k+IGZvciB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gaW1wdXRhdGlvbiBtb2RlbC4gIFRoaXMgcmVzdHJpY3Rpb24gb2YgaW5wdXQgZGF0YSBhbHJlYWR5IGltcGxpZXMgdGhhdCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gaW1wdXRhdGlvbiBtb2RlbCBpcyBsaWtlbHkgbm90IHRoZSBiZXN0IGZpdCBhcHByb2FjaCBmb3IgaW1wdXRpbmcgZGF0YSB0byB0aGVuIHVzZSBmb3IgZmxpZ2h0IGRlbGF5IHByZWRpY3Rpb25zLiANCg0KYGBge3J9DQphbS5yZWcgPC0gbG0oQXJyX0RlbGF5IH4gQWlycG9ydF9EaXN0YW5jZSArIE51bWJlcl9vZl9mbGlnaHRzICsgV2VhdGhlciArIFN1cHBvcnRfQ3Jld19BdmFpbGFibGUgKyBCYWdnYWdlX2xvYWRpbmdfdGltZSArIExhdGVfQXJyaXZhbF9vLCBkYXRhID0gYW1lbGlhX2ltcCRpbXB1dGF0aW9ucyRpbXAxKQ0Ka05OLnJlZyA8LSBsbShBcnJfRGVsYXkgfiBBaXJwb3J0X0Rpc3RhbmNlICsgTnVtYmVyX29mX2ZsaWdodHMgKyBXZWF0aGVyICsgU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSArIEJhZ2dhZ2VfbG9hZGluZ190aW1lICsgTGF0ZV9BcnJpdmFsX28sIGRhdGEgPSBkZWxheS5rTk4pDQpsaW4ucmVnIDwtIGxtKEFycl9EZWxheSB+IEFpcnBvcnRfRGlzdGFuY2UgKyBOdW1iZXJfb2ZfZmxpZ2h0cyArIFdlYXRoZXIgKyBTdXBwb3J0X0NyZXdfQXZhaWxhYmxlICsgTGF0ZV9BcnJpdmFsX28sIGRhdGEgPSBkZWxheS5saW5lYXIpDQptaWNlLnJlZyA8LSBsbShBcnJfRGVsYXkgfiBBaXJwb3J0X0Rpc3RhbmNlICsgTnVtYmVyX29mX2ZsaWdodHMgKyBXZWF0aGVyICsgU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSArIEJhZ2dhZ2VfbG9hZGluZ190aW1lICsgTGF0ZV9BcnJpdmFsX28sIGRhdGEgPSBtLmRlbGF5KQ0KYGBgDQoNCg0KYGBge3J9DQpyZWcuY29tcGFyZSA8LSBkYXRhLmZyYW1lKA0KICBtb2RlbCA9IGMoImFtLnJlZyIsICJrTk4ucmVnIiwgImxpbi5yZWciLCAibWljZS5yZWciKSwNCiAgci5zcXVhcmVkID0gYyhzdW1tYXJ5KGFtLnJlZykkci5zcXVhcmVkLCBzdW1tYXJ5KGtOTi5yZWcpJHIuc3F1YXJlZCwgc3VtbWFyeShsaW4ucmVnKSRyLnNxdWFyZWQsIHN1bW1hcnkobWljZS5yZWcpJHIuc3F1YXJlZCkNCikNCg0Ka2FibGUocmVnLmNvbXBhcmUsIGNvbC5uYW1lcyA9IGMoIkltcHV0YXRpb24gTW9kZWwiLCAnUiReMiQgVmFsdWUnKSwgY2FwdGlvbiA9ICJUYWJsZSAxMToNCiAgICAgIDxicj5Db21wYXJyaXNvbiBvZiB1dGlsaXppbmcgdmFyaW91cyBpbXB1dGF0aW9uIG1vZGVscyBmb3IgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcHJlZGljdGlvbiBvZiBmbGlnaHQgZGVsYXlzIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpBcyBkZW1vbnN0cmF0ZWQgYWJvdmUsIHRoZSBgayBOZWFyZXN0IE5laWdoYm9yYCBpbXB1dGF0aW9uIG1vZGVsIHJlc3VsdGVkIGluIHRoZSBoaWdoZXN0IFIkXjIkIHZhbHVlIGluZGljYXRpbmcgdGhhdCB0aGUgYGsgTmVhcmVzdCBOZWlnaGJvcmAgKGRlbGF5LmtOTikgaW1wdXRhdGlvbiBtb2RlbCB3b3JrZWQgYmVzdCBmb3IgcHJlZGljdGluZyBvdmVyYWxsIGZsaWdodCBkZWxheXMgd2hlbiB1dGlsaXppbmcgdGhlIHN0ZXAtd2lzZSBjcmVhdGVkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiAgVGhpcyB3YXMgZm9sbG93ZWQgdXAgYnkgdGhlIE1JQ0UgaW1wdXRhdGlvbiBtb2RlbCBhbmQgdGhlIEFtZWxpYSBib290c3RyYXAgaW1wdXRhdGlvbiBtb2RlbCBpbiBhIG5lYXIgdGllIGZvciBzZWNvbmQgcGxhY2UuICBUaGlzIGFsbG93cyBmb3Igc2VsZWN0aW9uIG9mIHRoZSBiZXN0IGltcHV0YXRpb24gYXBwcm9hY2ggZm9yIGltcHV0aW5nIGFueSBtaXNzaW5nIGdsb2JhbCBmbGlnaHQgZGF0YSBwcmlvciB0byBwcmVkaWN0aW5nIHRvdGFsIGZsaWdodCBkZWxheXMuICBEdWUgdG8gdGhlIGhpZ2ggUiReMiQgdmFsdWUgdGhlIGBrIE5lYXJlc3QgTmVpZ2hib3JgIGltcHV0YXRpb24gYXBwcm9hY2ggd2lsbCBiZSB1dGlsaXplZCBnb2luZyBmb3J3YXJkIGZvciBhbmFseXNpcy4gDQoNCiMgRmVhdHVyZSBUcmFuc2Zvcm1hdGlvbg0KDQpJbiB0aGUgbGFzdCBhbmFseXNpcyBvZiB0aGlzIGRhdGEgc2V0IHRoZSB0b3RhbCBzdXBwb3J0IGNyZXcgbWVtYmVycyBhdmFpbGFibGUsIHRvdGFsIGFycml2YWwgZGVsYXlzIGZvciBwbGFuZXMgbmVlZGVkIGZvciBvdXRnb2luZyBmbGlnaHRzLCBiYWdnYWdlIGxvYWRpbmcgdGltZXMsIGFuZCBmdWVsaW5nIHRpbWVzIHdlcmUgYW5hbHl6ZWQgZm9yIHRoZWlyIGltcGFjdHMgb24gdGhlIG92ZXJhbGwgZmxpZ2h0IGRlbGF5cy4gVGhlc2UgdmFyaWFibGVzIHdlcmUgcmUtYXNzZXNzZWQgYWxvbmdzaWRlIHRoZSByZW1haW5pbmcgdmFyaWFibGVzIHRvIGRldGVybWluZSBpZiB0aGVzZSB0aHJlZSB2YXJpYWJsZXMgcHJldmlvdXNseSBzZWxlY3RlZCB3ZXJlIHRydWx5IHRoZSBiZXN0IHByZWRpY3RvciB2YXJpYWJsZXMgZm9yIGZsaWdodCBkZWxheXMuIFRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIG5vdCBpbnZlc3RpZ2F0ZWQgaW4gdGhlIGZpcnN0IGFuYWx5c2lzIHdlcmUgcmV2aWV3ZWQgYWxvbmdzaWRlIHRoZSBwcmV2aW91c2x5IG1lbnRpb25lZCB2YXJpYWJsZXM6DQoNCiAgLSBBaXJwb3J0IERpc3RhbmNlDQogIC0gTnVtYmVyIG9mIEZsaWdodHMNCiAgLSBDbGVhbmluZyBUaW1lDQogIC0gU2VjdXJpdHkgVGltZQ0KICAtIEFpcmxpbmUgQ2Fycmllcg0KICAtIFdlYXRoZXINCiAgDQogIA0KDQojIyBTa2V3ICYgRGlzdHJpYnV0aW9uIENoZWNrDQoNCkluIG9yZGVyIHRvIGRldGVybWluZSBpZiBhbnkgb2YgdGhlIHZhcmlhYmxlIGRhdGEgaXMgc2tld2VkLCB0aGUgY29udGludW91cyB2YXJpYWJsZSBkaXN0cmlidXRpb25zIHdlcmUgcGxvdHRlZCB3aXRoIGFuIG92ZXJsYXkgaW5kaWNhdGluZyB0aGUgc3RhbmRhcmQgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhcyBjYWxjdWxhdGVkIHdpdGggdGhlIHJlc3BlY3RpdmUgdmFyaWFibGUncyBjYWxjdWxhdGVkIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4gVGhpcyBjaGVjayB3YXMgY29tcGxldGVkIHRvIGVuc3VyZSB0aGUgZGF0YSBhcyBhdmFpbGFibGUgd2lsbCBjb29wZXJhdGUgd2l0aCB0aGUgcHJlZGljdGlvbiBtb2RlbHMgdXRpbGl6ZWQgbGF0ZXIgaW4gdGhlIGFuYWx5c2lzLiAgQXMgYSBtYWpvcml0eSBvZiBwcmVkaWN0aW9uIG1vZGVscyByZXF1aXJlIHRoZSBhc3N1bXB0aW9uIG9mIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlIGNhbGN1bGF0ZWQgbm9ybWFsIGRpc3RyaWJ1dGlvbiBjdXJ2ZSBmb3IgZWFjaCByZXNwZWN0aXZlIHZhcmlhYmxlIHdhcyBvdmVybGFpZCBhcyBhIGNvbXBhcmlzb24gcmVmZXJlbmNlLiAgSW4gb3JkZXIgdG8gcHJvcGVybHkgbGV2ZXJhZ2UgdGhlIHByZWRpY3Rpb24gbW9kZWxzIHRvIG9wdGltaXplIGFpcnBvcnQgcGxhbm5pbmcgdG8gcmVkdWNlIGZsaWdodCBkZWxheXMsIGl0IGlzIGltcGVyYXRpdmUgdG8gZW5zdXJlIHRoZSBkYXRhIGZpdHMgd2l0aGluIHRoZSBhc3N1bXB0aW9ucyBvZiB0aGUgcHJlZGljdGlvbiBtb2RlbCB1dGlsaXplZC4gDQoNCmBgYHtyfQ0Ka05OLmFuYWx5c2lzIDwtIGRlbGF5LmtOTg0Ka05OLmFuYWx5c2lzJFdlYXRoZXIgPC0gYXMubnVtZXJpYyhrTk4uYW5hbHlzaXMkV2VhdGhlcikNCmtOTi5hbmFseXNpcyRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlIDwtIGFzLm51bWVyaWMoa05OLmFuYWx5c2lzJFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpDQprTk4uYW5hbHlzaXMkTGF0ZV9BcnJpdmFsX28gPC0gYXMubnVtZXJpYyhrTk4uYW5hbHlzaXMkTGF0ZV9BcnJpdmFsX28pDQoNCmRpc3RfZGVucyA8LSBnZ3Bsb3Qoa05OLmFuYWx5c2lzLCBhZXMoeCA9IEFpcnBvcnRfRGlzdGFuY2UpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihrTk4uYW5hbHlzaXMkQWlycG9ydF9EaXN0YW5jZSksIHNkID0gc2Qoa05OLmFuYWx5c2lzJEFpcnBvcnRfRGlzdGFuY2UpKSwgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV3aWR0aCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oa05OLmFuYWx5c2lzJEFpcnBvcnRfRGlzdGFuY2UpKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4oa05OLmFuYWx5c2lzJEFpcnBvcnRfRGlzdGFuY2UpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgQmV0d2VlbiBEZXBhcnR1cmUtQXJyaXZhbCBBaXJwb3J0cyIsDQogICAgICAgeCA9ICJEaXN0YW5jZSAobWlsZXMpIiwNCiAgICAgICB5ID0gIkRlbnNpdHkiKQ0KDQpudW1mbGlnaHRfZGVucyA8LSBnZ3Bsb3Qoa05OLmFuYWx5c2lzLCBhZXMoeCA9IE51bWJlcl9vZl9mbGlnaHRzKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKGtOTi5hbmFseXNpcyROdW1iZXJfb2ZfZmxpZ2h0cyksIHNkID0gc2Qoa05OLmFuYWx5c2lzJE51bWJlcl9vZl9mbGlnaHRzKSksIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKGtOTi5hbmFseXNpcyROdW1iZXJfb2ZfZmxpZ2h0cykpLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihrTk4uYW5hbHlzaXMkTnVtYmVyX29mX2ZsaWdodHMpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgRmxpZ2h0cyBwZXIgQWlycG9ydCAqIiwNCiAgICAgICB4ID0gIkZsaWdodHMgcGVyIEFpcnBvcnQiLA0KICAgICAgIHkgPSBOVUxMKQ0KDQpzdXBjcmV3X2RlbnMgPC0gZ2dwbG90KGtOTi5hbmFseXNpcywgYWVzKHggPSBTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKGtOTi5hbmFseXNpcyRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSwgc2QgPSBzZChrTk4uYW5hbHlzaXMkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSwgbmEucm0gPSBUUlVFKSksIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKGtOTi5hbmFseXNpcyRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKGtOTi5hbmFseXNpcyRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSksIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFN1cHBvcnQgQ3JldyBNZW1iZXJzIEF2YWlsYWJsZSAqIiwNCiAgICAgICB4ID0gIlN1cHBvcnQgQ3JldyBBdmFpbGFibGUiLA0KICAgICAgIHkgPSBOVUxMKQ0KDQpmdWVsaW5nX2RlbnMgPC0gZ2dwbG90KGtOTi5hbmFseXNpcywgYWVzKHggPSBGdWVsaW5nX28pKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4oa05OLmFuYWx5c2lzJEZ1ZWxpbmdfbyksIHNkID0gc2Qoa05OLmFuYWx5c2lzJEZ1ZWxpbmdfbykpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihrTk4uYW5hbHlzaXMkRnVlbGluZ19vKSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKGtOTi5hbmFseXNpcyRGdWVsaW5nX28pKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiUGxhbmUgRnVlbGluZyBUaW1lcyIsDQogICAgICAgeCA9ICJGdWVsaW5nIFRpbWUgKG1pbnV0ZXMpIiwNCiAgICAgICB5ID0gTlVMTCkgDQoNCnNlY3VyaXR5X2RlbnMgPC0gZ2dwbG90KGtOTi5hbmFseXNpcywgYWVzKHggPSBTZWN1cml0eV9vKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKGtOTi5hbmFseXNpcyRTZWN1cml0eV9vKSwgc2QgPSBzZChrTk4uYW5hbHlzaXMkU2VjdXJpdHlfbykpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihrTk4uYW5hbHlzaXMkU2VjdXJpdHlfbykpLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihrTk4uYW5hbHlzaXMkU2VjdXJpdHlfbykpLCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTZWN1cml0eSBUaW1lcyIsDQogICAgICAgeCA9ICJUaW1lIChtaW51dGVzKSIsDQogICAgICAgeSA9ICJEZW5zaXR5IikgIA0KDQphcnJpdmFsX2RlbnMgPC0gZ2dwbG90KGtOTi5hbmFseXNpcywgYWVzKHggPSBBcnJfRGVsYXkpKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4oa05OLmFuYWx5c2lzJEFycl9EZWxheSksIHNkID0gc2Qoa05OLmFuYWx5c2lzJEFycl9EZWxheSkpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihrTk4uYW5hbHlzaXMkQXJyX0RlbGF5KSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKGtOTi5hbmFseXNpcyRBcnJfRGVsYXkpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgRmxpZ2h0IERlbGF5cyIsDQogICAgICAgeCA9ICJEdXJhdGlvbiAobWludXRlcykiLA0KICAgICAgIHkgPSAiRGVuc2l0eSIpICANCg0KbGF0ZWluY29tX2RlbnMgPC0gZ2dwbG90KGtOTi5hbmFseXNpcywgYWVzKHggPSBMYXRlX0Fycml2YWxfbykpICsgDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihrTk4uYW5hbHlzaXMkTGF0ZV9BcnJpdmFsX28pLCBzZCA9IHNkKGtOTi5hbmFseXNpcyRMYXRlX0Fycml2YWxfbykpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihrTk4uYW5hbHlzaXMkTGF0ZV9BcnJpdmFsX28pKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4oa05OLmFuYWx5c2lzJExhdGVfQXJyaXZhbF9vKSksIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlID0gIkxhdGUgSW5jb21pZyBQbGFuZSBEZWxheXMgKiIsDQogICAgICAgeCA9ICJEdXJhdGlvbiAobWludXRlcykiLA0KICAgICAgIHkgPSAiRGVuc2l0eSIpICANCg0Kd2VhdGhlcl9kZW5zIDwtIGdncGxvdChrTk4uYW5hbHlzaXMsIGFlcyh4ID0gV2VhdGhlcikpICsgDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihrTk4uYW5hbHlzaXMkV2VhdGhlciksIHNkID0gc2Qoa05OLmFuYWx5c2lzJFdlYXRoZXIpKSwgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV3aWR0aCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oa05OLmFuYWx5c2lzJFdlYXRoZXIpKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4oa05OLmFuYWx5c2lzJFdlYXRoZXIpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiV2VhdGhlciBDb25kaXRpb25zIiwNCiAgICAgICB4ID0gIlJhbmtpbmcgb2YgV2VhdGhlciBTZXZlcml0eSIsDQogICAgICAgeSA9ICJEZW5zaXR5IikgIA0KDQpgYGANCg0KYGBge3J9DQprTk4uYW5hbHlzaXMkQmFnZ2FnZV9sb2FkaW5nX3RpbWUgPC0gZmFjdG9yKGtOTi5hbmFseXNpcyRCYWdnYWdlX2xvYWRpbmdfdGltZSwgbGV2ZWxzID0gYygiU2xvdyIsICJNb2RlcmF0ZSIsICJGYXN0IikpDQprTk4uYW5hbHlzaXMkQ2xlYW5pbmdfbyA8LSBmYWN0b3Ioa05OLmFuYWx5c2lzJENsZWFuaW5nX28sIGxldmVscyA9IGMoIlNsb3ciLCAiTW9kZXJhdGUiLCAiUXVpY2siLCAiSW1tZWRpYXRlIikpDQoNCmJhZ2dhZ2VfYmFyIDwtIGdncGxvdChrTk4uYW5hbHlzaXMsIGFlcyh4ID0gQmFnZ2FnZV9sb2FkaW5nX3RpbWUpKSArIA0KICBnZW9tX2JhcihmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkJhZ2dhZ2UgTG9hZGluZyBUaW1lcyAqIiwNCiAgICAgICB4ID0gIkxvYWRpbmcgRHVyYXRpb24gKHNsb3dlc3QgdG8gZmFzdGVzdCBmcm9tIGxlZnQgdG8gcmlnaHQpIiwNCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpDQoNCmNsZWFuaW5nX2JhciA8LSBnZ3Bsb3Qoa05OLmFuYWx5c2lzLCBhZXMoeCA9IENsZWFuaW5nX28pKSArIA0KICBnZW9tX2JhcihmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIlBsYW5lIENsZWFuaW5nIFRpbWVzICoiLA0KICAgICAgIHggPSAiQ2xlYW5pbmcgRHVyYXRpb24gKHNsb3dlc3QgdG8gZmFzdGVzdCBmcm9tIGxlZnQgdG8gcmlnaHQpIiwNCiAgICAgICB5ID0gTlVMTCkNCg0KYGBgDQoNCmBgYHtyLCAgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTE4LCBvdXQud2lkdGg9IjEwMCUiLCBvdXQuaGVpZ2h0ID0gIjEwMCUifQ0KZ3JpZC5hcnJhbmdlKA0KICBhcnJhbmdlR3JvYihhcnJpdmFsX2RlbnMsIG51bWZsaWdodF9kZW5zLCBkaXN0X2RlbnMsIGZ1ZWxpbmdfZGVucywgc2VjdXJpdHlfZGVucywgc3VwY3Jld19kZW5zLCBsYXRlaW5jb21fZGVucywgd2VhdGhlcl9kZW5zLCBiYWdnYWdlX2JhciwgY2xlYW5pbmdfYmFyLCBuY29sID0gMixucm93ID0gNSksDQogIHRvcCA9IHRleHRHcm9iKCJEaXN0cmlidXRpb24gb2YgRmxpZ2h0IFZhcmlhYmxlcw0KICAgICAgICAgICAgICAgICAiLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpKSwNCiAgYm90dG9tID0gdGV4dEdyb2IoIkZpZ3VyZSA1DQogIA0KICBHcmFwaHMgbWFya2VkIHdpdGggb25lIGFzdGVyaXNrICgqKSBpbmRpY2F0ZSBza2V3ZWQgZGF0YQ0KICBCbGFjayBzb2xpZCBsaW5lOiBtZWFuIG9mIHZhcmlhYmxlIGRhdGEgfCBCbGFjayBkYXNoZWQgbGluZTogbWVkaWFuIG9mIHZhcmlhYmxlIGRhdGEiKSkNCg0KYGBgDQoNCg0KQXMgZGVtb25zdHJhdGVkIGluIHRoZSBhYm92ZSBgRmlndXJlIDRgLCB0aGUgZGlzdGFuY2UgYmV0d2VlbiBhaXJwb3J0cyBhbmQgdG90YWwgc2VjdXJpdHkgdGltZXMgaG9sZCBjbG9zZSB0byB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBjdXJ2ZXMuICBNZWFud2hpbGUgdGhlIHRvdGFsIGRlbGF5IHRpbWVzIGZvciBpbmNvbWluZyBwbGFuZXMgZnJvbSBwcmV2aW91cyBmbGlnaHRzLCBmdWVsaW5nIHRpbWVzLCBhbmQgd2VhdGhlciBzZXZlcml0eSBpbGx1c3RyYXRlIG5vbi1ub3JtYWwgZGlzdHJpYnV0aW9ucy4gQXQgYSBjdXJzb3J5IHJldmlldyB0aGVzZSB2YXJpYWJsZXMgYXBwZWFyIHRvIGhvbGQgdG8gbXVsdGltb2RhbCBhbmQgQmVybm91bGxpIGRpc3RyaWJ1dGlvbnMuIE1lYW53aGlsZSB0aGUgbnVtYmVyIG9mIGZsaWdodHMgYW5kIHRvdGFsIHN1cHBvcnQgY3Jld3MgYXZhaWxhYmxlIGRlbW9uc3RyYXRlZCBza2V3ZWQgbm9ybWFsIGRpc3RyaWJ1dGlvbnMuDQoNCiMjIyBVbmRlcnN0YW5kaW5nIHRoZSBTa2V3DQoNCkluIG9yZGVyIHRvIGNvbmZpcm0gdGhlIHZpc3VhbGx5IGlkZW50aWZpZWQgZGF0YSBza2V3cyB0aGUgbWVhbnMgYW5kIG1lZGlhbnMgb2YgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXMgd2VyZSBjb21wYXJlZDoNCg0KICAtIE51bWJlciBvZiBGbGlnaHRzDQogIC0gU3VwcG9ydCBDcmV3cyBBdmFpbGFibGUNCiAgLSBMYXRlIEluY29taW5nIFBsYW5lcw0KICAtIEJhZ2dhZ2UgTG9hZGluZyBUaW1lDQogIC0gQ2xlYW5pbmcNCg0KVXBvbiBjb21wYXJpc29uIG9mIHRoZSBtZWFuIGFuZCBtZWRpYW4gb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIGl0IHdhcyBpZGVudGlmaWVkIHRoYXQgdGhlIGxhdGUgaW5jb21pbmcgcGxhbmVzIHZhcmlhYmxlIHdhcyBub3Qgc2tld2VkIGFzIHRoZSBtZWRpYW4gYW5kIG1lYW4gd2VyZSBpZGVudGljYWwuICBGcmVxdWVuY3kgY291bnRzIGZvciB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFsc28gaWRlbnRpZmllZCB0aGF0IHRoZSBtb2RlIG9mIHRoZXNlIHZhcmlhYmxlcyBjb3Jyb2JvcmF0ZWQgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGlzIGxlZnQgdGhlIG51bWJlciBvZiBmbGlnaHRzIGFuZCBzdXBwb3J0IGNyZXdzIGF2YWlsYWJsZSBhcyB0aGUgdHdvIHRydWx5IHNrZXdlZCB2YXJpYWJsZXMgcmVxdWlyaW5nIHRyYW5zZm9ybWF0aW9uLiANCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KHJvdW5kKGtOTi5hbmFseXNpc1tjKDMsNSw3KV0pLCBkaWdpdHMgPSAxKSwgY2FwdGlvbiA9ICJUYWJsZSAxMjoNCjxicj5TdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHZhcmlhYmxlcyB3aXRoIHNrZXdlZCBkaXN0cmlidXRpb25zLiBSb3VuZGVkIHRvIHdob2xlIG51bWJlcnMgYXMgdGhlIGZyYWN0aW9ucyBvZiBmbGlnaHRzIGFuZCBmcmFjdGlvbnMgb2Ygc3VwcG9ydCBjcmV3IG1lbWJlcnMgY2Fubm90IGV4aXN0IG5hdHVyYWxseS4iLCBjb2wubmFtZXMgPSBjKCJUb3RhbCBGbGlnaHRzIiwgIlN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlIiwgIkxhdGUgSW5jb21pbmcgUGxhbmVzIikpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCmthYmxlKHN1bW1hcnkoa05OLmFuYWx5c2lzW2MoNiwgOCldKSwgY2FwdGlvbiA9ICJUYWJsZSAxMzoNCjxicj5GcmVxdWVuY3kgY291bnQgdG8gY2FwdHVyZSB0aGUgbW9kZSBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBza2V3ZWQgZGlzdHJpYnV0aW9ucy4iLCBjb2wubmFtZXMgPSBjKCJCYWdnYWdlIExvYWRpbmcgVGltZXMiLCAiUGxhbmUgQ2xlYW5pbmcgRHVyYXRpb24iKSkgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCg0KYGBgDQoNCg0KDQojIyBWYXJpYWJsZSBOb3JtYWxpemF0aW9uDQoNCkluIG9yZGVyIHRvIG5vcm1hbGl6ZSB0aGUgdmFyaWFibGVzIGZvciBmdXR1cmUgcHJlZGljdGl2ZSBtb2RlbCB1c2UsIG1pbi1tYXggbm9ybWFsaXphdGlvbiB3YXMgZW1wbG95ZWQgdG8gcmVkdWNlIGFsbCB2YXJpYWJsZXMgdG8gYSByYW5nZSBvZiBbMCwxXS4gVGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3ZXJlIGFscmVhZHkgZW5jb2RlZCBhcyBmYWN0b3IgdmFyaWFibGVzLCBhbGxvd2luZyBmb3IgZWFzeSBsYWJlbCBlbmNvZGluZyB0byBhbGxvdyBmb3IgY29vcGVyYXRpb24gd2l0aCB0aGUgbWluLW1heCBzdGFuZGFyZGl6YXRpb24uIFRoZSBmb2xsb3dpbmcgZXF1YXRpb24gd2FzIGFwcGxpZWQgdG8gYWxsIG51bWVyaWMgdmFyaWFibGVzIChib3RoIG9yaWdpbmFsbHkgbnVtZXJpYyBhbmQgZm9yY2VkIG51bWVyaWMgZnJvbSBmYWN0b3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzKS4NCg0KJCR4X3tzdGFuZGFyZGl6ZWR9ID0gXGZyYWN7eCAtIG1pbih4KX17bWF4KHgpIC0gbWluKHgpfSQkDQoNCmBgYHtyfQ0Ka05OLmFuYWx5c2lzJGNsZWFuaW5nIDwtIGFzLm51bWVyaWMoa05OLmFuYWx5c2lzJENsZWFuaW5nX28pDQprTk4uYW5hbHlzaXMkYmFnZ2FnZSA8LSBhcy5udW1lcmljKGtOTi5hbmFseXNpcyRCYWdnYWdlX2xvYWRpbmdfdGltZSkNCg0KZGVsYXkuc3RkIDwtIGRhdGEuZnJhbWUoDQogIGFpcnBvcnRfZGlzdCA9IChrTk4uYW5hbHlzaXMkQWlycG9ydF9EaXN0YW5jZSAtIG1pbihrTk4uYW5hbHlzaXMkQWlycG9ydF9EaXN0YW5jZSkpIC8gKG1heChrTk4uYW5hbHlzaXMkQWlycG9ydF9EaXN0YW5jZSkgLSBtaW4oa05OLmFuYWx5c2lzJEFpcnBvcnRfRGlzdGFuY2UpKSwNCiAgbnVtX2ZsaWdodHMgPSAoa05OLmFuYWx5c2lzJE51bWJlcl9vZl9mbGlnaHRzIC0gbWluKGtOTi5hbmFseXNpcyROdW1iZXJfb2ZfZmxpZ2h0cykpIC8gKG1heChrTk4uYW5hbHlzaXMkTnVtYmVyX29mX2ZsaWdodHMpIC0gbWluKGtOTi5hbmFseXNpcyROdW1iZXJfb2ZfZmxpZ2h0cykpLA0KICB3ZWF0aGVyID0gKGtOTi5hbmFseXNpcyRXZWF0aGVyIC0gbWluKGtOTi5hbmFseXNpcyRXZWF0aGVyKSkgLyAobWF4KGtOTi5hbmFseXNpcyRXZWF0aGVyKSAtIG1pbihrTk4uYW5hbHlzaXMkV2VhdGhlcikpLA0KICBzdXBfY3JldyA9IChrTk4uYW5hbHlzaXMkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSAtIG1pbihrTk4uYW5hbHlzaXMkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSkpIC8gKG1heChrTk4uYW5hbHlzaXMkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSkgLSBtaW4oa05OLmFuYWx5c2lzJFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpKSwNCiAgbGF0ZV9pbmNvbWluZyA9IChrTk4uYW5hbHlzaXMkTGF0ZV9BcnJpdmFsX28gLSBtaW4oa05OLmFuYWx5c2lzJExhdGVfQXJyaXZhbF9vKSkgLyAobWF4KGtOTi5hbmFseXNpcyRMYXRlX0Fycml2YWxfbykgLSBtaW4oa05OLmFuYWx5c2lzJExhdGVfQXJyaXZhbF9vKSksDQogIGZ1ZWxpbmcgPSAoa05OLmFuYWx5c2lzJEZ1ZWxpbmdfbyAtIG1pbihrTk4uYW5hbHlzaXMkRnVlbGluZ19vKSkgLyAobWF4KGtOTi5hbmFseXNpcyRGdWVsaW5nX28pIC0gbWluKGtOTi5hbmFseXNpcyRGdWVsaW5nX28pKSwNCiAgc2VjdXJpdHkgPSAoa05OLmFuYWx5c2lzJFNlY3VyaXR5X28gLSBtaW4oa05OLmFuYWx5c2lzJFNlY3VyaXR5X28pKSAvIChtYXgoa05OLmFuYWx5c2lzJFNlY3VyaXR5X28pIC0gbWluKGtOTi5hbmFseXNpcyRTZWN1cml0eV9vKSksDQogIGFycl9kZWxheSA9IChrTk4uYW5hbHlzaXMkQXJyX0RlbGF5IC0gbWluKGtOTi5hbmFseXNpcyRBcnJfRGVsYXkpKSAvIChtYXgoa05OLmFuYWx5c2lzJEFycl9EZWxheSkgLSBtaW4oa05OLmFuYWx5c2lzJEFycl9EZWxheSkpLA0KICBjbGVhbmluZyA9IChrTk4uYW5hbHlzaXMkY2xlYW5pbmcgLSBtaW4oa05OLmFuYWx5c2lzJGNsZWFuaW5nKSkgLyAobWF4KGtOTi5hbmFseXNpcyRjbGVhbmluZykgLSBtaW4oa05OLmFuYWx5c2lzJGNsZWFuaW5nKSksDQogIGJhZ2dhZ2UgPSAoa05OLmFuYWx5c2lzJGJhZ2dhZ2UgLSBtaW4oa05OLmFuYWx5c2lzJGJhZ2dhZ2UpKSAvIChtYXgoa05OLmFuYWx5c2lzJGJhZ2dhZ2UpIC0gbWluKGtOTi5hbmFseXNpcyRiYWdnYWdlKSkNCikNCg0Ka2FibGUoc3VtbWFyeShkZWxheS5zdGQpLCBjYXB0aW9uID0gIlRhYmxlIDE0Og0KICAgICAgPGJyPlN1bW1hcnkgc3RhdGlzdGljcyBvZiBub3JtYWxpemVkIGZsaWdodCBkYXRhIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkgJT4lIA0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCg0KTWluLW1heCBub3JtYWxpemF0aW9uIHdhcyBjaG9zZW4gdG8gYWxsb3cgZm9yIGZ1dHVyZSB1c2Ugd2l0aCBmZWF0dXJlIG1hZ25pdHVkZSBzZW5zaXRpdmUgYWxnb3JpdGhtcywgc3VjaCBhcyBrIE5lYXJlc3QgTmVpZ2hib3JzIGNsdXN0ZXIgYW5hbHlzaXMuDQoNCiMjIEZlYXR1cmUgU2VsZWN0aW9uDQoNCkluIG9yZGVyIHRvIHNpbXBsaWZ5IHRoZSBkYXRhIGZvciBhbmFseXNpcyBhbmQgcHJlZGljdGl2ZSBtb2RlbGluZywgPGI+UmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24gKFJGRSk8L2I+IHdhcyBlbXBsb3llZCB1dGlsaXppbmcgdGhlIGNhcmV0IHBhY2thZ2UuIFRoaXMgYWxsb3dzIGZvciByZW1vdmFsIG9mIHJlZHVuZGFuY3kgaW4gdGhlIGRhdGEgc2V0IHRocm91Z2ggYSByYW5kb20gZm9yZXN0IG1vZGVsIHVzaW5nIDEwIGl0ZXJhdGlvbnMgb2YgY3Jvc3MgdmFsaWRhdGlvbiB0byBkZXRlcm1pbmUgd2hpY2ggdmFyaWFibGVzIGFyZSBtb3N0IGVzc2VudGlhbCBmb3IgY3JlYXRpbmcgcHJlZGljdGlvbiBtb2RlbHMgZm9yIGZsaWdodCBkZWxheXMuIFJlZHVjaW5nIHJlZHVuZGFuY3kgbm90IG9ubHkgcmVkdWNlcyB0aGUgY29tcHV0YXRpb25hbCBidXJkZW4gb2YgcHJlZGljdGl2ZSBtb2RlbHMgd2hpbGUgYWNjb3VudGluZyBmb3IgYW55IHBvdGVudGlhbCBmZWF0dXJlIGludGVyYWN0aW9ucyBiZXR3ZWVuIHZhcmlhYmxlcy4gSW4gb3JkZXIgdG8gcHJldmVudCBvdmVyIGZpdHRpbmcgdXNpbmcgUkZFLCBhbiA4MDoyMCB0cmFpbmluZzp0ZXN0IHNldCB3YXMgY3JlYXRlZCB1c2luZyBgZGVsYXkuY2FyZXRgIHRvIGlkZW50aWZ5IHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMgZm9yIHByZWRpY3Rpb24gb2Ygb3ZlcmFsbCBmbGlnaHQgZGVsYXlzIChhcnJfZGVsYXkpLiANCg0KQSB2aXN1YWxpemF0aW9uIG9mIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggdmFyaWFibGUgaW4gcG90ZW50aWFsIHByZWRpY3Rpb24gb2Ygb3ZlcmFsbCBmbGlnaHQgZGVsYXlzIGlzIGluY2x1ZGVkIGJlbG93IGluIGBGaWd1cmUgNmAuICBBcyBkZW1vbnN0cmF0ZWQgYmVsb3cgdGhlcmUgaXMgYSBzdGVlcCBkcm9wIG9mZiBpbiBpbXBvcnRhbmNlIGZvciB0b3RhbCBjbGVhbmluZyB0aW1lIChjbGVhbmluZyksIGZ1ZWxpbmcgdGltZXMgKGZ1ZWxpbmcpLCAmIHNlY3VyaXR5IHRpbWVzIChzZWN1cml0eSk7IHRoZXJlZm9yZSB0aGVzZSB2YXJpYWJsZXMgd2lsbCBiZSBvbWl0dGVkIGZyb20gZnV0dXJlIGFuYWx5c2lzIHRvIHJlZHVjZSBjb21wdXRhdGlvbmFsIGJ1cmRlbiBhbmQgcHJldmVudCByZWR1bmRhbmNpZXMuICANCg0KDQpgYGB7cn0NCnguY2FyZXQgPC0gZGVsYXkuc3RkWywtOF0NCnkuY2FyZXQgPC0gZGVsYXkuc3RkWyw4XQ0KDQpzZXQuc2VlZCgxMjM0NSkNCmluVHJhaW4gPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LmNhcmV0LCBwID0gMC44MCwgbGlzdCA9IEZBTFNFKVssMV0NCg0KeC5jYXJldC50cmFpbiA8LSB4LmNhcmV0W2luVHJhaW4sIF0NCnguY2FyZXQudGVzdCA8LSB4LmNhcmV0Wy1pblRyYWluLCBdDQp5LmNhcmV0LnRyYWluIDwtIHkuY2FyZXRbaW5UcmFpbl0NCnkuY2FyZXQudGVzdCA8LSB5LmNhcmV0Wy1pblRyYWluXQ0KDQoNCmNvbnRyb2wgPC0gcmZlQ29udHJvbChmdW5jdGlvbnMgPSByZkZ1bmNzLCBtZXRob2QgPSAiY3YiLCByZXBlYXRzID0gMTAsIG51bWJlciA9IDE1KQ0KDQpkZWxheS5jYXJldCA8LSByZmUoeCA9IHguY2FyZXQudHJhaW4sIHkuY2FyZXQudHJhaW4gLCBzaXplcyA9IGMoMTo1KSwgcmZlQ29udHJvbCA9IGNvbnRyb2wpDQpgYGANCg0KDQpgYGB7cn0NCmRlbGF5LmNhcmV0LnZhciA8LSBkYXRhLmZyYW1lKGZlYXR1cmUgPSByb3cubmFtZXModmFySW1wKGRlbGF5LmNhcmV0KSlbMTo5XSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltcG9ydGFuY2UgPSB2YXJJbXAoZGVsYXkuY2FyZXQpWzE6OSwgMV0pDQoNCmFjY3VyYWN5LmNhcmV0IDwtIHBvc3RSZXNhbXBsZShwcmVkaWN0KGRlbGF5LmNhcmV0LCB4LmNhcmV0LnRlc3QpLCB5LmNhcmV0LnRlc3QpDQoNCmdncGxvdChkZWxheS5jYXJldC52YXIsIGFlcyh4ID0gcmVvcmRlcihmZWF0dXJlLCAtaW1wb3J0YW5jZSksIHkgPSBpbXBvcnRhbmNlLCBmaWxsID0gZmVhdHVyZSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnMoeCA9ICJGZWF0dXJlcyIsIHkgPSAiVmFyaWFibGUgSW1wb3J0YW5jZSIsIHRpdGxlID0gIlZhcmlhYmxlIEltcG9ydGFuY2UgZm9yIEFsbCBGZWF0dXJlcyIsIGNhcHRpb24gPSAiRmlndXJlIDY6DQogICAgICAgSW1wb3J0YW5jZSBvZiBlYWNoIHZhcmlhYmxlIGluIGdsb2JhbCBmbGlnaHQgZGF0YSBmb3IgcHJlZGljdGluZyBmbGlnaHQgZGVsYXlzIikgKyANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGltcG9ydGFuY2UsIDIpKSwgdmp1c3QgPSAwLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHRoZW1lX2J3KCkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KICANCmBgYA0KDQojIyBGZWF0dXJlIENyZWF0aW9uIA0KDQpJbiBvcmRlciB0byBwcm9wZXJseSB1dGlsaXplIHRoZSA2IG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcyBmb3IgcHJlZGljdGlvbiBvZiBvdmVyYWxsIGZsaWdodCBkZWxheXMgYXMgb3V0bGluZWQgYWJvdmUgaW4gYEZpZ3VyZSA2YCBhIHR3byBzdGVwIGZlYXR1cmUgY3JlYXRpb24gcHJvY2VzcyB3YXMgdXRpbGl6ZWQuICBJbml0aWFsbHkgYSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIHdhcyBjb25kdWN0ZWQgdG8gY29tYmluZSB0aGVzZSA2IHZhcmlhYmxlcyBpbnRvIHByaW5jaXBhbCBjb21wb25lbnRzIHRoYXQgY2FwdHVyZSBhIGNvbWJpbmF0aW9uIG9mIHRoZXNlIDYgdmFyaWFibGVzLiBVdGlsaXppbmcgdGhlIFBDQSBhcHByb2FjaCBhbGxvd3MgZm9yIGEgbW9yZSBzdHJlYW1saW5lZCBhcHByb2FjaCBmb3IgY3JlYXRpbmcgcHJlZGljdGlvbiBtb2RlbHMgZm9yIG92ZXJhbGwgZmxpZ2h0IGRlbGF5IHRpbWVzLiAgT25jZSB0cmFuc2Zvcm1lZCBpbnRvIHByaW5jaXBhbCBjb21wb25lbnRzLCBhIGstbWVhbnMgY2x1c3RlciBhbmFseXNpcyB3YXMgdGhlbiBjb25kdWN0ZWQgdG8gYWRkIGZvciBhbiBhZGRpdGlvbmFsIGZlYXR1cmUgdG8gdXRpbGl6ZSB3aGVuIGNyZWF0aW5nIGZsaWdodCBkZWxheSBwcmVkaWN0aW9uIG1vZGVscy4gDQoNCiMjIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzDQoNClV0aWxpemluZyB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIGFzIGlkZW50aWZpZWQgaW4gZmlndXJlIDYgKG51bWJlciBvZiBmbGlnaHRzIHBlciBhaXJwb3J0LCBsYXRlIGluY29taW5nIHBsYW5lLCBiYWdnYWdlIGxvYWRpbmcgdGltZSwgZGlzdGFuY2UgYmV0d2VlbiBhaXJwb3J0cywgc3VwcG9ydCBjcmV3cyBhdmFpbGFibGUsIGFuZCB3ZWF0aGVyKSBhIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgd2FzIGNvbmR1Y3RlZCB0byBmdXJ0aGVyIHJlZHVjZSByZWR1bmRhbmNpZXMuIEFuIGluaXRpYWwgc2NyZWUgcGxvdCB3YXMgdXRpbGl6ZWQgdG8gZGV0ZXJtaW5lIHRoZSAiZWxib3ciIGN1dG9mZiBvZiBQQzEgJiBQQzIgd2hlcmUgdGhlIHZhcmlhbmNlIHN0ZWVwbHkgY3V0cyBvZmYuICBVcG9uIGNhbGN1bGF0aW9uIG9mIHRoZSB0b3RhbCBwZXJjZW50YWdlIG9mIHZhcmlhbmNlLCBpdCB3YXMgZm91bmQgdGhhdCBpbiBvcmRlciB0byBjcmVhdGUgYSBtb2RlbCB0aGF0IGFjY291bnRzIGZvciA+OTAlIG9mIHRoZSB2YXJpYW5jZSBpbiB0aGUgdG90YWwgZmxpZ2h0IGRlbGF5IGRhdGEsIHRoZSBmaXJzdCA1IFBDcyBtdXN0IGJlIHV0aWxpemVkLiANCg0KYGBge3J9DQpkZWxheS5zdGQua20gPC0gZGVsYXkuc3RkWy1jKDYsIDc6OSldDQoNCnBjYS5kZWxheSA8LSBwcmNvbXAoZGVsYXkuc3RkLmttLCBzY2FsZSA9IFRSVUUpDQpwY2EuZGVsYXkuMSA8LSBjYmluZChkZWxheS5zdGQua20sIHBjYS5kZWxheSR4KQ0KDQpwY2EudmFyIDwtIGFwcGx5KHBjYS5kZWxheSR4LCAyLCB2YXIpDQpgYGANCg0KYGBge3J9DQoNCnBsb3QocGNhLmRlbGF5LCB0eXBlID0ibCIsIHlsaW09YygwLDQpLCB4bGltPWMoMSw2LjUpLCBtYWluID0gTlVMTCkNCnRleHQoKDE6MTApKzAuMTUsIHBjYS52YXIrMC4zLCBhcy5jaGFyYWN0ZXIocm91bmQocGNhLnZhciwgMykpLCBjZXggPSAwLjcpDQp0aXRsZShtYWluID0gIlZhcmlhbmNlIG9mIEluZGl2aWR1YWwgUENzIiwgeGxhYiA9IlByaW5jaXBhbCBDb21wb25lbnRzIiwgc3ViID0gIkZpZ3VyZSA3OiBTY3JlZSBQbG90IHRvIGRldGVybWluZSBudW1iZXIgb2YgUENzIHRvIHVzZSIpDQpwb2ludHMoeCA9IDIsIHkgPSAocGNhLmRlbGF5JHNkZXZbMl0pXjIsIHBjaCA9IDE5LCBjb2wgPSAicHVycGxlIiwgY2V4ID0gMS41KQ0KdGV4dCh4ID0gMiwgeSA9IDAuNSwgIjYxLjkzOCUNCiAgICAgb2YgVmFyLiIsIGNvbCA9ICJwdXJwbGUiLCBjZXggPSAwLjYpDQpwb2ludHMoeCA9IDUsIHkgPSAocGNhLmRlbGF5JHNkZXZbNV0pXjIsIHBjaCA9IDE5LCBjb2wgPSAiZGFya3JlZCIsIGNleCA9IDEuNSkNCnRleHQoeCA9IDUsIHkgPSAwLjIsICI5MS45NTMlDQogICAgIG9mIFZhci4iLCBjb2wgPSAiZGFya3JlZCIsIGNleCA9IDAuNikNCg0Ka2FibGUocGNhLmRlbGF5JHJvdGF0aW9uWyxjKDE6NSldLCBjYXB0aW9uID0gIlRhYmxlIDE1OiANCiAgICAgIDxicj5QcmluY2lwYWwgQ29tcG9uZW50IENvZWZmaWNpZW50cyAocm90YXRpb24gbWF0cml4KSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0KYGBgDQoNCg0KIyMjIGstTWVhbnMgQ2x1c3RlcmluZyBGZWF0dXJlIENyZWF0aW9uDQoNClV0aWxpemluZyB0aGUgNSBjcmVhdGVkIHByaW5jaXBhbCBjb21wb25lbnRzLCBrLU1lYW5zIGNsdXN0ZXJpbmcgd2FzIGNvbXBsZXRlZCB0byBjcmVhdGUgY2x1c3RlcnMgdG8gYWlkIGluIHRvdGFsIGZsaWdodCBkZWxheSBwcmVkaWN0aW9uLiBJbiBvcmRlciB0byBkZXRlcm1pbmUgdGhlIGJlc3QgZml0IG51bWJlciBvZiBjbHVzdGVycywgYSBgd3NzYCBhbmQgYHNpbGhvdWV0dGVgIHBsb3RzIHdlcmUgY3JlYXRlZCBhcyBhbiBpbml0aWFsIGFuYWx5c2lzIG9mIHRoZSBQQ0EgZGF0YS4gQXMgZGVtb25zdHJhdGVkIGJlbG93IGluIGBGaWd1cmUgN2Agd2FzIGRldGVybWluZWQgdGhhdCB0aGUgYmVzdCBmaXQgbnVtYmVyIG9mIGNsdXN0ZXJzIGZvciB0aGlzIHRyYW5zZm9ybWVkIFBDQSBkYXRhIHdhcyAyIGNsdXN0ZXJzLiAgVXRpbGl6aW5nIHRoYXQgcHJlLWRldGVybWluZWQgbnVtYmVyIG9mIGNsdXN0ZXJzLCB0aGUgUENBIGRhdGEgd2FzIHRoZW4gY2x1c3RlcmVkIGludG8gMiBncm91cHMsIHVzaW5nIGEgdG90YWwgb2YgMjUgcnVuIHRocm91Z2hzIG9mIHRoZSBjbHVzdGVyaW5nIHByb2Nlc3MgdG8gYmVzdCBkZXRlcm1pbmUgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgY2x1c3RlcnMgYW5kIHJlZHVjZSBvdmVyYWxsIGNsdXN0ZXIgb3ZlcmxhcC4gDQoNCmBgYHtyfQ0KcGNhLmRlbGF5LjIgPC0gcGNhLmRlbGF5LjEgJT4lIA0KICBkcGx5cjo6c2VsZWN0KCJQQzEiLCAiUEMyIiwgIlBDMyIsICJQQzQiLCAiUEM1IikgDQoNCmttZWFucy53c3MgPC0gZnZpel9uYmNsdXN0KHBjYS5kZWxheS4yLCBGVU4gPSBoY3V0LCBtZXRob2QgPSAid3NzIikNCmttZWFucy5zaWwgPC0gZnZpel9uYmNsdXN0KHBjYS5kZWxheS4yLCBGVU4gPSBoY3V0LCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpDQpgYGANCg0KYGBge3J9DQpncmlkLmFycmFuZ2UoDQogIGFycmFuZ2VHcm9iKGttZWFucy53c3MsIGttZWFucy5zaWwsIG5jb2wgPSAyLG5yb3cgPSAxKSwNCiAgdG9wID0gdGV4dEdyb2IoIk9wdGltYWwgQ2x1c3RlciBEZXRlcm1pbmF0aW9uIiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMjAsIGZvbnRmYWNlID0gImJvbGQiKSksDQogIGJvdHRvbSA9IHRleHRHcm9iKCJGaWd1cmUgNw0KICANCiAgRGV0ZXJtaW5hdGlvbiBvZiBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBmb3IgUENBIG92ZXJhbGwgZmxpZ2h0IGRhdGEiKSkNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMzQ1KQ0KZGVsYXkua21lYW4gPC0ga21lYW5zKHBjYS5kZWxheS4yLCBjZW50ZXJzID0gIDIsIG5zdGFydCA9IDI1KQ0KcGNhLmRlbGF5LjIkY2x1c3RlciA8LSBhcy5mYWN0b3IoZGVsYXkua21lYW4kY2x1c3RlcikNCnBjYS5kZWxheS5maW5hbCA8LSBwY2EuZGVsYXkuMg0KcGNhLmRlbGF5LmZpbmFsJGFycl9kZWxheSA8LSBkZWxheS5zdGQkYXJyX2RlbGF5DQpgYGANCg0KDQpgYGB7cn0NCmVpZ2VuIDwtIHJvdW5kKGdldF9laWdlbnZhbHVlKHBjYS5kZWxheSksIDEpDQp2YXIuZWlnZW4gPC0gZWlnZW4kdmFyaWFuY2UucGVyY2VudA0KY2x1c3Rlcl9jZW50ZXJzIDwtIGFnZ3JlZ2F0ZShwY2EuZGVsYXkuMiwgYnk9bGlzdChjbHVzdGVyID0gcGNhLmRlbGF5LjIkY2x1c3RlciksIEZVTiA9IG1lYW4pICU+JSANCiAgZHBseXI6OnNlbGVjdCgtNykNCmBgYA0KDQpgYGB7cn0NCmdnc2NhdHRlcihwY2EuZGVsYXkuMiwgeCA9ICJQQzEiLCB5ID0gIlBDMiIsDQogICAgICAgICAgY29sb3IgPSAiY2x1c3RlciIsDQogICAgICAgICAgcGFsZXR0ZSA9ICJucGciLA0KICAgICAgICAgIGVsbGlwc2UgPSBUUlVFLA0KICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLA0KICAgICAgICAgIGxlZ2VuZCA9ICJyaWdodCIsDQogICAgICAgICAgeGxhYiA9IHBhc3RlKCJQQzEgKCIsdmFyLmVpZ2VuWzFdLCIlKSIpLA0KICAgICAgICAgIHlsYWIgPSBwYXN0ZSgiUEMyICgiLCB2YXIuZWlnZW5bMl0sIiUpIikpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gY2x1c3Rlcl9jZW50ZXJzLCBzaGFwZSA9IDE4LCBzaXplID0gNCwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnModGl0bGUgPSAiQ2x1c3RlciBBbmFseXNpcyIsIGNhcHRpb24gPSAiRmlndXJlIDgNCiAgQ2x1c3RlcmluZyBvZiBQQzEgJiBQQzIgdXNpbmcgay1NZWFucyBDbHVzdGVyIGFuYWx5c2lzLiBCbGFjayBkaWFtb25kcyByZXByZXNlbnQgY2x1c3RlciBtZWFucy4iKSArIA0KICB0aGVtZV9idygpICsNCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCiAgDQpgYGANCg0KIyBDb25jbHVzaW9ucw0KDQpJbiBhIGZ1cnRoZXIgYW5hbHlzaXMgb2YgZ2xvYmFsIGFpcmxpbmUgZGF0YSwgaXQgaGFzIGJlZW4gZGV0ZXJtaW5lZCB0aGF0IGluIG9yZGVyIHRvIGJlc3QgY3JlYXRlIHJlYWwgdGltZSBwcmVkaWN0aW9uIG1vZGVscyBmb3Igb3ZlcmFsbCBmbGlnaHQgZGVsYXksIHRoZSByYXcgZ2xvYmFsIGZsaWdodCBkYXRhIG11c3QgdW5kZXJnbyBhIGxpdGFueSBvZiBwcmVwYXJhdGlvbiBzdGVwcy4gIEZpcnN0IHRoZSBtaXNzaW5nIGRhdGEgbXVzdCBiZSBpbXB1dGVkIHV0aWxpemluZyB0aGUgYmVzdCBmaXQgYXBwcm9hY2ggZm9yIHRoZSBjdXJyZW50IHBhdHRlcm4gb2YgbWlzc2luZyBvYnNlcnZhdGlvbnMuICBPbmNlIHRoZSBiZXN0IGZpdCBpbXB1dGF0aW9uIHByb2Nlc3Mgd2FzIGRldGVybWluZWQsIHRoZSBpbXB1dGVkIGRhdGEgbXVzdCB1bmRlcmdvIGZlYXR1cmUgdHJhbnNmb3JtYXRpb24gdG8gbm9ybWFsaXplIGFueSBza2V3IHRvIHByZXZlbnQgYW55IHVubmVjZXNzYXJ5IHdlaWdodGluZyBvciBza2V3IGluIHByZWRpY3Rpb24gbW9kZWxzLiBOb3JtYWxpemF0aW9uIGlzIGltcGVyYXRpdmUgZm9yIGdsb2JhbCBmbGlnaHQgZGF0YSBhcyB0aGUgdmFyaWFibGVzIGNvbnRhaW4gd2lkZWx5IGRpZmZlcmVudCB1bml0cyBhbmQgc2NhbGVzIHRoYXQgd291bGQgcHJldmVudCBhIGNsZWFuIGFuYWx5c2lzIHdpdGhvdXQgbm9ybWFsaXphdGlvbi4gIEZvciBleGFtcGxlLCB3aXRob3V0IG5vcm1hbGl6YXRpb24gbnVtYmVyIG9mIGZsaWdodHMgYXQgdGhlIGRlcGFydGluZyBhaXJwb3J0IGFuZCBmdWVsaW5nIHRpbWVzIHdvdWxkIG5vdCBjb29wZXJhdGUgaW4gYSBwcmVkaWN0aW9uIG1vZGVsIGR1ZSB0byB0aGUgbWFzc2l2ZSBkaWZmZXJlbmNlIGluIG1pbmltdW1zIGFuZCBtYXhpbXVtcyBvZiB0aGVzZSB2YXJpYWJsZXMuICANCg0KQWZ0ZXIgbm9ybWFsaXphdGlvbiB0aGUgZGF0YSB0aGVuIG11c3QgYmUgYW5hbHl6ZWQgdG8gaWRlbnRpZnkgYW5kIHJlbW92ZSByZWR1bmRhbmN5IHRvIHByZXZlbnQgb3Zlci1maXR0aW5nIG9mIHByZWRpY3Rpb24gbW9kZWxzIGFuZCByZWR1Y2Ugb3ZlcmFsbCBjb21wdXRhdGlvbmFsIGJ1cmRlbiBkdXJpbmcgdGhlIG1vZGVsIGNyZWF0aW9uIHByb2Nlc3MuICBBcyB0aGUgYW1vdW50IG9mIGdsb2JhbCBmbGlnaHQgZGF0YSBpcyByb2J1c3QsIHRoaXMgc3RlcCBpcyBlc3NlbnRpYWwgaW4gY3JlYXRpbmcgYm90aCBxdWljayBhbmQgYWNjdXJhdGUgcHJlZGljdGlvbiBtb2RlbHMgc2hvdWxkIHRoZXkgYmUgaW50ZW5kZWQgZm9yIHJlYWwgdGltZSBmbGlnaHQgZGVsYXkgcHJlZGljdGlvbi4gIExhc3RseSwgcHJpb3IgdG8gbW9kZWwgY3JlYXRpb24sIHRoZSBkYXRhIG11c3QgdW5kZXJnbyBhZGRpdGlvbmFsIGZlYXR1cmUgY3JlYXRpb24gc3VjaCBhcyBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIGFuZCBrLU1lYW5zIGNsdXN0ZXJpbmcgdG8gY29tYmluZSB0aGUgbnVtZXJvdXMgdmFyaWFibGVzIGludG8gYSByZWR1Y2VkIGRhdGEgc2V0IHdpdGhvdXQgbG9zcyBvZiBhY3R1YWwgZGF0YS4gIFRoZSBjcmVhdGlvbiBvZiBhbiBhZGRpdGlvbmFsIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRocm91Z2ggay1NZWFucyBjbHVzdGVyaW5nIHV0aWxpemluZyB0aGUgYWdncmVnYXRlIFBDQSBkYXRhIGJ5IHByb3ZpZGluZyB0aGUgZnV0dXJlIHByZWRpY3Rpb24gbW9kZWxzIHdpdGggbWVhbmluZ2Z1bCBwcmUtYW5hbHlzaXMgZGF0YSB0byBpbXByb3ZlIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hpbGUgZnVydGhlciBwcmV2ZW50aW5nIG92ZXItZml0dGluZy4gDQoNCiMgUmVmZXJlbmNlcw0KDQpmbGlnaHQgZGVsYXkgcmlnaHRzOiBodHRwczovL3d3dy5iYXR0bGVmYWNlLmNvbS9ibG9nL3lvdXItZ2xvYmFsLWd1aWRlLXRvLWZsaWdodC1kZWxheS1yaWdodHMvDQoNCjxhIGhyZWY9Imh0dHBzOi8vYXRhZy5vcmcvZmFjdHMtZmlndXJlcyI+QWlyIFRyYW5zcG9ydCBBY3Rpb24gR3JvdXA8L2E+IA0KDQpIb3RkZWNrIGltcHV0YXRpb246IGh0dHBzOi8vcG1jLm5jYmkubmxtLm5paC5nb3YvYXJ0aWNsZXMvUE1DMzEzMDMzOC8gDQoNCg==