1 Introduction

Air travel has become a pivotal feature of the ever globalized world, driving global trade, connecting individuals and countries alike, and driving global economic growth. According to the Air Transport Action Group in 2023, a total of 35.3 million flights carried a total of 4.4 billion passengers and 61.4 million tonnes of cargo between 4,072 global airports. This resulted in a $4.1 trillion total global economic contribution in 2023 alone. While only 1% of all global trade is conducted through air travel, air shipping accounts for 33% of all trade value, indicating that the most expensive goods are shipped via air travel. 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.

Due to the massive impact of air travel on our global communities and GDPs, there has been significant efforts implemented to streamline air travel and reduce fight delays to ensure smooth predictable travel. Mitigation and reduction of overall air travel delays will allow for increased air travel driven trade output and overall boosts to the global economy. In order to properly mitigate air travel delays, confounding factors that may impact flight delays were captured by Deepti Gupta in Applied Analytics through Case Studies Using SAS and R for analysis and modeling.

1.1 Analysis Goals

In order to aid in reducing overall flight delays, the factors with the greatest impact on overall flight delay times must be identified. Once identified, this information can then be leveraged by airports and airlines to improve staffing, streamline pre-flight processes, and ultimately mitigate delays due to factors within the airlines’ or airports’ control. In addition to preventative measures to mitigate delays, understanding the greatest impacts on flight delays globally will allow for creation of prediction models to live predict upcoming flight delays for real time mitigation before the delays in question even happen. The subsequent analysis and prediction model creation of the global flight delay will serve to address two points of concern:

  • Identifying the variables with greatest impact on overall flight delay for subsequent mitigation and preventative efforts
  • Live flight delay prediction based on available flight data prior to landing (predicting total delay time before flights depart)

1.2 Overall Approach

Prior to any data analysis and visualization, the airline delay data was cleaned and modified in a process called exploratory data analysis. First, to allow for multiple analysis and prediction approaches, two numeric variables were binned to create categorical values (baggage loading time & cleaning time). After binning to create categorical variables, the data was examined to identify and subsequently remove impossible outlier values.

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. For the purposes of this analysis and prediction model builds, a Multiple Imputation by Chained Equations (MICE) approach was implemented to impute all missing observations.

Once imputed, the data was then analysed for any potential skew and subsequently normalized utilizing a min-max equation. This normalization was implemented to further prepare the data for use in predictive modeling by reducing any skew and ensuring normal distributions to ensure the assumptions of regression were met. The normalized data was then analyzed for redundancies and the high impact variables were identified for retention through RFE feature selection. Once fully pruned, the PCA component data was then subjected to k-Nearest Neighbor cluster based feature extraction. This k-Means cluster ID creation allows for consolidation of the selected PCA components into a single binary variable reducing the overall number of variables for analysis without loss of data.

The normalized data with added PCA components and k-Means cluster IDs assigned was then utilized to develop four linear regression prediction models for evaluation and ultimate selection. These linear regression prediction models were analyzed using 80:20 cross validation to ensure the best fit model was identified through mean Mean Square Error (MSE) comparison. The ultimate selection of a best fit linear regression prediction model serves as an analytic tool to allow airlines to best determine where upstream delay reduction approaches should be implemented (i.e: ensuring adequate support crews are available, identifying ideal baggage loading / plane cleaning / plane fueling time windows, etc.).

A second prediction model was developed omitting all PCA components and k-Means cluster IDs to develop a low computational burden rapid prediction model for use in live prediction of delays based on current airport conditions. Multiple models were developed using the previously split 80:20 train:test data and compared for efficacy and reliability utilizing Akaike Information Criteria (AIC) values, ROC Receiver Operating Characteristic (ROC) curve comparisons, and calculated Area Under the Curve (AUC) values as calculated from the ROC curves.

delay <- read.csv("https://nlepera.github.io/sta552/HW01/data/Flight_delay-data.csv")

2 Data Collection and Preparation

2.1 Data Details

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 was downloaded from https://pengdsci.github.io/datasets/.

A total of 3593 flights were analyzed for 11 variables of interest (1 categorical & 11 numeric). The flight variables as recorded include:

var.details <- data.frame(
name = c("Carrier", "Airport Distance", "Number of Flights", "Weather", "Support Crews Available", "Baggage Loading Time", "Late Incoming Plane Arrival", "Cleaning Time", "Fueling Time", "Security Time", "Overall Flight Delay"),
type = c("Categorical", "Numeric", "Numeric", "Numeric", "Numeric", "Numeric [Binned]", "Numeric", "Numeric [Binned]", "Numeric", "Numeric", "Numeric"),
details = c("Airline (Carrier)", "Distance (miles) between departure and arrival airport", "Total number of flights at arrival airport", "A ranking of delays due to weather condition (0: Mild to 10: Extreme)", "Total number of support crew available", "Total time for baggage loading (minutes)", "Delay time for plane arrival prior to flight (minutes)", "Time required to clean plane after arrival prior to passenger loading (minutes)", "Time required to fuel aircraft (minutes)", "Time required for security checks (minutes)", "Total flight delay time (minutes)")
)
kable(var.details, col.names = c("Variable Name", "Variable Type", "Details"), caption = "Table 1:
      <br>Global flight data details
      <br>note: numeric variables (Weather & Cleaning) were binned to allow for categorical styled handling") %>% 
  kable_styling()
Table 1:
Global flight data details
note: numeric variables (Weather & Cleaning) were binned to allow for categorical styled handling
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 Crews Available Numeric Total number of support crew available
Baggage Loading Time Numeric [Binned] Total time for baggage loading (minutes)
Late Incoming Plane Arrival Numeric Delay time for plane arrival prior to flight (minutes)
Cleaning Time Numeric [Binned] Time required to clean plane after arrival prior to passenger loading (minutes)
Fueling Time Numeric Time required to fuel aircraft (minutes)
Security Time Numeric Time required for security checks (minutes)
Overall Flight Delay Numeric Total flight delay time (minutes)

2.2 Data Preparation

2.2.1 Outlier Removal

A cursory analysis of all data points identified four (4) observations with impossible outliers in the cleaning time variable. These values are known to be impossible as a negative duration for cleaning value is impossible. Rather than dropping all data associated with these erroneous observations, these negative values were replaced with NA to allow for the remaining observations’ data to be utilized. These impossible values that were replaced with NA values will subsequently be imputed further along in the analysis process.

impossible <- delay %>% 
  filter(Cleaning_o < 0) %>% 
  dplyr::select(c(Carrier, Cleaning_o, Arr_Delay))

delay.clean <- delay %>% 
  mutate(Cleaning_o = replace(Cleaning_o, Cleaning_o < 0, "NA")) #removed outlier values without removing full observations

delay.clean$Cleaning_o <- as.numeric(delay.clean$Cleaning_o)
kable(impossible, col.names= c(var.details[1,1], var.details[8,1], var.details[11,1]) , caption = "Table 2:
      <br>Summary of impossible cleaning times
      <br>These times are considered impossible as negative time values cannot exist") %>% 
  kable_styling()
Table 2:
Summary of impossible cleaning times
These times are considered impossible as negative time values cannot exist
Carrier Cleaning Time Overall Flight Delay
B6 -1 70
EV -4 82
WN -1 75
EV -1 54
B6 -1 88
singlevar <- summary(delay.clean$Cleaning_o)
singlevar <- as.data.frame.AsIs(singlevar)

kable(singlevar, col.names = var.details[8,1], caption = "Table 3:
      <br>Summary statistics of cleaning time observations after impossible values replaced with NA") %>% 
  kable_styling()
Table 3:
Summary statistics of cleaning time observations after impossible values replaced with NA
Cleaning Time
Min. 0.00
1st Qu. 8.00
Median 10.00
Mean 10.04
3rd Qu. 12.00
Max. 23.00
NA’s 5

2.2.2 Observation Binning

As outlined in the table above in section 1.2 Overall Approach and 2.1 Data Details, two numerical variables were binned to create pseudo categorical variables. By binning these numeric variables into pseudo categorical variables, additional categorical analysis may be conducted and additional prediction models may be utilized for determining and predicting overall flight delays.

The baggage loading time variable was binned in to three (3) duration groups:

  • Fast (14 - 15 minutes)
  • Moderate (16 - 17 minutes)
  • Slowest (18 - 19 minutes)

Additionally, the cleaning time variable was binned in to four (4) duration groups:

  • Immediate (0 - 5 minutes)
  • Quick (6 - 11 minutes)
  • Moderate (12 - 17 minutes)
  • Slow (18 - 23 minutes)
bin_clean <- c(0, 6, 12, 18, 23)
labels_clean <- c("Immediate", "Quick", "Moderate", "Slow")
bin_baggage <- c(14, 16, 18, 19)
labels_baggage <- c("Fast", "Moderate", "Slow")


delay.clean <- delay.clean %>% 
  mutate(Cleaning_o = cut(Cleaning_o, breaks = bin_clean, labels = labels_clean, right = TRUE, include.lowest = TRUE))

delay.clean <- delay.clean %>% 
  mutate(Baggage_loading_time = cut(Baggage_loading_time, breaks = bin_baggage, labels = labels_baggage, right = TRUE, include.lowest = TRUE))

kable(summary(delay.clean[, c(6,8)]), col.names = c(var.details[6,1], var.details[8,1]), caption = "Table 4:
     <br>Summary of binned global flight data variables") %>% 
  kable_styling()
Table 4:
Summary of binned global flight data variables
Baggage Loading Time Cleaning Time
Fast : 749 Immediate: 517
Moderate:2833 Quick :2261
Slow : 11 Moderate : 780
NA Slow : 30
NA NA’s : 5

2.2.3 Forced Missing Values

While the global flight delay data set as prepared by Gupti does not contain missing values, real world collection of global flight data will inevitably result in missing values and poor standardization across countries and regions. In order to ensure the data analysis and prediction models are prepared to handle these real world gaps in data, missing values were forced for three variables. The weather, support crew, and late initial plane arrival time variables were forced to have missing observations at random using the sample() procedure. Table 5 below outlines the total number of missing values that were forced into the data set as a training method to prepare for the real world data ambiguity.

weather.missing <- sample(1:3593, 5, replace = FALSE)
late.missing <- sample(1:3593, 6, replace = FALSE)
support.missing <- sample(1:3593, 4, replace = FALSE)
delay.clean$Weather[weather.missing] <- NA
delay.clean$Late_Arrival_o[late.missing] <- NA
delay.clean$Support_Crew_Available[support.missing] <- NA
kable(sapply(delay.clean[,c(4,5,7,8)], function (x) sum(is.na(x))), caption = "Table 5:
      <br>Count of forced missing observations per variable", col.names = c("Variables", "Count of Missing Observations")) %>% 
  kable_styling()
Table 5:
Count of forced missing observations per variable
Variables Count of Missing Observations
Weather 5
Support_Crew_Available 4
Late_Arrival_o 6
Cleaning_o 5

2.3 Missing Value Imputation

In the previous section 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. This knockout procedure was completed to allow for the subsequent data imputation process that will be required for real world data that contains natural missing values. When handling real world global flight delay data, all missing values must be imputed to ensure no gaps remain in the data, without completely dropping observations with missing data. This prevents overall data loss by removing the need to omit full observations based on a single missing value.

2.3.1 Missing Value Analysis and Intepretation

Prior to completing any analysis, the missing values in the airline delay data must be imputed to ensure there are no gaps in the data. . A summary of the missing values across each variable are included in the below Figure 1, Table 6, and Table 7. As demonstrated by the p value in Little’s MCAR Test (p = 0.6382091) 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).

missing.array <- plot_pattern(delay.clean, rotate = TRUE, caption = TRUE) +
  theme(axis.title.x.top = element_blank())
grid.arrange(arrangeGrob(missing.array, ncol = 1, nrow = 1),
  top = textGrob("Missing Values
                 ", gp = gpar(fontsize = 20, fontface = "bold")),
  bottom = textGrob("
  Figure 1
  The distribution pattern of missing values in flight delay data"))

kable(misty_test$result$little, col.names = c("# of Obs.", "# Missing Values", "# of Patterns in Missing Vals", "Statistic", "DF", "p Value"), caption = "Table 6:
      <br>Little's MCAR Test") %>% 
  kable_styling()
Table 6:
Little’s MCAR Test
# of Obs. # Missing Values # of Patterns in Missing Vals Statistic DF p Value
3593 20 5 25.17261 40 0.9675297
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 7:
<br>Summary count of missing values in each variable of the airline delay data. Variables with no missing values omitted.") %>% 
  kable_styling()
Table 7:
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.3.2 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.3 MICE Imputation Approach

A mice imputation model was created 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 2.

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

set.seed(122357)
mice1 <- mice(m.delay, m = 5, maxit = 10, seed = 123, print = FALSE)
mice.delay <- complete(mice1)
mice.delay$Cleaning_o <- as.factor(mice.delay$Cleaning_o)
levels(mice.delay$Cleaning_o) <- c("Immediate", "Quick", "Moderate", "Slow")
mice.plot <- plot_trace(mice1)

grid.arrange(arrangeGrob(mice.plot, ncol = 1, nrow =1),
             top = textGrob("MICE Model Imputation
                            ", gp = gpar(fontsize = 18, fontface = "bold")),
             bottom = textGrob("Figure 2:
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.", gp = gpar(fontsize = 10)))

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

Before passing the MICE imputation package through the complete() package to automatically combine the imputation runs into a single best fit imputed data set, the imputation statistics were calculated using Ruben’s rules first to help determine the efficacy and validity of this imputation. 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. As demonstrated below in Table 9 B.bar and Standard Error values demonstrate near negligible variance between each imputation runs and minimal error for the Weather, Late Incoming Plane Arrival, and Cleaning time variables. While the Support Crews Available variance between imputation runs has returned a la value, the standard of error of 7 (7 crew members) supports this variance may not be inconceivable.

\[ \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.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 <- data.frame(
    Statistic = c("Q.bar", "U.bar", "B.var", "T", "Std.Err"),
    mice.weather = c(q.bar.weather.m, u.bar.weather.m, b.weather.m, t.weather.m, se.weather.m),
    mice.sup.crew = c(q.bar.supcrew.m, u.bar.supcrew.m, b.supcrew.m, t.supcrew.m, se.supcrew.m),
    mice.late.incoming = c(q.bar.lateincom.m, u.bar.lateincom.m, b.lateincom.m, t.lateincom.m, se.lateincom.m),
    mice.clean = c(q.bar.clean.m, u.bar.clean.m, b.clean.m, t.clean.m, se.clean.m)
)
kable(mice.merge, col.names = c("Statistic", var.details[4,1], var.details[5,1], var.details[7,1], var.details[8,1]) , caption = "Table 9:
      <br>
      Summary statistics using Ruben's Rules for MICE Imputation for missing values in global flight delay data <br> number of imputations (m) = 5 <br> maximum iterations (maxit) = 10") %>% 
  kable_styling()
Table 9:
Summary statistics using Ruben’s Rules for MICE Imputation for missing values in global flight delay data
number of imputations (m) = 5
maximum iterations (maxit) = 10
Statistic Weather Support Crews Available Late Incoming Plane Arrival Cleaning Time
Q.bar 5.4400000 86.150000 18.6333333 2.0400000
U.bar 0.2359592 18.894378 0.2410825 0.2696663
B.var 0.0280000 382.737500 0.0750000 0.0480000
T 5.4736000 545.435000 18.7233333 2.0976000
Std.Err 0.0748331 8.749143 0.1224745 0.0979796

As a supporting analysis to the Ruben’s Rules totals above, the summary statistics for the initial variables prior to imputation and the summary statistics after imputation were reviewed (Table 10.a & Table 10.b below). The imputed Weather and Late Incoming Plane Arrival variables displayed no change to the mean or median post imputation, indicating a high success rate for the MICE ppm imputation approach. Additionally, the Support Crews Available variable demonstrated no change in mean and only a 0.03 change in the median. As this variable is measuring the number of people available in the support crews this 0.03 change in median can be completely ignored as there cannot feasibly be 0.03 of a person. Finally the imputation of Cleaning Time variable demonstrated the greatest frequency increase for the mode value, further corroborating the success of the MICE ppm imputation for use with a categorical variable.

kable(summary(delay.clean[, c(4, 5, 7, 8)]), col.names = c(var.details[4,1], var.details[5,1], var.details[7,1], var.details[8,1]), caption = "Table 10.a:
<br>Summary statistics of variables with missing values before MICE imputation") %>% 
  kable_styling()
Table 10.a:
Summary statistics of variables with missing values before MICE imputation
Weather Support Crews Available Late Incoming Plane Arrival Cleaning Time
Min. :5.000 Min. : 0 Min. :15.00 Immediate: 517
1st Qu.:5.000 1st Qu.: 56 1st Qu.:18.00 Quick :2261
Median :5.000 Median : 83 Median :19.00 Moderate : 780
Mean :5.354 Mean : 85 Mean :18.74 Slow : 30
3rd Qu.:6.000 3rd Qu.:112 3rd Qu.:19.00 NA’s : 5
Max. :6.000 Max. :222 Max. :22.00 NA
NA’s :5 NA’s :4 NA’s :6 NA
kable(summary(mice.delay[, c(3, 4, 6, 7)]), col.names = c(var.details[4,1], var.details[5,1], var.details[7,1], var.details[8,1]), caption = "Table 10.b:
<br>Summary statistics of variables with missing values after MICE imputation") %>% 
  kable_styling()
Table 10.b:
Summary statistics of variables with missing values after MICE imputation
Weather Support Crews Available Late Incoming Plane Arrival Cleaning Time
Min. :5.000 Min. : 0.00 Min. :15.00 Immediate: 518
1st Qu.:5.000 1st Qu.: 56.00 1st Qu.:18.00 Quick :2265
Median :5.000 Median : 83.00 Median :19.00 Moderate : 780
Mean :5.354 Mean : 85.03 Mean :18.74 Slow : 30
3rd Qu.:6.000 3rd Qu.:112.00 3rd Qu.:19.00 NA
Max. :6.000 Max. :222.00 Max. :22.00 NA

3 Exploratory Data Analysis (EDA)

3.1 Skew and Distribuion 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.

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

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

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

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

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

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

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

weather_dens <- ggplot(mice.delay, aes(x = Weather)) + 
  geom_density(fill = "blue", alpha = 0.5) +
  stat_function(fun = dnorm, args = list(mean = mean(mice.delay$Weather), sd = sd(mice.delay$Weather)), color = "darkred", linewidth = 1, linetype = "dashed") +
  geom_vline(aes(xintercept = mean(mice.delay$Weather)), color = "black") +
  geom_vline(aes(xintercept = median(mice.delay$Weather)), color = "black", linetype = "dashed") +
  labs(title = "Weather Conditions",
       x = "Ranking of Weather Severity",
       y = "Density")  
baggage_bar <- ggplot(mice.delay, 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(mice.delay, 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 3
  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 3, 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(mice.delay[c(2,4,6)]), digits = 1), caption = "Table 11:
<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(var.details[3,1], var.details[5,1], var.details[7,1])) %>% 
  kable_styling()
Table 11:
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.
Number of Flights Support Crews Available Late Incoming Plane Arrival
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(mice.delay[c(5, 7)]), caption = "Table 12:
<br>Frequency count to capture the mode of categorical variables with skewed distributions.", col.names = c(var.details[6,1], var.details[8,1])) %>% 
  kable_styling()
Table 12:
Frequency count to capture the mode of categorical variables with skewed distributions.
Baggage Loading Time Cleaning Time
Fast : 749 Immediate: 518
Moderate:2833 Quick :2265
Slow : 11 Moderate : 780
NA Slow : 30

3.1.2 Skew Correction Trough 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)}\]

mice.delay$cleaning <- as.numeric(mice.delay$Cleaning_o)
mice.delay$baggage <- as.numeric(mice.delay$Baggage_loading_time)

delay.std <- data.frame(
  airport_dist = (mice.delay$Airport_Distance - min(mice.delay$Airport_Distance)) / (max(mice.delay$Airport_Distance) - min(mice.delay$Airport_Distance)),
  num_flights = (mice.delay$Number_of_flights - min(mice.delay$Number_of_flights)) / (max(mice.delay$Number_of_flights) - min(mice.delay$Number_of_flights)),
  weather = (mice.delay$Weather - min(mice.delay$Weather)) / (max(mice.delay$Weather) - min(mice.delay$Weather)),
  sup_crew = (mice.delay$Support_Crew_Available - min(mice.delay$Support_Crew_Available)) / (max(mice.delay$Support_Crew_Available) - min(mice.delay$Support_Crew_Available)),
  late_incoming = (mice.delay$Late_Arrival_o - min(mice.delay$Late_Arrival_o)) / (max(mice.delay$Late_Arrival_o) - min(mice.delay$Late_Arrival_o)),
  fueling = (mice.delay$Fueling_o - min(mice.delay$Fueling_o)) / (max(mice.delay$Fueling_o) - min(mice.delay$Fueling_o)),
  security = (mice.delay$Security_o - min(mice.delay$Security_o)) / (max(mice.delay$Security_o) - min(mice.delay$Security_o)),
  arr_delay = (mice.delay$Arr_Delay - min(mice.delay$Arr_Delay)) / (max(mice.delay$Arr_Delay) - min(mice.delay$Arr_Delay)),
  cleaning = (mice.delay$cleaning - min(mice.delay$cleaning)) / (max(mice.delay$cleaning) - min(mice.delay$cleaning)),
  baggage = (mice.delay$baggage - min(mice.delay$baggage)) / (max(mice.delay$baggage) - min(mice.delay$baggage))
)
kable(summary(delay.std), col.names = c(var.details[2,1], var.details[3,1], var.details[4,1], var.details[5,1], var.details[6,1], var.details[7,1], var.details[8,1], var.details[9,1], var.details[10,1], var.details[11,1]), caption = "Table 13:
      <br>Summary statistics of normalized flight data") %>% 
  kable_styling() %>% 
  scroll_box(width = "100%")
Table 13:
Summary statistics of normalized flight data
Airport Distance Number of Flights Weather Support Crews Available Baggage Loading Time Late Incoming Plane Arrival Cleaning Time Fueling Time Security Time Overall Flight Delay
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.3333 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.3333 Median :0.5000
Mean :0.5395 Mean :0.5768 Mean :0.3537 Mean :0.3830 Mean :0.5341 Mean :0.5222 Mean :0.4817 Mean :0.3878 Mean :0.3632 Mean :0.3973
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.3333 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.2 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 5 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 4. 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 = 5, number = 10)

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 4:
       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.3 Feature Creation

In order to properly utilize the 6 most important variables for prediction of overall flight delays as outlined above in Figure 4 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.3.1 Principal Component Analysis

Utilizing the most important variables as identified in Figure 4 (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 (Figure 5) 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 >80% of the variance in the total flight delay data, the first 4 PCs must be utilized.

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

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

pca.var <- apply(pca.delay$x, 2, var)
eigen.pca <- summary(pca.delay)


plot(pca.delay, type ="l", ylim=c(0,3), xlim=c(1,6.5), main = NULL)
text((1:10)+0.15, pca.var+0.2, as.character(round(pca.var, 3)), cex = 0.8)
title(main = "Variance of Individual PCs", xlab ="Principal Components", sub = "Figure 5: 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.7, "58.656%
     of Var.", col = "purple", cex = 0.8)
points(x = 4, y = (pca.delay$sdev[4])^2, pch = 19, col = "darkred", cex = 1.5)
text(x = 4, y = 0.5, "84.613%
     of Var.", col = "darkred", cex = 0.8)

kable(pca.delay$rotation[,c(1:5)], caption = "Table 14: 
      <br>Principal Component Coefficients (rotation matrix)") %>% 
  kable_styling()
Table 14:
Principal Component Coefficients (rotation matrix)
PC1 PC2 PC3 PC4 PC5
airport_dist 0.3706110 0.0899273 -0.4517494 0.7958560 -0.0373642
num_flights 0.5153757 0.0539246 -0.1114829 -0.1796823 -0.0161651
weather 0.2584309 -0.8926859 0.3298843 0.1642819 0.0014252
sup_crew -0.2986655 -0.4333072 -0.8063486 -0.2590790 0.0564256
late_incoming 0.4674321 0.0237125 -0.1323775 -0.3910015 -0.6664500
baggage 0.4717573 0.0616424 -0.0833575 -0.2955247 0.7422945
kable(eigen.pca$importance, caption = "Table 15: 
      <br>Variance captured by each principal component, as calculated by using eigenvalues") %>% 
  kable_styling()
Table 15:
Variance captured by each principal component, as calculated by using eigenvalues
PC1 PC2 PC3 PC4 PC5 PC6
Standard deviation 1.620099 0.9463168 0.9171852 0.8463776 0.7307255 0.6230744
Proportion of Variance 0.437450 0.1492500 0.1402000 0.1193900 0.0889900 0.0647000
Cumulative Proportion 0.437450 0.5867100 0.7269100 0.8463000 0.9353000 1.0000000

3.3.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.

kmeans.wss <- fviz_nbclust(pca.delay.1[-c(1:7, 12:13)], FUN = hcut, method = "wss")
kmeans.sil <- fviz_nbclust(pca.delay.1[-c(1:7, 12:13)], 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 6:
  Determination of optimal number of clusters for PCA overall flight data"))

set.seed(12345)
pca.delay.1$cluster <- kmeans(pca.delay.1[-c(1:7, 12:13)], centers =  2, nstart = 25)$cluster
pca.delay.1$centers <- kmeans(pca.delay.1[-c(1:7, 12:13)], centers =  2, nstart = 25)$center[1]
eigen <- round(get_eigenvalue(pca.delay), 1)
var.eigen <- eigen$variance.percent
cluster_centers <- aggregate(pca.delay.1, by=list(cluster = pca.delay.1$cluster), FUN = mean)
pca.delay.1$cluster <- as.factor(pca.delay.1$cluster)
ggscatter(pca.delay.1, 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(x = cluster_centers$PC1[1], y = cluster_centers$PC2[1], shape = 18, size = 5, color = "black") +
  geom_point(x = cluster_centers$PC1[2], y = cluster_centers$PC2[2], shape = 18, size = 5, color = "black") +
  labs(title = "Cluster Analysis", caption = "
  Figure 7:
  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 Linear Regression Model for Arrival Delay Prediction

4.1 Regression Model Creation

In order to determine the best linear regression prediction model for use in determining staff resourcing decisions to reduce global delay times. The following models were developed and analyzed to determine the best fit model to conduct these tests and determinations:

  • Model 1: Full regression model (Overall Flight Delay ~ All Variables in Final.Delay data set)
    • The full model was selected to serve as an initial analysis and point of reference for models 2 - 4.
  • Model 2: PCA & Cluster regression model (Overall Flight Delay ~ PC1 + PC2 + k-Means Cluster ID)
    • The PCA-Cluster model was selected as PCA accounts for all variables in the full model with preliminary transformation to best align the full model variables for optimized Overall Flight Delay prediction. Additionally by utilizing the k-Means cluster ID both created variables are present in this model with reduced redundancy than would be found in a model containing multiple PCs
  • Model 3: Step-wise automated selection regression model (Overall Flight Delay ~ Airport Distance + Number of flights + Weather + Support Crew Available + Late Incoming Plane Arrival + Baggage Loading Time + Cluster)
    • The step-wise automated selection model was chosen to remove potential human bias and error in creating the best fit prediction model. Both forward and backwards step-wise analysis was completed to identify the optimal model for prediction.
  • Model 4: Recursive feature elimination regression model (Overall Flight Delay ~ Late Incoming Planes + Number of Flights + Baggage Loading Times + k-Means Cluster ID)
    • The RFE model was created using the top 4 variables identified as important in arrival delay prediction as previously identified in Figure 4
final.delay <- pca.delay.1[-c(12:13, 15)]
final.delay$cluster <- as.numeric(final.delay$cluster)

lm.delay.full <- lm(arr_delay ~ ., data = final.delay)
lm.delay.step <- step(lm.delay.full, direction = "both", trace = 0)

4.2 Model Comparrison Through Cross Validation

With all four potential regression models created, 5-fold cross validation was conducted to calculate the Mean Square Error (MSE) of each validation run, followed by the mean MSE of all validation runs per prediction model. The data was split 80:20 into train:test data subsets respectively, utilizing the training subsets for the cross validation process. Through comparing the calculated training data mean MSE values, the best fit prediction model was selected for further testing utilizing the test data. As MSE represents the mean squared distance between the model’s predicted value and the actual associated data value, MSE can be utilized to compare the prediction accuracy of linear regression models and identify the best model for selection (lower MSE = more accurate model).

As identified below in Figure 8 and Table 16, model 2 returned the lowest mean MSE and lowest individual MSE values for each cross-validation fold; therefore model 2 (PCA model) was selected as the primary prediction model for live overall arrival delay times. As discussed earlier in section 3.3.1 Principal Component Analysis, each principal component contains a finely tuned transformation of all important variables in the global flight delay data. This holistic approach supports the returned results of model 2 returning the lowest MSE as the PCA component of this model was preliminary tuned for this purpose. Additionally the k-Means cluster ID was created utilizing only the created PCA components, further ensuring all aspects of the normalized data is represented in the regression model with minimal noise.

set.seed(123)
#shuffling the data
n <- dim(final.delay)[1] #set sample size  
obs.ID <- 1:n   #create observation ID to allow for random splitting
n.train <- round(0.8*n)  #create sample size for training data. round to keep whole number since no partial observations

shuffle.id  <-  sample(obs.ID, n, replace = FALSE)  #randomize created observation ID, does not allow for dupes in list.

shuffle.delay <- final.delay[shuffle.id, ] #orders rows of dataframe to be in order of shuffle.id created random obs.id list

#splitting the data

train.data <- shuffle.delay[1:n.train, ] #splitting shuffled data from 1st obs to end of training data (80%)
test.data <- shuffle.delay[(n.train + 1):n, ] #splitting shuffled data from 1st obs after end of training data to end of remaining data (test)

n.fold <- round(n.train/5)-1   #number of observations in each of the 5 folds of validation


#validation loop - 5 fold

mse.lin.1 <- rep(0,5)
mse.lin.2 <- rep(0,5)
mse.lin.3 <- rep(0,5)
mse.lin.4 <- rep(0,5)

for (i in 1:5){
  valid.id = ((i-1) * n.fold + 1):(1 * n.fold)  #breaks down folds into 5 groups 
  lin.train = train.data[-valid.id, ]
  lin.valid = train.data[valid.id, ]
  
  #building models to run off of folded data
  m1 = lm(arr_delay ~ . , data = lin.train)
  m2 = lm(arr_delay ~ PC1 + cluster, data = lin.train)
  m3 = lm(arr_delay ~ airport_dist + num_flights + weather + sup_crew + late_incoming + baggage + cluster, data = lin.train)
  m4 = lm(arr_delay ~ late_incoming + num_flights + baggage + cluster, data = lin.train)
  
  #predicting flight delay using models
  predm1 = predict(m1, newdata = lin.train)
  predm2 = predict(m2, newdata = lin.train)
  predm3 = predict(m3, newdata = lin.train)
  predm4 = predict(m4, newdata = lin.train)
  
  #calculate mean square error between predicted values and values from validation data
  mse.lin.1[i] = mean((predm1 - lin.valid$arr_delay)^2)
  mse.lin.2[i] = mean((predm2 - lin.valid$arr_delay)^2)
  mse.lin.3[i] = mean((predm3 - lin.valid$arr_delay)^2)
  mse.lin.4[i] = mean((predm4 - lin.valid$arr_delay)^2)
}

l.mse.1 = mean(mse.lin.1)
l.mse.2 = mean(mse.lin.2)
l.mse.3 = mean(mse.lin.3)
l.mse.4 = mean(mse.lin.4)
mse.mod1 <- data.frame (
  x = 1:5,
  y = mse.lin.1
)
mse.mod2 <- data.frame (
  x = 1:5,
  y = mse.lin.2
)
mse.mod3 <- data.frame (
  x = 1:5,
  y = mse.lin.3
)
mse.mod4 <- data.frame (
  x = 1:5,
  y = mse.lin.4
)
mse.mod1.plot <- ggplot (mse.mod1, aes(x = x, y = y)) +
  geom_point(aes(x = mse.mod1$x, mse.mod1$y, color = "Model 1"))+
  geom_line(aes(x = mse.mod1$x, mse.mod1$y, color = "Model 1")) +
  geom_point (aes (x = mse.mod2$x, y = mse.mod2$y, color = "Model 2")) + 
  geom_line (aes (x = mse.mod2$x, y = mse.mod2$y, color = "Model 2")) +
  geom_point(aes(x = mse.mod3$x, y = mse.mod3$y, color = "Model 3"))+ 
  geom_line (aes(x = mse.mod3$x, y = mse.mod3$y, color = "Model 3"), linetype = "longdash") +
  geom_point(aes(x = mse.mod4$x, y = mse.mod4$y, color = "Model 4"))+ 
  geom_line (aes(x = mse.mod4$x, y = mse.mod4$y, color = "Model 4")) +
  labs (x = "Validation Fold", y = "Calculated MSE") +
  scale_color_manual (values = c("Model 1" = "black", "Model 2" = "blue", "Model 3" = "deeppink", "Model 4" = "orange4" ))+
  theme_bw()

l.mse <- data.frame(
  model1 = l.mse.1,
  model2 = l.mse.2,
  model3 = l.mse.3,
  model4 = l.mse.4
)
grid.arrange(
  arrangeGrob(mse.mod1.plot, ncol = 1,nrow = 1),
  top = textGrob("Prediction Model Cross Validation", gp = gpar(fontsize = 20, fontface = "bold")),
  bottom = textGrob("
  Figure 8:
  Comparrison of model Mean Square Error values across 5-fold cross validation"))

kable(l.mse, col.names = c("Model 1 (Full)", "Model 2 (PCA)", "Model 3 (Step-wise)", "Model 4 (RFE)"), caption = "Table 16:
      <br>Mean MSE values for each model calculated across 5 cross-validation runs") %>% 
  kable_styling()
Table 16:
Mean MSE values for each model calculated across 5 cross-validation runs
Model 1 (Full) Model 2 (PCA) Model 3 (Step-wise) Model 4 (RFE)
0.0451025 0.044092 0.0451025 0.0444112

4.3 PCA Model Analysis & Evaluation

Once selected as the best fit linear regression prediction model, the PCA model (Model 2) was re-evaluated utilizing the test data rather than the training data to ensure accuracy and ensure the model was not over-fitted. As a part of the development of each PCA, the original variables were linearly combined to create these principal components, effectively setting each PC to be a unique combination of 6 normalized variables (Airport Distance, Number of Flights, Weather, Support Crews Available, Late Incoming Plane Arrival, & Baggage Loading Time).

As identified in Table 17 below, each dependent variable returned a statistically significant p value of well below 0.05 when Model 2 was run utilizing the test data as well as the. This has ensured that all variables present in the regression model are significant to the prediction model and are not contributing noise or redundancy to the model.

lm.pca.train <- lm(arr_delay ~ PC1 + cluster, data = train.data)
lm.pca.test <- test.data
lm.pca.test$arr_delay_pred <- predict(lm.pca.train, newdata = lm.pca.test)
lm.pca <- final.delay
lm.pca$arr_delay_pred <- predict(lm.pca.train, newdata = lm.pca)

rsquares.lin <- data.frame(
  Data = c("Training Data", "Test Data", "Full Delay Data"),
  R.squared = c(summary(lm.pca.train)$r.squared ,(cor(lm.pca.test$arr_delay, lm.pca.test$arr_delay_pred))**2,(cor(lm.pca$arr_delay, lm.pca$arr_delay_pred))**2)
)
shrinkage.lin <- rsquares.lin
shrinkage.lin$Shrinkage <- c("", rsquares.lin[1,2] - rsquares.lin[2,2], rsquares.lin[1,2] - rsquares.lin[3,2])

shrinkage.lin$rely <- NULL

for (i in 1:length(shrinkage.lin)){
  if(shrinkage.lin[i,3] == ""){
    n <-  ""}
  else if(shrinkage.lin[i,3] < 0.1){
    n <-  "Reliable Model"}
  else if(shrinkage.lin[i,3] > 0.9){
    n <-  "Very Unreliable"}
  else {n  <-  "Potentially Reliable"}
  
  shrinkage.lin$rely[i] <- n  
}



kable(summary(lm.pca.train)$coefficients, col.names = c("Estimate", "Std. Error", "t value", "Pr(> I t I)"), caption = "Table 17:
      <br>Regression coefficients for linear regression prediction model 2 (PCA) run on <b>test data</b>") %>% 
  kable_styling()
Table 17:
Regression coefficients for linear regression prediction model 2 (PCA) run on test data
Estimate Std. Error t value Pr(> I t I)
(Intercept) 0.3660901 0.0074979 48.825947 0.0000000
PC1 0.0903510 0.0015904 56.810698 0.0000000
cluster 0.0166348 0.0057361 2.900037 0.0037595

Utilizing the above outlined final test data and full data regression coefficients from using the Model 2 prediction model, the Model 2 equations can be defined as:

\[ \begin{align*} Arrival \ Delay &\sim PC1 \ + \ Cluster \ + \ Intercept\\ \\ Arrival \ Delay \ _{train} &\sim 0.0902174*PC1 \ + \ 0.0163031*Cluster \ + \ 0.3665648 \end{align*} \]

As captured below in Table 18 the calculated \(R^2\) values for Model 2 as fit to the training data, test data, and full model delay data indicate Model 2 is a good fit for Overall Flight Delay prediction. The shrinkage was calculated for both the test data and the full delay data to measure the reliability of the prediction model. Utilizing the standard accepted cutoff of \(Shrinkage < 0.1\), the linear regression model, Model 2 (above equations) was found to be reliable as a prediction model for overall flight delays.

kable(shrinkage.lin, col.names = c("Data Utilized", "R$^2$ Value", "Calculated Shrinkage", "Reliability"),  caption = "Table 18:
      <br>Assessment of model fit and reliability") %>% 
  kable_styling()
Table 18:
Assessment of model fit and reliability
Data Utilized R\(^2\) Value Calculated Shrinkage Reliability
Training Data 0.7526599
Test Data 0.7717050 -0.0190451144868257 Reliable Model
Full Delay Data 0.7564583 -0.00379841841295325 Reliable Model

In reviewing the residual plots, QQ plots, and Cook’s distance as obtained from model 2 run on the test data the following information was obtained:

  • While there is a minor curve plotted in the Residuals vs. Fitted plot, the curve is minor ranging from 0.0 to 0.1
  • The Residual vs. Fitted plot demonstrates a random scatter centered around y=0 indicating the assumption of linear regression that the residuals are normally distributed with a mean of 0 is met
  • The Q-Q plot indicates a near perfect match between the residuals distribution and a normal distribution, proving the assumption of linear regression that the residuals are normally distributed is met
  • The Scale-location plot demonstrates a random scatter of all residuals indicating that the assumption of linear regression that the residuals posses homoscedasticity (constant variance) is met
  • The Residuals vs Leverage (Cook’s Distance) plot indicates that all values are within the Cook’s Distance therefore ensuring no variables are truly influential / outlier residuals

These findings indicate that the linear regression prediction model (Figure 9) is a good fit for the Overall Flight Delay data. As this model is a good fit and meets all assumptions of linear regression, this model has proven useful for live prediction of overall flight delay & determination of focus in airline budgets and staff to preemptively mitigate overall flight delays.

lm.pca.test.plots <- lm(arr_delay ~ arr_delay_pred, data = lm.pca.test)

par(mfrow = c(2,2),oma = c(4, 0, 0, 0))
plot(lm.pca.test.plots)
mtext("Figure 9:
      Residual plots for linear regression prediction model 2 run on test data", side= 1, line = 1, outer=TRUE)

5 Logistic Regression Model for Live Arrival Delay Prediction

Live determining predicted delay times is imperative for airport planning and resourcing to reduce a downstream cascade of delayed flights throughout the entire flight network. In order to efficiently pivot to ensure a cascade does not occur, it is imperative airlines and airports can accurately and quickly feed data into prediction models to obtain overall delay estimates. As the linear regression model created is computationally heavy, a second logistic regression model was created to predict an airline’s k-Means cluster ID to reduce overall time and computational burden. This will allow for reduced computational input to calculate the overall flight delay utilizing the linear regression equation (Model 2) as outlined above in section 4. Linear Regression Model for Arrival Delay Prediction. The correlation between Overall Flight Delay and k-Means Cluster ID was checked and a strong negative linear relationship was identified, highlighting the importance in rapidly determine this k-Means Cluster ID with minimal computational burden to allow for live Overall Flight Delay prediction.

corr.delay.clust <- data.frame(
  Correlation =  cor(final.delay$arr_delay, final.delay$cluster)
)

kable(corr.delay.clust, caption = "Table 19:
      <br>Correlation between Overall Flight Delay and k-Means Cluster ID") %>% 
  kable_styling()
Table 19:
Correlation between Overall Flight Delay and k-Means Cluster ID
Correlation
-0.6852907

5.1 Regression Model Creation

In order to determine the best logistic regression prediction model for use in k-Means cluster ID prediction to improve live Overall Flight Delay prediction times. The following models were developed and analyzed to determine the best fit model to conduct these tests and determinations:

  • Model 1: Full regression model (Cluster ~ All Variables in Final.Delay data set)
    • The full model was selected to serve as an initial analysis and point of reference for models 2 - 4.
  • Model 2: Step-wise automated selection regression model (Cluster ~ Late Incoming Planes)
    • The step-wise automated selection model was chosen to remove potential human bias and error in creating the best fit prediction model. Both forward and backwards step-wise analysis was completed to identify the optimal model for prediction.
  • Model 3: External Conditions Model (Cluster ~ Late Incoming Planes + Weather + Number of Flights)
    • This model was created to predict based on non-staffing related airport conditions
  • Model 4: Staff Model (Cluster ~ Support Crews Available + Baggage Loading Time)
    • This model was created to predict based on staffing related airport conditions
train.data$cluster <- as.numeric(train.data$cluster)
test.data$cluster <- as.numeric(test.data$cluster)

train.data$cluster2 <- NULL
for(i in 1:length(train.data)){
  if(train.data$cluster[i] == 1){
    n <- 0}
  else {n <- 1}
  
  train.data$cluster2[i] <- n
}

test.data$cluster2 <- NULL
for(i in 1:length(train.data)){
  if(test.data$cluster[i] == 1){
    n <- 0}
  else {n <- 1}
  
  test.data$cluster2[i] <- n
}

final.delay$cluster2 <- NULL
for(i in 1:length(final.delay)){
  if(final.delay$cluster[i] == 1){
    n <- 0}
  else {n <- 1}
  
  final.delay$cluster2[i] <- n
}
fm.train <- train.data[-12]
qm.train <- train.data[-c(8:12)]
  
full.model.logit <- glm(cluster2 ~., data = fm.train, family = "binomial")
quick.model.logit <- glm(cluster2~., data = qm.train, family = "binomial")

logit.step <- stepAIC(full.model.logit, direction = "both", trace = FALSE)
logit.qstep <- stepAIC(quick.model.logit, direction = "both", trace = FALSE)

step.logit <- glm(cluster2 ~ late_incoming, data = train.data, family = "binomial")

ext.logit <- glm(cluster2 ~ weather*late_incoming*num_flights, data = train.data, family = "binomial")

staff.logit <- glm(cluster2 ~ sup_crew*baggage, data = train.data, family = "binomial")

5.2 Model Comparrison

With all four potential regression models created, 5-fold cross validation was conducted and the Akaike Information Criterion (AIC) values for each prediction model were obtained. The data was split 80:20 into train:test data subsets respectively, utilizing the training subsets for the cross validation process. Through comparing the calculated training data AIC values as represented below in Table 20, the best fit prediction model was identified as Model 3 and subsequently selected for further testing utilizing the test data.

aic.comp <- data.frame(
  Model = c("Full Model (Model 1)", "Stepwise Model (Model 2)", "External Cond. Model (Model 3)", "Staff Model (Model 4)"),
  AIC = c(full.model.logit$aic, step.logit$aic, ext.logit$aic, staff.logit$aic)
)

kable(aic.comp, caption = "Table 20:
      <br>Comparrison of calculated AIC values for proposed logistic regression prediction models for cluster ID") %>% 
  kable_styling()
Table 20:
Comparrison of calculated AIC values for proposed logistic regression prediction models for cluster ID
Model AIC
Full Model (Model 1) 108.13419
Stepwise Model (Model 2) 98.77241
External Cond. Model (Model 3) 101.69402
Staff Model (Model 4) 102.39538

Finally the ROC (Receiver Operating Characteristic) curves for each prediction model were compared for a second visual representation of the comparison of true positive and false positive rates as well as the calculated area under the curve (AUC) measures below in Figure 10. As seen with the calculated AUC for Model 3 the External Cond. Model demonstrates excellent prediction performance by the model. The best fit sensitivity and specificity values for use with the model and indicated in Figure 10 with a yellow triangle and in Table 21. These cutoffs are then utilized for weighting of the false positive and true positive rates when utilizing the probabilities as output in the binary logistic regression model to predict the cluster ID without full PCA and k-Means cluster ID creation.

#model predictions 
ROC.step <- roc(step.logit$y, step.logit$fitted.values)

ROC.ext <- roc(ext.logit$y, ext.logit$fitted.values)

ROC.full <- roc(full.model.logit$y, full.model.logit$fitted.values)

ROC.staff <- roc(staff.logit$y, staff.logit$fitted.values)



roc.colors <- c("blue", "purple", "black", "darkgreen")
cp <- coords(ROC.ext, "best", ret = c("threshold", "sensitivity", "specificity"))

par(oma = c(6,2,2,2),  cex.main = 2)
plot.roc(ROC.staff, col = "blue", ylab = "True Postive Rate (Sensitivity))", xlab = "False Positive Rate (1 - Specificity)", lwd = 2)
plot.roc(ROC.step, col = "purple", add = TRUE, lwd = 2)
plot.roc(ROC.full, add = TRUE, lwd = 2)
plot.roc(ROC.ext, col = "darkgreen", add = TRUE, lwd = 2)
legend("bottomright", legend = 
         c(paste(
             "Staff Model                            AUC =", round(ROC.staff$auc,4)), 
           paste(
             "Stepwise Model                   AUC =", round(ROC.step$auc,4)), 
           paste(
              "Full Model                             AUC =", round(ROC.full$auc, 4)), 
           paste(
             "External Cond. Model         AUC =", round(ROC.ext$auc, 4))), col = roc.colors, lwd = 1)
title(main="ROC Comparrison", outer = T)
title(sub = paste("Figure 10:
Comparrison of ROC and AUC values for each logistic regression prediction model
Yellow triangle represents ideal cutoff point of best fit model. 

Threshold =", round(cp$threshold,4), " Sensitivity = ", round(cp$sensitivity,4), " Specificity =", round(cp$specificity, 4)), outer = T, line=4)
points(x = cp$specificity, y = cp$sensitivity, pch = 24, bg = "yellow", cex = 2)

kable(cp, caption = "Table 21:
      <br>Threshold and cutoff true positive and false positive rates", col.names = c("Threshold", "True Positive (Sensitivity)", "False Positive (1-Specificity)")) %>% 
  kable_styling()
Table 21:
Threshold and cutoff true positive and false positive rates
Threshold True Positive (Sensitivity) False Positive (1-Specificity)
0.9964902 0.8350192 0.8571429

6 Conclusions

In this analysis of global flight delay data, two prediction models were created for separate utilization purposes, both tuned to assist airlines and airports in reduction of overall flight delay data. The PCA based linear regression model proves to create a detailed tool for airlines to proactively plan staffing considerations, overall flight scheduling, and departmental budgets by predicting the impact of overall changes in the terms of average Overall Flight Delay times. As this model is of a high computational burden, a second model was created to predict the k-Means cluster ID utilized in the linear regression model for a rapid approach to live flight delay predictions. By first using the low computational burden logistic regression prediction model the predicted k-Means cluster ID can be fed into the linear regression prediction model while simultaneously dropping the PCA process to 1 variable and omitting the k-Means cluster ID creation to live predict flight delays. This will allow airports to predict downstream impacts of delays based on current airport conditions (weather, staff availability, average incoming flight delays, and number of flights at the airport that day). Through stacking these two robust prediction models airlines and airports should be more than equipped to predict delays live/before they happen and preemptively plan staffing and budgeting considerations for high travel days (ex: Christmas Eve) to reduce bottlenecks and downstream delays.

LS0tDQp0aXRsZTogIkhvbGQgVGhhdCBQbGFuZSEgPGltZyBzcmM9XCJodHRwczovL25sZXBlcmEuZ2l0aHViLmlvL3N0YTU1MS9IVzAxL2ltZy9wZW5ndWluX2N1dGUucG5nXCIgc3R5bGU9XCJmbG9hdDogcmlnaHQ7IHdpZHRoOiAxMiVcIi8+Ig0Kc3VidGl0bGU6ICJBbiBhbmFsaXNpcyBvZiBnbG9iYWwgZmxpZ2h0IGRlbGF5IGRhdGEgYW5kIHByZWRpY3Rpb24gbW9kZWwgY3JlYXRpb24gdG8gcmVkdWNlIGRlbGF5cyBhbmQgaW5jcmVhc2UgbGl2ZSBwcmVkaWN0aW9uIGNhcGFiaWxpdGllcyINCmF1dGhvcjoNCi0gbmFtZTogTmF0YWxpZSBMZVBlcmENCiAgYWZmaWxpYXRpb246IFdlc3QgQ2hlc3RlciBVbml2ZXJzaXR5IHwgU1RBNTUyIC0gSFcgMDMNCmRhdGU6ICIwNSBNYXIgMjAyNSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRvY19jb2xsYXBzZTogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZmlnX2FsaWduOiBjZW50ZXINCiAgICBkZl9wcmludDoga2FibGUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9DQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXdlaWdodDpib2xkOw0KICBjb2xvcjogZGFya21hZ2VudGEgOw0KfQ0KaDEuc3VidGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgY29sb3I6IGRhcmttYWdlbnRhIDsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtd2VpZ2h0OmJvbGQ7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsNCiAgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsNCn0NCg0KLmhpZ2hsaWdodG1lIHsgDQogIGJhY2tncm91bmQtY29sb3I6eWVsbG93OyANCn0NCg0KcCB7IA0KICBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyANCn0NCg0KaDUgew0KICBjb2xvcjogbmF2eTsNCn0NCg0KLmlmcmFtZSB7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KYTpsaW5rIHsNCiAgY29sb3I6IGRhcmttYWdlbnRhOw0KfQ0KDQouZmlnbGFiZWwgew0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGNvbG9yOiBzbGF0ZWdyYXk7DQogIGZvbnQtc3R5bGU6IGl0YWxpYzsNCiAgZm9udC1zaXplOiAxODsNCn0NCg0KLnRkMSB7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQp0aCwgdGQgew0KICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2RkZDsNCiAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KdHI6aG92ZXIge2JhY2tncm91bmQtY29sb3I6IGNvcmFsO30NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmlmICghcmVxdWlyZSgiZHBseXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImRwbHlyIikNCn0NCmlmICghcmVxdWlyZSgibWlzdHkiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1pc3R5IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1pc3R5IikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbHlyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbHlyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBseXIiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInN0cmluZ3IiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgic3RyaW5nciIpDQp9DQoNCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGxvdGx5IikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwYW5kb2MiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBhbmRvYyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwYW5kb2MiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdyaWRFeHRyYSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZEV4dHJhIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdyaWRFeHRyYSIpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RyaXgiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBsb3RyaXgiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGxvdHJpeCIpDQp9DQoNCmlmICghcmVxdWlyZSgiZ3JpZCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZCIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJncmlkIikNCn0NCmlmICghcmVxdWlyZSgicmFzdGVyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJyYXN0ZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicmFzdGVyIikNCn0NCmlmICghcmVxdWlyZSgiZGJzY2FuIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkYnNjYW4iKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZGJzY2FuIikNCn0NCmlmICghcmVxdWlyZSgicFJPQyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwUk9DIikNCn0NCmlmICghcmVxdWlyZSgiZ2dyaWRnZXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdncmlkZ2VzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdncmlkZ2VzIikNCn0NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImtuaXRyIikNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiR0dhbGx5IikNCn0NCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ2xwb3QyIikNCn0NCmlmICghcmVxdWlyZSgiY2x1c3RlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiY2x1c3RlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJjbHVzdGVyIikNCn0NCmlmICghcmVxdWlyZSgia2FibGVFeHRyYSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygia2FibGVFeHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJrYWJsZUV4dHJhIikNCn0NCmlmICghcmVxdWlyZSgiZm9yY2F0cyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZm9yY2F0cyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJmb3JjYXRzIikNCn0NCmlmICghcmVxdWlyZSgicnBhcnQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJwYXJ0IikNCn0NCmlmICghcmVxdWlyZSgicnBhcnQucGxvdCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJycGFydC5wbG90IikNCn0NCmlmICghcmVxdWlyZSgibWV0YW4iKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1ldGFuIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1ldGFuIikNCn0NCiBpZiAoIXJlcXVpcmUoImZhY3RvZXh0cmEiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJmYWN0b2V4dHJhIikNCiB9DQogaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoInBhbmRlciIpDQogfQ0KDQogaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgiZ2dwbG90MiIpDQogfQ0KDQogaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgiTUFTUyIpDQogfQ0KIGlmICghcmVxdWlyZSgic2ltcHV0YXRpb24iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygic2ltcHV0YXRpb24iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgic2ltcHV0YXRpb24iKQ0KIH0NCiBpZiAoIXJlcXVpcmUoIm1pY2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibWljZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJtaWNlIikNCiB9DQogaWYgKCFyZXF1aXJlKCJnZ21pY2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dtaWNlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoImdnbWljZSIpDQogfQ0KaWYgKCFyZXF1aXJlKCJBbWVsaWEiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJBbWVsaWEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KCJBbWVsaWEiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KCJjYXJldCIpDQp9DQoNCmlmICghcmVxdWlyZSgiZ2dwdWJyIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwdWJyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgbGlicmFyeSgiZ2dwdWJyIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJtdm5tbGUiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJtdm5tbGUiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KCJtdm5tbGUiKQ0KfQ0KDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicpDQoNCm9wdGlvbnMoRFQub3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUsIHNjcm9sbFggPSBUUlVFKSkNCmBgYA0KDQoNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KQWlyIHRyYXZlbCBoYXMgYmVjb21lIGEgcGl2b3RhbCBmZWF0dXJlIG9mIHRoZSBldmVyIGdsb2JhbGl6ZWQgd29ybGQsIGRyaXZpbmcgZ2xvYmFsIHRyYWRlLCBjb25uZWN0aW5nIGluZGl2aWR1YWxzIGFuZCBjb3VudHJpZXMgYWxpa2UsIGFuZCBkcml2aW5nIGdsb2JhbCBlY29ub21pYyBncm93dGguICBBY2NvcmRpbmcgdG8gdGhlIDxhIGhyZWY9Imh0dHBzOi8vYXRhZy5vcmcvZmFjdHMtZmlndXJlcyI+QWlyIFRyYW5zcG9ydCBBY3Rpb24gR3JvdXA8L2E+IGluIDIwMjMsIGEgdG90YWwgb2YgMzUuMyBtaWxsaW9uIGZsaWdodHMgY2FycmllZCBhIHRvdGFsIG9mIDxiPjQuNCBiaWxsaW9uIHBhc3NlbmdlcnM8L2I+IGFuZCA8Yj42MS40IG1pbGxpb24gdG9ubmVzIG9mIGNhcmdvPC9iPiBiZXR3ZWVuIDQsMDcyIGdsb2JhbCBhaXJwb3J0cy4gIFRoaXMgcmVzdWx0ZWQgaW4gYSAkNC4xIHRyaWxsaW9uIHRvdGFsIGdsb2JhbCBlY29ub21pYyBjb250cmlidXRpb24gaW4gMjAyMyBhbG9uZS4gV2hpbGUgb25seSAxJSBvZiBhbGwgZ2xvYmFsIHRyYWRlIGlzIGNvbmR1Y3RlZCB0aHJvdWdoIGFpciB0cmF2ZWwsIGFpciBzaGlwcGluZyBhY2NvdW50cyBmb3IgMzMlIG9mIGFsbCB0cmFkZSB2YWx1ZSwgaW5kaWNhdGluZyB0aGF0IHRoZSBtb3N0IGV4cGVuc2l2ZSBnb29kcyBhcmUgc2hpcHBlZCB2aWEgYWlyIHRyYXZlbC4gSW4gMjAyMyBhbG9uZSwgYSB0b3RhbCBvZiAxLDEzOCBhaXJsaW5lcyB3ZXJlIG9wZXJhdGluZyBhIHRvdGFsIG9mIDI5LDAzOSBwbGFuZXMgYWNyb3NzIDY3LDMwMCBnbG9iYWwgcm91dGVzLCBjb25uZWN0aW5nIDIxLDAwMCB1bmlxdWUgcGFpcnMgb2YgY2l0aWVzLiAgVGhlIHZhc3Qgc2l6ZSBvZiB0aGlzIHRyYW5zaXQgbmV0d29yayBwcmVzZW50cyB1bmlxdWUgY2hhbGxlbmdlcyB3aGVuIHdvcmtpbmcgdG8gYW5hbHl6ZSBmbGlnaHQgZGF0YSB3aXRoIHRoZSBnb2FsIG9mIHJlZHVjaW5nIGZsaWdodCBkZWxheXMuICANCg0KRHVlIHRvIHRoZSBtYXNzaXZlIGltcGFjdCBvZiBhaXIgdHJhdmVsIG9uIG91ciBnbG9iYWwgY29tbXVuaXRpZXMgYW5kIEdEUHMsIHRoZXJlIGhhcyBiZWVuIHNpZ25pZmljYW50IGVmZm9ydHMgaW1wbGVtZW50ZWQgdG8gc3RyZWFtbGluZSBhaXIgdHJhdmVsIGFuZCByZWR1Y2UgZmlnaHQgZGVsYXlzIHRvIGVuc3VyZSBzbW9vdGggcHJlZGljdGFibGUgdHJhdmVsLiAgTWl0aWdhdGlvbiBhbmQgcmVkdWN0aW9uIG9mIG92ZXJhbGwgYWlyIHRyYXZlbCBkZWxheXMgd2lsbCBhbGxvdyBmb3IgaW5jcmVhc2VkIGFpciB0cmF2ZWwgZHJpdmVuIHRyYWRlIG91dHB1dCBhbmQgb3ZlcmFsbCBib29zdHMgdG8gdGhlIGdsb2JhbCBlY29ub215LiAgSW4gb3JkZXIgdG8gcHJvcGVybHkgbWl0aWdhdGUgYWlyIHRyYXZlbCBkZWxheXMsIGNvbmZvdW5kaW5nIGZhY3RvcnMgdGhhdCBtYXkgaW1wYWN0IGZsaWdodCBkZWxheXMgd2VyZSBjYXB0dXJlZCBieSBEZWVwdGkgR3VwdGEgaW4gQXBwbGllZCBBbmFseXRpY3MgdGhyb3VnaCBDYXNlIFN0dWRpZXMgVXNpbmcgU0FTIGFuZCBSIGZvciBhbmFseXNpcyBhbmQgbW9kZWxpbmcuIA0KDQoNCiMjIEFuYWx5c2lzIEdvYWxzDQoNCkluIG9yZGVyIHRvIGFpZCBpbiByZWR1Y2luZyBvdmVyYWxsIGZsaWdodCBkZWxheXMsIHRoZSBmYWN0b3JzIHdpdGggdGhlIGdyZWF0ZXN0IGltcGFjdCBvbiBvdmVyYWxsIGZsaWdodCBkZWxheSB0aW1lcyBtdXN0IGJlIGlkZW50aWZpZWQuICBPbmNlIGlkZW50aWZpZWQsIHRoaXMgaW5mb3JtYXRpb24gY2FuIHRoZW4gYmUgbGV2ZXJhZ2VkIGJ5IGFpcnBvcnRzIGFuZCBhaXJsaW5lcyB0byBpbXByb3ZlIHN0YWZmaW5nLCBzdHJlYW1saW5lIHByZS1mbGlnaHQgcHJvY2Vzc2VzLCBhbmQgdWx0aW1hdGVseSBtaXRpZ2F0ZSBkZWxheXMgZHVlIHRvIGZhY3RvcnMgd2l0aGluIHRoZSBhaXJsaW5lcycgb3IgYWlycG9ydHMnIGNvbnRyb2wuICBJbiBhZGRpdGlvbiB0byBwcmV2ZW50YXRpdmUgbWVhc3VyZXMgdG8gbWl0aWdhdGUgZGVsYXlzLCB1bmRlcnN0YW5kaW5nIHRoZSBncmVhdGVzdCBpbXBhY3RzIG9uIGZsaWdodCBkZWxheXMgZ2xvYmFsbHkgd2lsbCBhbGxvdyBmb3IgY3JlYXRpb24gb2YgcHJlZGljdGlvbiBtb2RlbHMgdG8gbGl2ZSBwcmVkaWN0IHVwY29taW5nIGZsaWdodCBkZWxheXMgZm9yIHJlYWwgdGltZSBtaXRpZ2F0aW9uIGJlZm9yZSB0aGUgZGVsYXlzIGluIHF1ZXN0aW9uIGV2ZW4gaGFwcGVuLiBUaGUgc3Vic2VxdWVudCBhbmFseXNpcyBhbmQgcHJlZGljdGlvbiBtb2RlbCBjcmVhdGlvbiBvZiB0aGUgZ2xvYmFsIGZsaWdodCBkZWxheSB3aWxsIHNlcnZlIHRvIGFkZHJlc3MgdHdvIHBvaW50cyBvZiBjb25jZXJuOg0KDQogIC0gSWRlbnRpZnlpbmcgdGhlIHZhcmlhYmxlcyB3aXRoIGdyZWF0ZXN0IGltcGFjdCBvbiBvdmVyYWxsIGZsaWdodCBkZWxheSBmb3Igc3Vic2VxdWVudCBtaXRpZ2F0aW9uIGFuZCBwcmV2ZW50YXRpdmUgZWZmb3J0cw0KICAtIExpdmUgZmxpZ2h0IGRlbGF5IHByZWRpY3Rpb24gYmFzZWQgb24gYXZhaWxhYmxlIGZsaWdodCBkYXRhIHByaW9yIHRvIGxhbmRpbmcgKHByZWRpY3RpbmcgdG90YWwgZGVsYXkgdGltZSBiZWZvcmUgZmxpZ2h0cyBkZXBhcnQpDQoNCiMjIE92ZXJhbGwgQXBwcm9hY2ggDQoNClByaW9yIHRvIGFueSBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uLCB0aGUgYWlybGluZSBkZWxheSBkYXRhIHdhcyBjbGVhbmVkIGFuZCBtb2RpZmllZCBpbiBhIHByb2Nlc3MgY2FsbGVkIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMuICBGaXJzdCwgdG8gYWxsb3cgZm9yIG11bHRpcGxlIGFuYWx5c2lzIGFuZCBwcmVkaWN0aW9uIGFwcHJvYWNoZXMsIHR3byBudW1lcmljIHZhcmlhYmxlcyB3ZXJlIGJpbm5lZCB0byBjcmVhdGUgY2F0ZWdvcmljYWwgdmFsdWVzIChiYWdnYWdlIGxvYWRpbmcgdGltZSAmIGNsZWFuaW5nIHRpbWUpLiAgQWZ0ZXIgYmlubmluZyB0byBjcmVhdGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB0aGUgZGF0YSB3YXMgZXhhbWluZWQgdG8gaWRlbnRpZnkgYW5kIHN1YnNlcXVlbnRseSByZW1vdmUgaW1wb3NzaWJsZSBvdXRsaWVyIHZhbHVlcy4gDQoNCkl0IGlzIG5lYXIgaW1wb3NzaWJsZSB0byBlbnN1cmUgc3RhbmRhcmRpemVkIGRhdGEgY29sbGVjdGlvbiBmb3JtYXRzIGdsb2JhbGx5LCByZXN1bHRpbmcgaW4gaW5jb21wbGV0ZSBkYXRhIHNldHMgd2l0aCBtaXNzaW5nIHZhbHVlcy4gIEluIG9yZGVyIHRvIHByb3Blcmx5IGFuYWx5emUgZmxpZ2h0IGRhdGEgdG8gc3VjY2Vzc2Z1bGx5IHJlZHVjZSBmbGlnaHQgZGVsYXkgdGltZXMsIGNvbnNpZGVyYXRpb25zIG11c3QgYmUgdGFrZW4gdG8gcHJlZGljdCBzYWlkIG1pc3NpbmcgdmFsdWVzLCBlc3NlbnRpYWxseSBmaWxsaW5nIGluIHRoZSBnYXBzLiBJbiB0aGUgd29ybGQgb2YgZGF0YSBzY2llbmNlLCBmaWxsaW5nIGluIHRoZXNlIGRhdGEgZ2FwcyBpcyBrbm93biBhcyBpbXB1dGF0aW9uLiBGb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgYW5hbHlzaXMgYW5kIHByZWRpY3Rpb24gbW9kZWwgYnVpbGRzLCBhIE11bHRpcGxlIEltcHV0YXRpb24gYnkgQ2hhaW5lZCBFcXVhdGlvbnMgKE1JQ0UpIGFwcHJvYWNoIHdhcyBpbXBsZW1lbnRlZCB0byBpbXB1dGUgYWxsIG1pc3Npbmcgb2JzZXJ2YXRpb25zLiAgDQoNCk9uY2UgaW1wdXRlZCwgdGhlIGRhdGEgd2FzIHRoZW4gYW5hbHlzZWQgZm9yIGFueSBwb3RlbnRpYWwgc2tldyBhbmQgc3Vic2VxdWVudGx5IG5vcm1hbGl6ZWQgdXRpbGl6aW5nIGEgbWluLW1heCBlcXVhdGlvbi4gIFRoaXMgbm9ybWFsaXphdGlvbiB3YXMgaW1wbGVtZW50ZWQgdG8gZnVydGhlciBwcmVwYXJlIHRoZSBkYXRhIGZvciB1c2UgaW4gcHJlZGljdGl2ZSBtb2RlbGluZyBieSByZWR1Y2luZyBhbnkgc2tldyBhbmQgZW5zdXJpbmcgbm9ybWFsIGRpc3RyaWJ1dGlvbnMgdG8gZW5zdXJlIHRoZSBhc3N1bXB0aW9ucyBvZiByZWdyZXNzaW9uIHdlcmUgbWV0LiAgVGhlIG5vcm1hbGl6ZWQgZGF0YSB3YXMgdGhlbiBhbmFseXplZCBmb3IgcmVkdW5kYW5jaWVzIGFuZCB0aGUgaGlnaCBpbXBhY3QgdmFyaWFibGVzIHdlcmUgaWRlbnRpZmllZCBmb3IgcmV0ZW50aW9uIHRocm91Z2ggUkZFIGZlYXR1cmUgc2VsZWN0aW9uLiAgT25jZSBmdWxseSBwcnVuZWQsIHRoZSBQQ0EgY29tcG9uZW50IGRhdGEgd2FzIHRoZW4gc3ViamVjdGVkIHRvIGstTmVhcmVzdCBOZWlnaGJvciBjbHVzdGVyIGJhc2VkIGZlYXR1cmUgZXh0cmFjdGlvbi4gIFRoaXMgay1NZWFucyBjbHVzdGVyIElEIGNyZWF0aW9uIGFsbG93cyBmb3IgY29uc29saWRhdGlvbiBvZiB0aGUgc2VsZWN0ZWQgUENBIGNvbXBvbmVudHMgaW50byBhIHNpbmdsZSBiaW5hcnkgdmFyaWFibGUgcmVkdWNpbmcgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgd2l0aG91dCBsb3NzIG9mIGRhdGEuIA0KDQpUaGUgbm9ybWFsaXplZCBkYXRhIHdpdGggYWRkZWQgUENBIGNvbXBvbmVudHMgYW5kIGstTWVhbnMgY2x1c3RlciBJRHMgYXNzaWduZWQgd2FzIHRoZW4gdXRpbGl6ZWQgdG8gZGV2ZWxvcCBmb3VyIGxpbmVhciByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWxzIGZvciBldmFsdWF0aW9uIGFuZCB1bHRpbWF0ZSBzZWxlY3Rpb24uICBUaGVzZSBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVscyB3ZXJlIGFuYWx5emVkIHVzaW5nIDgwOjIwIGNyb3NzIHZhbGlkYXRpb24gdG8gZW5zdXJlIHRoZSBiZXN0IGZpdCBtb2RlbCB3YXMgaWRlbnRpZmllZCB0aHJvdWdoIG1lYW4gTWVhbiBTcXVhcmUgRXJyb3IgKE1TRSkgY29tcGFyaXNvbi4gVGhlIHVsdGltYXRlIHNlbGVjdGlvbiBvZiBhIGJlc3QgZml0IGxpbmVhciByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwgc2VydmVzIGFzIGFuIGFuYWx5dGljIHRvb2wgdG8gYWxsb3cgYWlybGluZXMgdG8gYmVzdCBkZXRlcm1pbmUgd2hlcmUgdXBzdHJlYW0gZGVsYXkgcmVkdWN0aW9uIGFwcHJvYWNoZXMgc2hvdWxkIGJlIGltcGxlbWVudGVkIChpLmU6IGVuc3VyaW5nIGFkZXF1YXRlIHN1cHBvcnQgY3Jld3MgYXJlIGF2YWlsYWJsZSwgaWRlbnRpZnlpbmcgaWRlYWwgYmFnZ2FnZSBsb2FkaW5nIC8gcGxhbmUgY2xlYW5pbmcgLyBwbGFuZSBmdWVsaW5nIHRpbWUgd2luZG93cywgZXRjLikuICANCg0KDQpBIHNlY29uZCBwcmVkaWN0aW9uIG1vZGVsIHdhcyBkZXZlbG9wZWQgb21pdHRpbmcgYWxsIFBDQSBjb21wb25lbnRzIGFuZCBrLU1lYW5zIGNsdXN0ZXIgSURzIHRvIGRldmVsb3AgYSBsb3cgY29tcHV0YXRpb25hbCBidXJkZW4gcmFwaWQgcHJlZGljdGlvbiBtb2RlbCBmb3IgdXNlIGluIGxpdmUgcHJlZGljdGlvbiBvZiBkZWxheXMgYmFzZWQgb24gY3VycmVudCBhaXJwb3J0IGNvbmRpdGlvbnMuICBNdWx0aXBsZSBtb2RlbHMgd2VyZSBkZXZlbG9wZWQgdXNpbmcgdGhlIHByZXZpb3VzbHkgc3BsaXQgODA6MjAgdHJhaW46dGVzdCBkYXRhIGFuZCBjb21wYXJlZCBmb3IgZWZmaWNhY3kgYW5kIHJlbGlhYmlsaXR5IHV0aWxpemluZyBBa2Fpa2UgSW5mb3JtYXRpb24gQ3JpdGVyaWEgKEFJQykgdmFsdWVzLCAgUk9DIFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpYyAoUk9DKSBjdXJ2ZSBjb21wYXJpc29ucywgYW5kIGNhbGN1bGF0ZWQgQXJlYSBVbmRlciB0aGUgQ3VydmUgKEFVQykgdmFsdWVzIGFzIGNhbGN1bGF0ZWQgZnJvbSB0aGUgUk9DIGN1cnZlcy4gDQoNCmBgYHtyfQ0KZGVsYXkgPC0gcmVhZC5jc3YoImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUyL0hXMDEvZGF0YS9GbGlnaHRfZGVsYXktZGF0YS5jc3YiKQ0KYGBgDQoNCg0KIyBEYXRhIENvbGxlY3Rpb24gYW5kIFByZXBhcmF0aW9uDQoNCiMjIERhdGEgRGV0YWlscw0KDQpGbGlnaHQgZGVsYXkgZGF0YSBhcyBjb2xsZWN0ZWQgZnJvbSA8aT5BcHBsaWVkIEFuYWx5dGljcyB0aHJvdWdoIENhc2UgU3R1ZGllcyBVc2luZyBTQVMgYW5kIFIsIERlZXB0aSBHdXB0YSBieSBBUHJlc3MsIElTQk4gLSA5NzgtMS00ODQyLTM1MjUtNjwvaT4gd2FzIGRvd25sb2FkZWQgZnJvbSA8YSByZWY9Imh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL2RhdGFzZXRzLyI+aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vZGF0YXNldHMvPC9hPi4gIA0KDQpBIHRvdGFsIG9mIDxiPjM1OTMgZmxpZ2h0czwvYj4gd2VyZSBhbmFseXplZCBmb3IgPGI+MTEgdmFyaWFibGVzIG9mIGludGVyZXN0PC9iPiAoMSBjYXRlZ29yaWNhbCAmIDExIG51bWVyaWMpLiAgVGhlIGZsaWdodCB2YXJpYWJsZXMgYXMgcmVjb3JkZWQgaW5jbHVkZToNCg0KYGBge3J9DQp2YXIuZGV0YWlscyA8LSBkYXRhLmZyYW1lKA0KbmFtZSA9IGMoIkNhcnJpZXIiLCAiQWlycG9ydCBEaXN0YW5jZSIsICJOdW1iZXIgb2YgRmxpZ2h0cyIsICJXZWF0aGVyIiwgIlN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlIiwgIkJhZ2dhZ2UgTG9hZGluZyBUaW1lIiwgIkxhdGUgSW5jb21pbmcgUGxhbmUgQXJyaXZhbCIsICJDbGVhbmluZyBUaW1lIiwgIkZ1ZWxpbmcgVGltZSIsICJTZWN1cml0eSBUaW1lIiwgIk92ZXJhbGwgRmxpZ2h0IERlbGF5IiksDQp0eXBlID0gYygiQ2F0ZWdvcmljYWwiLCAiTnVtZXJpYyIsICJOdW1lcmljIiwgIk51bWVyaWMiLCAiTnVtZXJpYyIsICJOdW1lcmljIFtCaW5uZWRdIiwgIk51bWVyaWMiLCAiTnVtZXJpYyBbQmlubmVkXSIsICJOdW1lcmljIiwgIk51bWVyaWMiLCAiTnVtZXJpYyIpLA0KZGV0YWlscyA9IGMoIkFpcmxpbmUgKENhcnJpZXIpIiwgIkRpc3RhbmNlIChtaWxlcykgYmV0d2VlbiBkZXBhcnR1cmUgYW5kIGFycml2YWwgYWlycG9ydCIsICJUb3RhbCBudW1iZXIgb2YgZmxpZ2h0cyBhdCBhcnJpdmFsIGFpcnBvcnQiLCAiQSByYW5raW5nIG9mIGRlbGF5cyBkdWUgdG8gd2VhdGhlciBjb25kaXRpb24gKDA6IE1pbGQgdG8gMTA6IEV4dHJlbWUpIiwgIlRvdGFsIG51bWJlciBvZiBzdXBwb3J0IGNyZXcgYXZhaWxhYmxlIiwgIlRvdGFsIHRpbWUgZm9yIGJhZ2dhZ2UgbG9hZGluZyAobWludXRlcykiLCAiRGVsYXkgdGltZSBmb3IgcGxhbmUgYXJyaXZhbCBwcmlvciB0byBmbGlnaHQgKG1pbnV0ZXMpIiwgIlRpbWUgcmVxdWlyZWQgdG8gY2xlYW4gcGxhbmUgYWZ0ZXIgYXJyaXZhbCBwcmlvciB0byBwYXNzZW5nZXIgbG9hZGluZyAobWludXRlcykiLCAiVGltZSByZXF1aXJlZCB0byBmdWVsIGFpcmNyYWZ0IChtaW51dGVzKSIsICJUaW1lIHJlcXVpcmVkIGZvciBzZWN1cml0eSBjaGVja3MgKG1pbnV0ZXMpIiwgIlRvdGFsIGZsaWdodCBkZWxheSB0aW1lIChtaW51dGVzKSIpDQopDQpgYGANCg0KYGBge3J9DQprYWJsZSh2YXIuZGV0YWlscywgY29sLm5hbWVzID0gYygiVmFyaWFibGUgTmFtZSIsICJWYXJpYWJsZSBUeXBlIiwgIkRldGFpbHMiKSwgY2FwdGlvbiA9ICJUYWJsZSAxOg0KICAgICAgPGJyPkdsb2JhbCBmbGlnaHQgZGF0YSBkZXRhaWxzDQogICAgICA8YnI+bm90ZTogbnVtZXJpYyB2YXJpYWJsZXMgKFdlYXRoZXIgJiBDbGVhbmluZykgd2VyZSBiaW5uZWQgdG8gYWxsb3cgZm9yIGNhdGVnb3JpY2FsIHN0eWxlZCBoYW5kbGluZyIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIyBEYXRhIFByZXBhcmF0aW9uDQoNCiMjIyBPdXRsaWVyIFJlbW92YWwgDQoNCkEgY3Vyc29yeSBhbmFseXNpcyBvZiBhbGwgZGF0YSBwb2ludHMgaWRlbnRpZmllZCBmb3VyICg0KSBvYnNlcnZhdGlvbnMgd2l0aCBpbXBvc3NpYmxlIG91dGxpZXJzIGluIHRoZSBjbGVhbmluZyB0aW1lIHZhcmlhYmxlLiBUaGVzZSB2YWx1ZXMgYXJlIGtub3duIHRvIGJlIGltcG9zc2libGUgYXMgYSBuZWdhdGl2ZSBkdXJhdGlvbiBmb3IgY2xlYW5pbmcgdmFsdWUgaXMgaW1wb3NzaWJsZS4gUmF0aGVyIHRoYW4gZHJvcHBpbmcgYWxsIGRhdGEgYXNzb2NpYXRlZCB3aXRoIHRoZXNlIGVycm9uZW91cyBvYnNlcnZhdGlvbnMsIHRoZXNlIG5lZ2F0aXZlIHZhbHVlcyB3ZXJlIHJlcGxhY2VkIHdpdGggYE5BYCB0byBhbGxvdyBmb3IgdGhlIHJlbWFpbmluZyBvYnNlcnZhdGlvbnMnIGRhdGEgdG8gYmUgdXRpbGl6ZWQuIFRoZXNlIGltcG9zc2libGUgdmFsdWVzIHRoYXQgd2VyZSByZXBsYWNlZCB3aXRoIE5BIHZhbHVlcyB3aWxsIHN1YnNlcXVlbnRseSBiZSBpbXB1dGVkIGZ1cnRoZXIgYWxvbmcgaW4gdGhlIGFuYWx5c2lzIHByb2Nlc3MuDQoNCmBgYHtyfQ0KaW1wb3NzaWJsZSA8LSBkZWxheSAlPiUgDQogIGZpbHRlcihDbGVhbmluZ19vIDwgMCkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGMoQ2FycmllciwgQ2xlYW5pbmdfbywgQXJyX0RlbGF5KSkNCg0KZGVsYXkuY2xlYW4gPC0gZGVsYXkgJT4lIA0KICBtdXRhdGUoQ2xlYW5pbmdfbyA9IHJlcGxhY2UoQ2xlYW5pbmdfbywgQ2xlYW5pbmdfbyA8IDAsICJOQSIpKSAjcmVtb3ZlZCBvdXRsaWVyIHZhbHVlcyB3aXRob3V0IHJlbW92aW5nIGZ1bGwgb2JzZXJ2YXRpb25zDQoNCmRlbGF5LmNsZWFuJENsZWFuaW5nX28gPC0gYXMubnVtZXJpYyhkZWxheS5jbGVhbiRDbGVhbmluZ19vKQ0KYGBgDQoNCmBgYHtyfQ0Ka2FibGUoaW1wb3NzaWJsZSwgY29sLm5hbWVzPSBjKHZhci5kZXRhaWxzWzEsMV0sIHZhci5kZXRhaWxzWzgsMV0sIHZhci5kZXRhaWxzWzExLDFdKSAsIGNhcHRpb24gPSAiVGFibGUgMjoNCiAgICAgIDxicj5TdW1tYXJ5IG9mIGltcG9zc2libGUgY2xlYW5pbmcgdGltZXMNCiAgICAgIDxicj5UaGVzZSB0aW1lcyBhcmUgY29uc2lkZXJlZCBpbXBvc3NpYmxlIGFzIG5lZ2F0aXZlIHRpbWUgdmFsdWVzIGNhbm5vdCBleGlzdCIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCnNpbmdsZXZhciA8LSBzdW1tYXJ5KGRlbGF5LmNsZWFuJENsZWFuaW5nX28pDQpzaW5nbGV2YXIgPC0gYXMuZGF0YS5mcmFtZS5Bc0lzKHNpbmdsZXZhcikNCg0Ka2FibGUoc2luZ2xldmFyLCBjb2wubmFtZXMgPSB2YXIuZGV0YWlsc1s4LDFdLCBjYXB0aW9uID0gIlRhYmxlIDM6DQogICAgICA8YnI+U3VtbWFyeSBzdGF0aXN0aWNzIG9mIGNsZWFuaW5nIHRpbWUgb2JzZXJ2YXRpb25zIGFmdGVyIGltcG9zc2libGUgdmFsdWVzIHJlcGxhY2VkIHdpdGggTkEiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCg0KIyMjIE9ic2VydmF0aW9uIEJpbm5pbmcNCg0KQXMgb3V0bGluZWQgaW4gdGhlIHRhYmxlIGFib3ZlIGluIHNlY3Rpb24gPGI+MS4yIE92ZXJhbGwgQXBwcm9hY2g8L2I+IGFuZCA8Yj4yLjEgRGF0YSBEZXRhaWxzPC9iPiwgdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMgd2VyZSBiaW5uZWQgdG8gY3JlYXRlIHBzZXVkbyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEJ5IGJpbm5pbmcgdGhlc2UgbnVtZXJpYyB2YXJpYWJsZXMgaW50byBwc2V1ZG8gY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBhZGRpdGlvbmFsIGNhdGVnb3JpY2FsIGFuYWx5c2lzIG1heSBiZSBjb25kdWN0ZWQgYW5kIGFkZGl0aW9uYWwgcHJlZGljdGlvbiBtb2RlbHMgbWF5IGJlIHV0aWxpemVkIGZvciBkZXRlcm1pbmluZyBhbmQgcHJlZGljdGluZyBvdmVyYWxsIGZsaWdodCBkZWxheXMuIA0KDQpUaGUgYmFnZ2FnZSBsb2FkaW5nIHRpbWUgdmFyaWFibGUgd2FzIGJpbm5lZCBpbiB0byB0aHJlZSAoMykgZHVyYXRpb24gZ3JvdXBzOiANCg0KICAtIEZhc3QgKDE0IC0gMTUgbWludXRlcykNCiAgLSBNb2RlcmF0ZSAoMTYgLSAxNyBtaW51dGVzKQ0KICAtIFNsb3dlc3QgKDE4IC0gMTkgbWludXRlcykNCiAgDQpBZGRpdGlvbmFsbHksIHRoZSBjbGVhbmluZyB0aW1lIHZhcmlhYmxlIHdhcyBiaW5uZWQgaW4gdG8gZm91ciAoNCkgZHVyYXRpb24gZ3JvdXBzOiANCg0KICAtIEltbWVkaWF0ZSAoMCAtIDUgbWludXRlcykNCiAgLSBRdWljayAoNiAtIDExIG1pbnV0ZXMpDQogIC0gTW9kZXJhdGUgKDEyIC0gMTcgbWludXRlcykNCiAgLSBTbG93ICgxOCAtIDIzIG1pbnV0ZXMpDQogIA0KICANCg0KYGBge3J9DQpiaW5fY2xlYW4gPC0gYygwLCA2LCAxMiwgMTgsIDIzKQ0KbGFiZWxzX2NsZWFuIDwtIGMoIkltbWVkaWF0ZSIsICJRdWljayIsICJNb2RlcmF0ZSIsICJTbG93IikNCmJpbl9iYWdnYWdlIDwtIGMoMTQsIDE2LCAxOCwgMTkpDQpsYWJlbHNfYmFnZ2FnZSA8LSBjKCJGYXN0IiwgIk1vZGVyYXRlIiwgIlNsb3ciKQ0KDQoNCmRlbGF5LmNsZWFuIDwtIGRlbGF5LmNsZWFuICU+JSANCiAgbXV0YXRlKENsZWFuaW5nX28gPSBjdXQoQ2xlYW5pbmdfbywgYnJlYWtzID0gYmluX2NsZWFuLCBsYWJlbHMgPSBsYWJlbHNfY2xlYW4sIHJpZ2h0ID0gVFJVRSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkNCg0KZGVsYXkuY2xlYW4gPC0gZGVsYXkuY2xlYW4gJT4lIA0KICBtdXRhdGUoQmFnZ2FnZV9sb2FkaW5nX3RpbWUgPSBjdXQoQmFnZ2FnZV9sb2FkaW5nX3RpbWUsIGJyZWFrcyA9IGJpbl9iYWdnYWdlLCBsYWJlbHMgPSBsYWJlbHNfYmFnZ2FnZSwgcmlnaHQgPSBUUlVFLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKQ0KDQprYWJsZShzdW1tYXJ5KGRlbGF5LmNsZWFuWywgYyg2LDgpXSksIGNvbC5uYW1lcyA9IGModmFyLmRldGFpbHNbNiwxXSwgdmFyLmRldGFpbHNbOCwxXSksIGNhcHRpb24gPSAiVGFibGUgNDoNCiAgICAgPGJyPlN1bW1hcnkgb2YgYmlubmVkIGdsb2JhbCBmbGlnaHQgZGF0YSB2YXJpYWJsZXMiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCiMjIyBGb3JjZWQgTWlzc2luZyBWYWx1ZXMNCg0KV2hpbGUgdGhlIGdsb2JhbCBmbGlnaHQgZGVsYXkgZGF0YSBzZXQgYXMgcHJlcGFyZWQgYnkgR3VwdGkgZG9lcyBub3QgY29udGFpbiBtaXNzaW5nIHZhbHVlcywgcmVhbCB3b3JsZCBjb2xsZWN0aW9uIG9mIGdsb2JhbCBmbGlnaHQgZGF0YSB3aWxsIGluZXZpdGFibHkgcmVzdWx0IGluIG1pc3NpbmcgdmFsdWVzIGFuZCBwb29yIHN0YW5kYXJkaXphdGlvbiBhY3Jvc3MgY291bnRyaWVzIGFuZCByZWdpb25zLiAgSW4gb3JkZXIgdG8gZW5zdXJlIHRoZSBkYXRhIGFuYWx5c2lzIGFuZCBwcmVkaWN0aW9uIG1vZGVscyBhcmUgcHJlcGFyZWQgdG8gaGFuZGxlIHRoZXNlIHJlYWwgd29ybGQgZ2FwcyBpbiBkYXRhLCBtaXNzaW5nIHZhbHVlcyB3ZXJlIGZvcmNlZCBmb3IgdGhyZWUgdmFyaWFibGVzLiAgVGhlIHdlYXRoZXIsIHN1cHBvcnQgY3JldywgYW5kIGxhdGUgaW5pdGlhbCBwbGFuZSBhcnJpdmFsIHRpbWUgdmFyaWFibGVzIHdlcmUgZm9yY2VkIHRvIGhhdmUgbWlzc2luZyBvYnNlcnZhdGlvbnMgYXQgcmFuZG9tIHVzaW5nIHRoZSBgc2FtcGxlKClgIHByb2NlZHVyZS4gYFRhYmxlIDVgIGJlbG93IG91dGxpbmVzIHRoZSB0b3RhbCBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgdGhhdCB3ZXJlIGZvcmNlZCBpbnRvIHRoZSBkYXRhIHNldCBhcyBhIHRyYWluaW5nIG1ldGhvZCB0byBwcmVwYXJlIGZvciB0aGUgcmVhbCB3b3JsZCBkYXRhIGFtYmlndWl0eS4NCg0KYGBge3J9DQp3ZWF0aGVyLm1pc3NpbmcgPC0gc2FtcGxlKDE6MzU5MywgNSwgcmVwbGFjZSA9IEZBTFNFKQ0KbGF0ZS5taXNzaW5nIDwtIHNhbXBsZSgxOjM1OTMsIDYsIHJlcGxhY2UgPSBGQUxTRSkNCnN1cHBvcnQubWlzc2luZyA8LSBzYW1wbGUoMTozNTkzLCA0LCByZXBsYWNlID0gRkFMU0UpDQpkZWxheS5jbGVhbiRXZWF0aGVyW3dlYXRoZXIubWlzc2luZ10gPC0gTkENCmRlbGF5LmNsZWFuJExhdGVfQXJyaXZhbF9vW2xhdGUubWlzc2luZ10gPC0gTkENCmRlbGF5LmNsZWFuJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbc3VwcG9ydC5taXNzaW5nXSA8LSBOQQ0KYGBgDQoNCmBgYHtyfQ0Ka2FibGUoc2FwcGx5KGRlbGF5LmNsZWFuWyxjKDQsNSw3LDgpXSwgZnVuY3Rpb24gKHgpIHN1bShpcy5uYSh4KSkpLCBjYXB0aW9uID0gIlRhYmxlIDU6DQogICAgICA8YnI+Q291bnQgb2YgZm9yY2VkIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHBlciB2YXJpYWJsZSIsIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlcyIsICJDb3VudCBvZiBNaXNzaW5nIE9ic2VydmF0aW9ucyIpKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCiMjIE1pc3NpbmcgVmFsdWUgSW1wdXRhdGlvbg0KDQpJbiB0aGUgcHJldmlvdXMgc2VjdGlvbiBtaXNzaW5nIHZhbHVlcyB3ZXJlIGZvcmNlZCBpbiB0aGlzIHN5bnRoZXRpYyBkYXRhIHNldCB0byBpbWl0YXRlIHRoZSByZWFsaXRpZXMgb2YgZGF0YSBjb2xsZWN0aW9uIGFuZCBzdGFuZGFyZGl6YXRpb24gYWNyb3NzIHRoaXMgZ2xvYmFsbHkgZGl2ZXJzZSBhcnJheSBvZiBhaXJsaW5lcyBhbmQgYWlycG9ydHMuIFRoaXMga25vY2tvdXQgcHJvY2VkdXJlIHdhcyBjb21wbGV0ZWQgdG8gYWxsb3cgZm9yIHRoZSBzdWJzZXF1ZW50IGRhdGEgaW1wdXRhdGlvbiBwcm9jZXNzIHRoYXQgd2lsbCBiZSByZXF1aXJlZCBmb3IgcmVhbCB3b3JsZCBkYXRhIHRoYXQgY29udGFpbnMgbmF0dXJhbCBtaXNzaW5nIHZhbHVlcy4gV2hlbiBoYW5kbGluZyByZWFsIHdvcmxkIGdsb2JhbCBmbGlnaHQgZGVsYXkgZGF0YSwgYWxsIG1pc3NpbmcgdmFsdWVzIG11c3QgYmUgaW1wdXRlZCB0byBlbnN1cmUgbm8gZ2FwcyByZW1haW4gaW4gdGhlIGRhdGEsIHdpdGhvdXQgY29tcGxldGVseSBkcm9wcGluZyBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nIGRhdGEuICBUaGlzIHByZXZlbnRzIG92ZXJhbGwgZGF0YSBsb3NzIGJ5IHJlbW92aW5nIHRoZSBuZWVkIHRvIG9taXQgZnVsbCBvYnNlcnZhdGlvbnMgYmFzZWQgb24gYSBzaW5nbGUgbWlzc2luZyB2YWx1ZS4gDQoNCg0KIyMjIE1pc3NpbmcgVmFsdWUgQW5hbHlzaXMgYW5kIEludGVwcmV0YXRpb24NCg0KUHJpb3IgdG8gY29tcGxldGluZyBhbnkgYW5hbHlzaXMsIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgYWlybGluZSBkZWxheSBkYXRhIG11c3QgYmUgaW1wdXRlZCB0byBlbnN1cmUgdGhlcmUgYXJlIG5vIGdhcHMgaW4gdGhlIGRhdGEuIC4gIEEgc3VtbWFyeSBvZiB0aGUgbWlzc2luZyB2YWx1ZXMgYWNyb3NzIGVhY2ggdmFyaWFibGUgYXJlIGluY2x1ZGVkIGluIHRoZSBiZWxvdyBgRmlndXJlIDFgLCBgVGFibGUgNmAsIGFuZCBgVGFibGUgN2AuICBBcyBkZW1vbnN0cmF0ZWQgYnkgdGhlIHAgdmFsdWUgaW4gTGl0dGxlJ3MgTUNBUiBUZXN0IDxpPihwID0gMC42MzgyMDkxKTwvaT4gdGhlIHJlc3VsdCBpcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBpbmRpY2F0aW5nIHRoYXQgdGhlIG1pc3NpbmcgdmFsdWVzIGFyZSBhdCByYW5kb20gYW5kIG5vdCBjcm9zcyBjb3JyZWxhdGVkIGJldHdlZW4gdmFyaWFibGVzLiAgVGhpcyBoaWdobGlnaHRzIHRoYXQgdGhlIG1pc3NpbmcgdmFsdWVzIGJldHdlZW4gdmFyaWFibGVzIGFyZSBub3QgcmVsYXRlZCB0byBvbmUgYW5vdGhlciAoZXg6IGEgbWlzc2luZyB2YWx1ZSBmb3IgV2VhdGhlciBkb2VzIG5vdCBpbXBseSB0aGF0IHRoZXJlIHdpbGwgYWxzbyBiZSBhIG1pc3NpbmcgdmFsdWUgaW4gQ2xlYW5pbmcgVGltZSkuDQoNCg0KYGBge3IsIGNvbnNvbGUgPSBGQUxTRX0NCm1pc3NpbmcuYXJyYXkgPC0gcGxvdF9wYXR0ZXJuKGRlbGF5LmNsZWFuLCByb3RhdGUgPSBUUlVFLCBjYXB0aW9uID0gVFJVRSkgKw0KICB0aGVtZShheGlzLnRpdGxlLngudG9wID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9IDEwLCBmaWcuaGVpZ2h0ID0gOCwgb3V0LndpZHRoPSI5MCUifQ0KZ3JpZC5hcnJhbmdlKGFycmFuZ2VHcm9iKG1pc3NpbmcuYXJyYXksIG5jb2wgPSAxLCBucm93ID0gMSksDQogIHRvcCA9IHRleHRHcm9iKCJNaXNzaW5nIFZhbHVlcw0KICAgICAgICAgICAgICAgICAiLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpKSwNCiAgYm90dG9tID0gdGV4dEdyb2IoIg0KICBGaWd1cmUgMQ0KICBUaGUgZGlzdHJpYnV0aW9uIHBhdHRlcm4gb2YgbWlzc2luZyB2YWx1ZXMgaW4gZmxpZ2h0IGRlbGF5IGRhdGEiKSkNCg0KYGBgDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQptaXN0eV90ZXN0IDwtIG5hLnRlc3QoZGVsYXkuY2xlYW4pDQpgYGANCg0KYGBge3J9DQprYWJsZShtaXN0eV90ZXN0JHJlc3VsdCRsaXR0bGUsIGNvbC5uYW1lcyA9IGMoIiMgb2YgT2JzLiIsICIjIE1pc3NpbmcgVmFsdWVzIiwgIiMgb2YgUGF0dGVybnMgaW4gTWlzc2luZyBWYWxzIiwgIlN0YXRpc3RpYyIsICJERiIsICJwIFZhbHVlIiksIGNhcHRpb24gPSAiVGFibGUgNjoNCiAgICAgIDxicj5MaXR0bGUncyBNQ0FSIFRlc3QiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCmBgYHtyfQ0Ka2FibGUoc2FwcGx5KGRlbGF5LmNsZWFuW2MoNCw1LDcsOCldLCBmdW5jdGlvbiAoeCkgc3VtKGlzLm5hKHgpKSksIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlIiwgIkNvdW50IG9mIE1pc3NpbmcgVmFsdWVzIiksIGNhcHRpb24gPSAiVGFibGUgNzoNCjxicj5TdW1tYXJ5IGNvdW50IG9mIG1pc3NpbmcgdmFsdWVzIGluIGVhY2ggdmFyaWFibGUgb2YgdGhlIGFpcmxpbmUgZGVsYXkgZGF0YS4gVmFyaWFibGVzIHdpdGggbm8gbWlzc2luZyB2YWx1ZXMgb21pdHRlZC4iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQoNCmBgYA0KDQoNCiMjIyBNdWx0aXBsZSBJbXB1dGF0aW9uDQoNCldoaWxlIHRoZSBkYXRhIHNldCB1dGlsaXplZCBmb3IgdGhpcyBhbmFseXNpcyBoYWQgYSBzbWFsbCBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMsIG1hbnVhbGx5IGtub2NrZWQgb3V0IG9mIHRoZSBkYXRhIHNldCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgYW5hbHlzaXMgYW5kIGRhdGEgbWFuaXB1bGF0aW9uLCByZWFsIHdvcmxkIGZsaWdodCBkYXRhIGNvbnRhaW5zIG11bHRpdHVkZXMgb2YgZ2Fwcy4gIEluIG9yZGVyIHRvIGFjY3VyYXRlbHkgY2xvc2UgdGhlc2UgbWlzc2luZyBnYXBzIGl0IGlzIHdpc2VyIHRvIHJ1biBhbiBpbXB1dGF0aW9uIG1vZGVsIG11bHRpcGxlIHRpbWVzIHRvIHRoZW4gY2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgbWlzc2luZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG11bHRpcGxlIGltcHV0YXRpb24gcnVucywgcmF0aGVyIHRoYW4gcmVseWluZyBvbiBhIHNpbmdsZSBtb2RlbCB0byBhY2N1cmF0ZWx5IGNvbXBsZXRlIGFsbCBwcmVkaWN0aW9ucywgZmxhd2xlc3NseSwgZWFjaCB0aW1lLiAgQnkgdXRpbGl6aW5nIHRoaXMgYXBwcm9hY2gsIGtub3duIGFzIGEgbXVsdGlwbGUgaW1wdXRhdGlvbiBtb2RlbCwgYW5hbHlzdHMgY2FuIGJldHRlciBjbG9zZSB0aGUgZ2FwIGluIGFpcmxpbmUgZGF0YSB3aGlsZSBhY2N1cmF0ZWx5IHJlZmxlY3RpbmcgdGhlIG1vZGVsJ3MgdW5jZXJ0YWludHkgYmFzZWQgb24gdGhlIGN1cnJlbnQgcXVhbnRpdHkgYW5kIHBhdHRlcm4gb2YgbWlzc2luZyBvYnNlcnZhdGlvbnMuICBUaHJvdWdoIGEgbXVsdGlwbGUgaW1wdXRhdGlvbiBtb2RlbCB0aGUgdmFyaWFuY2UgYmV0d2VlbiBlYWNoIGltcHV0YXRpb24gcnVuIGNhbiBiZSBjb21wYXJlZCB0byBjYXB0dXJlIGEgbWVhc3VyZSBvZiB0aGUgbW9kZWwncyB1bmNlcnRhaW50eS4gDQoNCg0KIyMjIE1JQ0UgSW1wdXRhdGlvbiBBcHByb2FjaA0KDQpBIG1pY2UgaW1wdXRhdGlvbiBtb2RlbCB3YXMgY3JlYXRlZCB0byBhbGxvdyBmb3IgaW5jcmVhc2VkIGZsZXhpYmlsaXR5IGluIHRoZSBhcnJpdmFsIGRlbGF5IHByZWRpY3Rpb25zIGFuZCB0byBhbGxvdyBmb3IgYmV0dGVyIHByZXNlcnZhdGlvbiBvZiByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIHRoYXQgbWF5IG5vdCBoYXZlIGJlZW4gb2JzZXJ2ZWQgaW4gdGhlIGluaXRpYWwgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyBzdGVwcy4gIFRoaXMgcHJvY2VzcyBhbGxvd2VkIGZvciBmdXJ0aGVyIGltcHJvdmVkIHByZWRpY3RpdmUgcG93ZXIgd2hlbiBhcHBseWluZyB0aGVzZSBtb2RlbHMgdG8gcmVhbCB3b3JsZCBkYXRhIHdpdGggYSBsYXJnZXIgdmFyaWV0eSBvZiBtaXNzaW5nIGRhdGEgcGF0dGVybnMgdGhhbiBvYnNlcnZlZCBhYm92ZSBpbiBgRmlndXJlIDFgLiANCg0KTm8gaW1wdXRhdGlvbiBtZXRob2Qgd2FzIGRpY3RhdGVkIHRvIHRoZSBNSUNFIHBhY2thZ2UgdG8gYWxsb3cgdGhlIHBhY2thZ2UgdG8gcHJlbGltaW5hcnkgZXZhbHVhdGUgZWFjaCB2YXJpYWJsZSB3aXRoIG1pc3NpbmcgZGF0YSBhbmQgZGV0ZXJtaW5lIHRoZSBhcHByb3ByaWF0ZSBpbXB1dGF0aW9uIGFwcHJvYWNoIGZvciB0aGUgaW5kaXZpZHVhbCB2YXJpYWJsZS4gIFRoaXMgYWxsb3dzIGZvciBpbmNyZWFzZWQgZmxleGliaWxpdHkgaW4gaW1wdXRpbmcgdGhlIGRhdGEsIGFuZCBwcmV2ZW50cyBhY2NpZGVudGFsbHkgYnVpbGRpbmcgYSBtb2RlbCB0aGF0IG9ubHkgd29ya3Mgd2l0aCB0aGUgY3VycmVudCBzZXQgb2YgZGF0YS4gIEJ5IGFsbG93aW5nIGZvciB0aGlzIHZhcmlhYmxlIHNwZWNpZmljIGZsZXhpYmlsaXR5LCBhIHJlLWRldGVybWluYXRpb24gb2YgdGhlIGJlc3QgZml0IGltcHV0YXRpb24gbW9kZWwgaXMgY29tcGxldGVkIGVhY2ggdGltZSB0aGUgaW1wdXRhdGlvbiBtb2RlbCBpcyBydW4uICBUaGVyZWZvcmUgdGhlIGltcHV0YXRpb24gbW9kZWwgY2FuIGVtcGxveSBiZXN0IGZpdCBhcHByb2FjaCBiYXNlZCBvbiB0aGUgY3VycmVudGx5IGF2YWlsYWJsZSBkYXRhLCBlYWNoIHRpbWUgdGhlIGltcHV0YXRpb24gaXMgcnVuIHJlZ2FyZGxlc3Mgb2YgdGhlIHBhdHRlcm4gYW5kIHF1YW50aXR5IG9mIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHByZXNlbnQuICBUaGlzIGFsbG93cyBmb3IgcmVhbCB3b3JsZCBkYXRhIHR1bmluZyBhcyBuZXcgZmxpZ2h0IGRlbGF5IGRhdGEgaXMgb2J0YWluZWQgZ2xvYmFsbHkuICBEaXN0cmlidXRpb25zIG9mIHRoZSBpbXB1dGVkIHZhbHVlcyBpbiBlYWNoIG9mIHRoZSA1IGltcHV0YXRpb24gcnVucyB1dGlsaXplZCBpbiB0aGUgTUlDRSBpbXB1dGF0aW9uIG1vZGVsIGNhbiBiZSBzZWVuIGJlbG93IGluIGBGaWd1cmUgMmAuICANCg0KDQpgYGB7cn0NCm0uZGVsYXkgPC0gZGVsYXkuY2xlYW5bLTFdDQptLmRlbGF5JENsZWFuaW5nX28gPC0gYXMubnVtZXJpYyhtLmRlbGF5JENsZWFuaW5nX28pDQoNCnNldC5zZWVkKDEyMjM1NykNCm1pY2UxIDwtIG1pY2UobS5kZWxheSwgbSA9IDUsIG1heGl0ID0gMTAsIHNlZWQgPSAxMjMsIHByaW50ID0gRkFMU0UpDQptaWNlLmRlbGF5IDwtIGNvbXBsZXRlKG1pY2UxKQ0KbWljZS5kZWxheSRDbGVhbmluZ19vIDwtIGFzLmZhY3RvcihtaWNlLmRlbGF5JENsZWFuaW5nX28pDQpsZXZlbHMobWljZS5kZWxheSRDbGVhbmluZ19vKSA8LSBjKCJJbW1lZGlhdGUiLCAiUXVpY2siLCAiTW9kZXJhdGUiLCAiU2xvdyIpDQpgYGANCg0KDQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSIxMDAlIn0NCm1pY2UucGxvdCA8LSBwbG90X3RyYWNlKG1pY2UxKQ0KDQpncmlkLmFycmFuZ2UoYXJyYW5nZUdyb2IobWljZS5wbG90LCBuY29sID0gMSwgbnJvdyA9MSksDQogICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIk1JQ0UgTW9kZWwgSW1wdXRhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE4LCBmb250ZmFjZSA9ICJib2xkIikpLA0KICAgICAgICAgICAgIGJvdHRvbSA9IHRleHRHcm9iKCJGaWd1cmUgMjoNCkEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZWFjaCBpbXB1dGF0aW9uIHJ1biANCnV0aWxpemluZyB0aGUgTUlDRSBpbXB1dGF0aW9uIHBhY2thZ2UuIEEgdG90YWwgb2YgNSBpbXB1dGF0aW9uIHJ1bnMgd2VyZSBjb21wbGV0ZWQgDQp3aXRoIGEgbWF4aW11bSBvZiAxMCBpdGVyYXRpb25zIHBlciBpbXB1dGF0aW9uLiIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDEwKSkpDQoNCg0Ka2FibGUobWljZTEkbWV0aG9kLCBjb2wubmFtZXMgPSBjKCJWYXJpYWJsZSBOYW1lIiwgIkltcHV0YXRpb24gTWV0aG9kIiksIGNhcHRpb24gPSAiVGFibGUgODoNCiAgICAgIDxicj5JbXB1dGF0aW9uIG1ldGhvZHMgdXRpbGl6ZWQgZm9yIGVhY2ggdmFyaWFibGUuDQogICAgICA8YnI+QmxhbmsgdmFsdWVzIGluZGljYXRlIG5vIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHdlcmUgaWRlbnRpZmllZCBmb3IgdGhlIHZhcmlhYmxlIGFuZCB0aHVzIG5vIGltcHV0YXRpb24gd2FzIHJlcXVpcmVkLiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCmBgYA0KDQpCZWZvcmUgcGFzc2luZyB0aGUgTUlDRSBpbXB1dGF0aW9uIHBhY2thZ2UgdGhyb3VnaCB0aGUgY29tcGxldGUoKSBwYWNrYWdlIHRvIGF1dG9tYXRpY2FsbHkgY29tYmluZSB0aGUgaW1wdXRhdGlvbiBydW5zIGludG8gYSBzaW5nbGUgYmVzdCBmaXQgaW1wdXRlZCBkYXRhIHNldCwgdGhlIGltcHV0YXRpb24gc3RhdGlzdGljcyB3ZXJlIGNhbGN1bGF0ZWQgdXNpbmcgUnViZW4ncyBydWxlcyBmaXJzdCB0byBoZWxwIGRldGVybWluZSB0aGUgZWZmaWNhY3kgYW5kIHZhbGlkaXR5IG9mIHRoaXMgaW1wdXRhdGlvbi4gVGhpcyBhbGxvd2VkIGZvciBzYW1wbGUgbWVhbnMgYW5kIHZhcmlhbmNlIG9mIGVhY2ggaW1wdXRhdGlvbiBydW4gYXMgd2VsbCBhcyB0aGUgdmFyaWFuY2UgPGk+YmV0d2VlbiBlYWNoIGltcHV0YXRpb24gcnVuPC9pPiB0byBiZSBjb21iaW5lZCBpbnRvIGEgd29ya2luZyBwYXJhbWV0ZXJzIHRvIGRlc2NyaWJlIHRoZSBjb21iaW5hdGlvbnMgb2YgdGhlIG11bHRpcGxlIGltcHV0YXRpb24gbW9kZWwuIEFzIGRlbW9uc3RyYXRlZCBiZWxvdyBpbiBgVGFibGUgOWAgQi5iYXIgYW5kIFN0YW5kYXJkIEVycm9yIHZhbHVlcyBkZW1vbnN0cmF0ZSBuZWFyIG5lZ2xpZ2libGUgdmFyaWFuY2UgYmV0d2VlbiBlYWNoIGltcHV0YXRpb24gcnVucyBhbmQgbWluaW1hbCBlcnJvciBmb3IgdGhlIFdlYXRoZXIsIExhdGUgSW5jb21pbmcgUGxhbmUgQXJyaXZhbCwgYW5kIENsZWFuaW5nIHRpbWUgdmFyaWFibGVzLiAgV2hpbGUgdGhlIFN1cHBvcnQgQ3Jld3MgQXZhaWxhYmxlIHZhcmlhbmNlIGJldHdlZW4gaW1wdXRhdGlvbiBydW5zIGhhcyByZXR1cm5lZCBhIGxhIHZhbHVlLCB0aGUgc3RhbmRhcmQgb2YgZXJyb3Igb2YgNyAoNyBjcmV3IG1lbWJlcnMpIHN1cHBvcnRzIHRoaXMgdmFyaWFuY2UgbWF5IG5vdCBiZSBpbmNvbmNlaXZhYmxlLiANCg0KJCQgDQpcYmVnaW57YWxpZ24qfQ0KTWVhbiBcIG9mIFwgUXVhbml0aXR5IFwgT2YgXCBJbnRlcmVzdCdzIFwgTWVhbnMtPiBcIFxvdmVybGluZXtRfSAmPSBcc3VtIF97aT0xfSBee219IFxmcmFje1FfaX17bX0gXFwNCkF2ZXJhZ2UgXCBTdGFuZGFyZCBcIG9mIFwgRXJyb3IgXCBvZiBcIFF1YW50LiBcIG9mIFwgSW50ZXJlc3QtPiBcIFxvdmVybGluZXtVfSAmPSBcc3VtIF97aT0xfSBee219IFxmcmFje1VfaX17bX0gXFwNClZhcmlhbmNlIFwgb2YgXCBNZWFuIFwgb2YgXCBRdWFudC4gXCBvZiBcIEludGVyZXN0LT4gXCBCICY9IFxmcmFjezF9e20tMX1cc3VtIF97aT0xfSBee219IChRX2kgLSBcb3ZlcmxpbmV7UX0pXjIgXFwNCkVzdGltYXRlZCBcIFRvdGFsIFwgVmFyaWFuY2UgXCBvZiBcIFxvdmVybGluZXtRfS0+IFwgVCAmPSBcb3ZlcmxpbmV7VX0gKyBcZnJhY3ttICsgMX17bX1CDQpcZW5ke2FsaWduKn0NCiQkDQoNCmBgYHtyfQ0KDQpuLm1pY2UgPC0gbGVuZ3RoKG1pY2UxJGltcCRXZWF0aGVyKQ0KDQpxd2VhdGhlci5tIDwtIGMobWVhbihtaWNlMSRpbXAkV2VhdGhlclssMV0pLCBtZWFuKG1pY2UxJGltcCRXZWF0aGVyWywyXSksIG1lYW4obWljZTEkaW1wJFdlYXRoZXJbLDNdKSwgbWVhbihtaWNlMSRpbXAkV2VhdGhlclssNF0pLCANCiAgICAgICAgICAgICAgICAgICAgIG1lYW4obWljZTEkaW1wJFdlYXRoZXJbLDVdKSkNCnV3ZWF0aGVyLm0gPC0gYyhzdGQuZXJyb3IobWljZTEkaW1wJFdlYXRoZXJbLDFdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRXZWF0aGVyWywyXSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkV2VhdGhlclssM10pLCANCiAgICAgICAgICAgICAgICBzdGQuZXJyb3IobWljZTEkaW1wJFdlYXRoZXJbLDRdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRXZWF0aGVyWyw1XSkpDQpxLmJhci53ZWF0aGVyLm0gPC0gbWVhbihxd2VhdGhlci5tKQ0KdS5iYXIud2VhdGhlci5tIDwtIG1lYW4odXdlYXRoZXIubSkNCmIud2VhdGhlci5tIDwtIHZhcihxd2VhdGhlci5tKQ0KdC53ZWF0aGVyLm0gPC0gKHEuYmFyLndlYXRoZXIubSArICgobi5taWNlICsgMSkvbi5taWNlKSpiLndlYXRoZXIubSkNCnNlLndlYXRoZXIubSA8LSBzdGQuZXJyb3IocXdlYXRoZXIubSkNCg0KDQpxc3VwY3Jldy5tIDwtIGMobWVhbihtaWNlMSRpbXAkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVssMV0pLCBtZWFuKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWywyXSksIG1lYW4obWljZTEkaW1wJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbLDNdKSwgDQogICAgICAgICAgICAgICAgbWVhbihtaWNlMSRpbXAkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVssNF0pLCBtZWFuKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWyw1XSkpDQp1c3VwY3Jldy5tIDwtIGMoc3RkLmVycm9yKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWywxXSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVssMl0pLCANCiAgICAgICAgICAgICAgICBzdGQuZXJyb3IobWljZTEkaW1wJFN1cHBvcnRfQ3Jld19BdmFpbGFibGVbLDNdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlWyw0XSksIA0KICAgICAgICAgICAgICAgIHN0ZC5lcnJvcihtaWNlMSRpbXAkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZVssNV0pKQ0KcS5iYXIuc3VwY3Jldy5tIDwtIG1lYW4ocXN1cGNyZXcubSkNCnUuYmFyLnN1cGNyZXcubSA8LSBtZWFuKHVzdXBjcmV3Lm0pDQpiLnN1cGNyZXcubSA8LSB2YXIocXN1cGNyZXcubSkNCnQuc3VwY3Jldy5tIDwtIChxLmJhci5zdXBjcmV3Lm0gKyAoKG4ubWljZSArIDEpL24ubWljZSkqYi5zdXBjcmV3Lm0pDQpzZS5zdXBjcmV3Lm0gPC0gc3RkLmVycm9yKHFzdXBjcmV3Lm0pDQoNCnFsYXRlaW5jb20ubSA8LSBjKG1lYW4obWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWywxXSksIG1lYW4obWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWywyXSksIG1lYW4obWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWywzXSksIA0KICAgICAgICAgICAgICAgICAgbWVhbihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDRdKSwgbWVhbihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDVdKSkNCnVsYXRlaW5jb20ubSA8LSBjKHN0ZC5lcnJvcihtaWNlMSRpbXAkTGF0ZV9BcnJpdmFsX29bLDFdKSwgc3RkLmVycm9yKG1pY2UxJGltcCRMYXRlX0Fycml2YWxfb1ssMl0pLCBzdGQuZXJyb3IobWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWywzXSksIA0KICAgICAgICAgICAgICAgICAgc3RkLmVycm9yKG1pY2UxJGltcCRMYXRlX0Fycml2YWxfb1ssNF0pLCBzdGQuZXJyb3IobWljZTEkaW1wJExhdGVfQXJyaXZhbF9vWyw1XSkpDQpxLmJhci5sYXRlaW5jb20ubSA8LSBtZWFuKHFsYXRlaW5jb20ubSkNCnUuYmFyLmxhdGVpbmNvbS5tIDwtIG1lYW4odWxhdGVpbmNvbS5tKQ0KYi5sYXRlaW5jb20ubSA8LSB2YXIocWxhdGVpbmNvbS5tKQ0KdC5sYXRlaW5jb20ubSA8LSAocS5iYXIubGF0ZWluY29tLm0gKyAoKG4ubWljZSArIDEpL24ubWljZSkqYi5sYXRlaW5jb20ubSkNCnNlLmxhdGVpbmNvbS5tIDwtIHN0ZC5lcnJvcihxbGF0ZWluY29tLm0pDQoNCnFjbGVhbi5tIDwtIGMobWVhbihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssMV0pLCBtZWFuKG1pY2UxJGltcCRDbGVhbmluZ19vWywyXSksIG1lYW4obWljZTEkaW1wJENsZWFuaW5nX29bLDNdKSwgbWVhbihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssNF0pLCANCiAgICAgICAgICAgICAgICAgICAgIG1lYW4obWljZTEkaW1wJENsZWFuaW5nX29bLDVdKSkNCnVjbGVhbi5tIDwtIGMoc3RkLmVycm9yKG1pY2UxJGltcCRDbGVhbmluZ19vWywxXSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssMl0pLCBzdGQuZXJyb3IobWljZTEkaW1wJENsZWFuaW5nX29bLDNdKSwgDQogICAgICAgICAgICAgICAgc3RkLmVycm9yKG1pY2UxJGltcCRDbGVhbmluZ19vWyw0XSksIHN0ZC5lcnJvcihtaWNlMSRpbXAkQ2xlYW5pbmdfb1ssNV0pKQ0KcS5iYXIuY2xlYW4ubSA8LSBtZWFuKHFjbGVhbi5tKQ0KdS5iYXIuY2xlYW4ubSA8LSBtZWFuKHVjbGVhbi5tKQ0KYi5jbGVhbi5tIDwtIHZhcihxY2xlYW4ubSkNCnQuY2xlYW4ubSA8LSAocS5iYXIuY2xlYW4ubSArICgobi5taWNlICsgMSkvbi5taWNlKSpiLmNsZWFuLm0pDQpzZS5jbGVhbi5tIDwtIHN0ZC5lcnJvcihxY2xlYW4ubSkNCg0KDQptaWNlLm1lcmdlIDwtIGRhdGEuZnJhbWUoDQogICAgU3RhdGlzdGljID0gYygiUS5iYXIiLCAiVS5iYXIiLCAiQi52YXIiLCAiVCIsICJTdGQuRXJyIiksDQogICAgbWljZS53ZWF0aGVyID0gYyhxLmJhci53ZWF0aGVyLm0sIHUuYmFyLndlYXRoZXIubSwgYi53ZWF0aGVyLm0sIHQud2VhdGhlci5tLCBzZS53ZWF0aGVyLm0pLA0KICAgIG1pY2Uuc3VwLmNyZXcgPSBjKHEuYmFyLnN1cGNyZXcubSwgdS5iYXIuc3VwY3Jldy5tLCBiLnN1cGNyZXcubSwgdC5zdXBjcmV3Lm0sIHNlLnN1cGNyZXcubSksDQogICAgbWljZS5sYXRlLmluY29taW5nID0gYyhxLmJhci5sYXRlaW5jb20ubSwgdS5iYXIubGF0ZWluY29tLm0sIGIubGF0ZWluY29tLm0sIHQubGF0ZWluY29tLm0sIHNlLmxhdGVpbmNvbS5tKSwNCiAgICBtaWNlLmNsZWFuID0gYyhxLmJhci5jbGVhbi5tLCB1LmJhci5jbGVhbi5tLCBiLmNsZWFuLm0sIHQuY2xlYW4ubSwgc2UuY2xlYW4ubSkNCikNCg0KYGBgDQoNCmBgYHtyfQ0Ka2FibGUobWljZS5tZXJnZSwgY29sLm5hbWVzID0gYygiU3RhdGlzdGljIiwgdmFyLmRldGFpbHNbNCwxXSwgdmFyLmRldGFpbHNbNSwxXSwgdmFyLmRldGFpbHNbNywxXSwgdmFyLmRldGFpbHNbOCwxXSkgLCBjYXB0aW9uID0gIlRhYmxlIDk6DQogICAgICA8YnI+DQogICAgICBTdW1tYXJ5IHN0YXRpc3RpY3MgdXNpbmcgUnViZW4ncyBSdWxlcyBmb3IgTUlDRSBJbXB1dGF0aW9uIGZvciBtaXNzaW5nIHZhbHVlcyBpbiBnbG9iYWwgZmxpZ2h0IGRlbGF5IGRhdGEgPGJyPiBudW1iZXIgb2YgaW1wdXRhdGlvbnMgKG0pID0gNSA8YnI+IG1heGltdW0gaXRlcmF0aW9ucyAobWF4aXQpID0gMTAiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCkFzIGEgc3VwcG9ydGluZyBhbmFseXNpcyB0byB0aGUgUnViZW4ncyBSdWxlcyB0b3RhbHMgYWJvdmUsIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBpbml0aWFsIHZhcmlhYmxlcyBwcmlvciB0byBpbXB1dGF0aW9uIGFuZCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGFmdGVyIGltcHV0YXRpb24gd2VyZSByZXZpZXdlZCAoYFRhYmxlIDEwLmFgICYgYFRhYmxlIDEwLmJgIGJlbG93KS4gVGhlIGltcHV0ZWQgV2VhdGhlciBhbmQgTGF0ZSBJbmNvbWluZyBQbGFuZSBBcnJpdmFsIHZhcmlhYmxlcyBkaXNwbGF5ZWQgbm8gY2hhbmdlIHRvIHRoZSBtZWFuIG9yIG1lZGlhbiBwb3N0IGltcHV0YXRpb24sIGluZGljYXRpbmcgYSBoaWdoIHN1Y2Nlc3MgcmF0ZSBmb3IgdGhlIE1JQ0UgcHBtIGltcHV0YXRpb24gYXBwcm9hY2guICBBZGRpdGlvbmFsbHksIHRoZSBTdXBwb3J0IENyZXdzIEF2YWlsYWJsZSB2YXJpYWJsZSBkZW1vbnN0cmF0ZWQgbm8gY2hhbmdlIGluIG1lYW4gYW5kIG9ubHkgYSAwLjAzIGNoYW5nZSBpbiB0aGUgbWVkaWFuLiAgQXMgdGhpcyB2YXJpYWJsZSBpcyBtZWFzdXJpbmcgdGhlIG51bWJlciBvZiBwZW9wbGUgYXZhaWxhYmxlIGluIHRoZSBzdXBwb3J0IGNyZXdzIHRoaXMgMC4wMyBjaGFuZ2UgaW4gbWVkaWFuIGNhbiBiZSBjb21wbGV0ZWx5IGlnbm9yZWQgYXMgdGhlcmUgY2Fubm90IGZlYXNpYmx5IGJlIDAuMDMgb2YgYSBwZXJzb24uICBGaW5hbGx5IHRoZSBpbXB1dGF0aW9uIG9mIENsZWFuaW5nIFRpbWUgdmFyaWFibGUgZGVtb25zdHJhdGVkIHRoZSBncmVhdGVzdCBmcmVxdWVuY3kgaW5jcmVhc2UgZm9yIHRoZSBtb2RlIHZhbHVlLCBmdXJ0aGVyIGNvcnJvYm9yYXRpbmcgdGhlIHN1Y2Nlc3Mgb2YgdGhlIE1JQ0UgcHBtIGltcHV0YXRpb24gZm9yIHVzZSB3aXRoIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuIA0KDQpgYGB7cn0NCg0Ka2FibGUoc3VtbWFyeShkZWxheS5jbGVhblssIGMoNCwgNSwgNywgOCldKSwgY29sLm5hbWVzID0gYyh2YXIuZGV0YWlsc1s0LDFdLCB2YXIuZGV0YWlsc1s1LDFdLCB2YXIuZGV0YWlsc1s3LDFdLCB2YXIuZGV0YWlsc1s4LDFdKSwgY2FwdGlvbiA9ICJUYWJsZSAxMC5hOg0KPGJyPlN1bW1hcnkgc3RhdGlzdGljcyBvZiB2YXJpYWJsZXMgd2l0aCBtaXNzaW5nIHZhbHVlcyBiZWZvcmUgTUlDRSBpbXB1dGF0aW9uIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCg0Ka2FibGUoc3VtbWFyeShtaWNlLmRlbGF5WywgYygzLCA0LCA2LCA3KV0pLCBjb2wubmFtZXMgPSBjKHZhci5kZXRhaWxzWzQsMV0sIHZhci5kZXRhaWxzWzUsMV0sIHZhci5kZXRhaWxzWzcsMV0sIHZhci5kZXRhaWxzWzgsMV0pLCBjYXB0aW9uID0gIlRhYmxlIDEwLmI6DQo8YnI+U3VtbWFyeSBzdGF0aXN0aWNzIG9mIHZhcmlhYmxlcyB3aXRoIG1pc3NpbmcgdmFsdWVzIGFmdGVyIE1JQ0UgaW1wdXRhdGlvbiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkNCg0KIyMgU2tldyBhbmQgRGlzdHJpYnVpb24gQ2hlY2sNCg0KSW4gb3JkZXIgdG8gZGV0ZXJtaW5lIGlmIGFueSBvZiB0aGUgdmFyaWFibGUgZGF0YSBpcyBza2V3ZWQsIHRoZSBjb250aW51b3VzIHZhcmlhYmxlIGRpc3RyaWJ1dGlvbnMgd2VyZSBwbG90dGVkIHdpdGggYW4gb3ZlcmxheSBpbmRpY2F0aW5nIHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uIGFzIGNhbGN1bGF0ZWQgd2l0aCB0aGUgcmVzcGVjdGl2ZSB2YXJpYWJsZSdzIGNhbGN1bGF0ZWQgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLiBUaGlzIGNoZWNrIHdhcyBjb21wbGV0ZWQgdG8gZW5zdXJlIHRoZSBkYXRhIGFzIGF2YWlsYWJsZSB3aWxsIGNvb3BlcmF0ZSB3aXRoIHRoZSBwcmVkaWN0aW9uIG1vZGVscyB1dGlsaXplZCBsYXRlciBpbiB0aGUgYW5hbHlzaXMuICBBcyBhIG1ham9yaXR5IG9mIHByZWRpY3Rpb24gbW9kZWxzIHJlcXVpcmUgdGhlIGFzc3VtcHRpb24gb2YgYSBub3JtYWwgZGlzdHJpYnV0aW9uLCB0aGUgY2FsY3VsYXRlZCBub3JtYWwgZGlzdHJpYnV0aW9uIGN1cnZlIGZvciBlYWNoIHJlc3BlY3RpdmUgdmFyaWFibGUgd2FzIG92ZXJsYWlkIGFzIGEgY29tcGFyaXNvbiByZWZlcmVuY2UuICBJbiBvcmRlciB0byBwcm9wZXJseSBsZXZlcmFnZSB0aGUgcHJlZGljdGlvbiBtb2RlbHMgdG8gb3B0aW1pemUgYWlycG9ydCBwbGFubmluZyB0byByZWR1Y2UgZmxpZ2h0IGRlbGF5cywgaXQgaXMgaW1wZXJhdGl2ZSB0byBlbnN1cmUgdGhlIGRhdGEgZml0cyB3aXRoaW4gdGhlIGFzc3VtcHRpb25zIG9mIHRoZSBwcmVkaWN0aW9uIG1vZGVsIHV0aWxpemVkLiANCg0KYGBge3J9DQoNCg0KZGlzdF9kZW5zIDwtIGdncGxvdChtaWNlLmRlbGF5LCBhZXMoeCA9IEFpcnBvcnRfRGlzdGFuY2UpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihtaWNlLmRlbGF5JEFpcnBvcnRfRGlzdGFuY2UpLCBzZCA9IHNkKG1pY2UuZGVsYXkkQWlycG9ydF9EaXN0YW5jZSkpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihtaWNlLmRlbGF5JEFpcnBvcnRfRGlzdGFuY2UpKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4obWljZS5kZWxheSRBaXJwb3J0X0Rpc3RhbmNlKSksIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIEJldHdlZW4gRGVwYXJ0dXJlLUFycml2YWwgQWlycG9ydHMiLA0KICAgICAgIHggPSAiRGlzdGFuY2UgKG1pbGVzKSIsDQogICAgICAgeSA9ICJEZW5zaXR5IikNCg0KbnVtZmxpZ2h0X2RlbnMgPC0gZ2dwbG90KG1pY2UuZGVsYXksIGFlcyh4ID0gTnVtYmVyX29mX2ZsaWdodHMpKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4obWljZS5kZWxheSROdW1iZXJfb2ZfZmxpZ2h0cyksIHNkID0gc2QobWljZS5kZWxheSROdW1iZXJfb2ZfZmxpZ2h0cykpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihtaWNlLmRlbGF5JE51bWJlcl9vZl9mbGlnaHRzKSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKG1pY2UuZGVsYXkkTnVtYmVyX29mX2ZsaWdodHMpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgRmxpZ2h0cyBwZXIgQWlycG9ydCAqIiwNCiAgICAgICB4ID0gIkZsaWdodHMgcGVyIEFpcnBvcnQiLA0KICAgICAgIHkgPSBOVUxMKQ0KDQpzdXBjcmV3X2RlbnMgPC0gZ2dwbG90KG1pY2UuZGVsYXksIGFlcyh4ID0gU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihtaWNlLmRlbGF5JFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpLCBzZCA9IHNkKG1pY2UuZGVsYXkkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSwgbmEucm0gPSBUUlVFKSksIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG1pY2UuZGVsYXkkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSkpLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihtaWNlLmRlbGF5JFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgU3VwcG9ydCBDcmV3IE1lbWJlcnMgQXZhaWxhYmxlICoiLA0KICAgICAgIHggPSAiU3VwcG9ydCBDcmV3IEF2YWlsYWJsZSIsDQogICAgICAgeSA9IE5VTEwpDQoNCmZ1ZWxpbmdfZGVucyA8LSBnZ3Bsb3QobWljZS5kZWxheSwgYWVzKHggPSBGdWVsaW5nX28pKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4obWljZS5kZWxheSRGdWVsaW5nX28pLCBzZCA9IHNkKG1pY2UuZGVsYXkkRnVlbGluZ19vKSksIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG1pY2UuZGVsYXkkRnVlbGluZ19vKSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKG1pY2UuZGVsYXkkRnVlbGluZ19vKSksIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlID0gIlBsYW5lIEZ1ZWxpbmcgVGltZXMiLA0KICAgICAgIHggPSAiRnVlbGluZyBUaW1lIChtaW51dGVzKSIsDQogICAgICAgeSA9IE5VTEwpIA0KDQpzZWN1cml0eV9kZW5zIDwtIGdncGxvdChtaWNlLmRlbGF5LCBhZXMoeCA9IFNlY3VyaXR5X28pKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4obWljZS5kZWxheSRTZWN1cml0eV9vKSwgc2QgPSBzZChtaWNlLmRlbGF5JFNlY3VyaXR5X28pKSwgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV3aWR0aCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4obWljZS5kZWxheSRTZWN1cml0eV9vKSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKG1pY2UuZGVsYXkkU2VjdXJpdHlfbykpLCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTZWN1cml0eSBUaW1lcyIsDQogICAgICAgeCA9ICJUaW1lIChtaW51dGVzKSIsDQogICAgICAgeSA9ICJEZW5zaXR5IikgIA0KDQphcnJpdmFsX2RlbnMgPC0gZ2dwbG90KG1pY2UuZGVsYXksIGFlcyh4ID0gQXJyX0RlbGF5KSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKG1pY2UuZGVsYXkkQXJyX0RlbGF5KSwgc2QgPSBzZChtaWNlLmRlbGF5JEFycl9EZWxheSkpLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihtaWNlLmRlbGF5JEFycl9EZWxheSkpLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihtaWNlLmRlbGF5JEFycl9EZWxheSkpLCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBGbGlnaHQgRGVsYXlzIiwNCiAgICAgICB4ID0gIkR1cmF0aW9uIChtaW51dGVzKSIsDQogICAgICAgeSA9ICJEZW5zaXR5IikgIA0KDQpsYXRlaW5jb21fZGVucyA8LSBnZ3Bsb3QobWljZS5kZWxheSwgYWVzKHggPSBMYXRlX0Fycml2YWxfbykpICsgDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihtaWNlLmRlbGF5JExhdGVfQXJyaXZhbF9vKSwgc2QgPSBzZChtaWNlLmRlbGF5JExhdGVfQXJyaXZhbF9vKSksIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG1pY2UuZGVsYXkkTGF0ZV9BcnJpdmFsX28pKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4obWljZS5kZWxheSRMYXRlX0Fycml2YWxfbykpLCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJMYXRlIEluY29taWcgUGxhbmUgRGVsYXlzICoiLA0KICAgICAgIHggPSAiRHVyYXRpb24gKG1pbnV0ZXMpIiwNCiAgICAgICB5ID0gIkRlbnNpdHkiKSAgDQoNCndlYXRoZXJfZGVucyA8LSBnZ3Bsb3QobWljZS5kZWxheSwgYWVzKHggPSBXZWF0aGVyKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKG1pY2UuZGVsYXkkV2VhdGhlciksIHNkID0gc2QobWljZS5kZWxheSRXZWF0aGVyKSksIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG1pY2UuZGVsYXkkV2VhdGhlcikpLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihtaWNlLmRlbGF5JFdlYXRoZXIpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGUgPSAiV2VhdGhlciBDb25kaXRpb25zIiwNCiAgICAgICB4ID0gIlJhbmtpbmcgb2YgV2VhdGhlciBTZXZlcml0eSIsDQogICAgICAgeSA9ICJEZW5zaXR5IikgIA0KDQpgYGANCg0KYGBge3J9DQoNCg0KYmFnZ2FnZV9iYXIgPC0gZ2dwbG90KG1pY2UuZGVsYXksIGFlcyh4ID0gQmFnZ2FnZV9sb2FkaW5nX3RpbWUpKSArIA0KICBnZW9tX2JhcihmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkJhZ2dhZ2UgTG9hZGluZyBUaW1lcyAqIiwNCiAgICAgICB4ID0gIkxvYWRpbmcgRHVyYXRpb24gKHNsb3dlc3QgdG8gZmFzdGVzdCBmcm9tIGxlZnQgdG8gcmlnaHQpIiwNCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpDQoNCmNsZWFuaW5nX2JhciA8LSBnZ3Bsb3QobWljZS5kZWxheSwgYWVzKHggPSBDbGVhbmluZ19vKSkgKyANCiAgZ2VvbV9iYXIoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJQbGFuZSBDbGVhbmluZyBUaW1lcyAqIiwNCiAgICAgICB4ID0gIkNsZWFuaW5nIER1cmF0aW9uIChzbG93ZXN0IHRvIGZhc3Rlc3QgZnJvbSBsZWZ0IHRvIHJpZ2h0KSIsDQogICAgICAgeSA9IE5VTEwpDQoNCmBgYA0KDQpgYGB7ciwgIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xOCwgb3V0LndpZHRoPSIxMDAlIiwgb3V0LmhlaWdodCA9ICIxMDAlIn0NCmdyaWQuYXJyYW5nZSgNCiAgYXJyYW5nZUdyb2IoYXJyaXZhbF9kZW5zLCBudW1mbGlnaHRfZGVucywgZGlzdF9kZW5zLCBmdWVsaW5nX2RlbnMsIHNlY3VyaXR5X2RlbnMsIHN1cGNyZXdfZGVucywgbGF0ZWluY29tX2RlbnMsIHdlYXRoZXJfZGVucywgYmFnZ2FnZV9iYXIsIGNsZWFuaW5nX2JhciwgbmNvbCA9IDIsbnJvdyA9IDUpLA0KICB0b3AgPSB0ZXh0R3JvYigiRGlzdHJpYnV0aW9uIG9mIEZsaWdodCBWYXJpYWJsZXMNCiAgICAgICAgICAgICAgICAgIiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMjAsIGZvbnRmYWNlID0gImJvbGQiKSksDQogIGJvdHRvbSA9IHRleHRHcm9iKCINCiAgRmlndXJlIDMNCiAgR3JhcGhzIG1hcmtlZCB3aXRoIG9uZSBhc3RlcmlzayAoKikgaW5kaWNhdGUgc2tld2VkIGRhdGENCiAgQmxhY2sgc29saWQgbGluZTogbWVhbiBvZiB2YXJpYWJsZSBkYXRhIHwgQmxhY2sgZGFzaGVkIGxpbmU6IG1lZGlhbiBvZiB2YXJpYWJsZSBkYXRhIikpDQoNCmBgYA0KDQoNCkFzIGRlbW9uc3RyYXRlZCBpbiB0aGUgYWJvdmUgYEZpZ3VyZSAzYCwgdGhlIGRpc3RhbmNlIGJldHdlZW4gYWlycG9ydHMgYW5kIHRvdGFsIHNlY3VyaXR5IHRpbWVzIGhvbGQgY2xvc2UgdG8gdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gY3VydmVzLiAgTWVhbndoaWxlIHRoZSB0b3RhbCBkZWxheSB0aW1lcyBmb3IgaW5jb21pbmcgcGxhbmVzIGZyb20gcHJldmlvdXMgZmxpZ2h0cywgZnVlbGluZyB0aW1lcywgYW5kIHdlYXRoZXIgc2V2ZXJpdHkgaWxsdXN0cmF0ZSBub24tbm9ybWFsIGRpc3RyaWJ1dGlvbnMuIEF0IGEgY3Vyc29yeSByZXZpZXcgdGhlc2UgdmFyaWFibGVzIGFwcGVhciB0byBob2xkIHRvIG11bHRpbW9kYWwgYW5kIEJlcm5vdWxsaSBkaXN0cmlidXRpb25zLiBNZWFud2hpbGUgdGhlIG51bWJlciBvZiBmbGlnaHRzIGFuZCB0b3RhbCBzdXBwb3J0IGNyZXdzIGF2YWlsYWJsZSBkZW1vbnN0cmF0ZWQgc2tld2VkIG5vcm1hbCBkaXN0cmlidXRpb25zDQoNCiMjIyBVbmRlcnN0YW5kaW5nIHRoZSBTa2V3DQoNCkluIG9yZGVyIHRvIGNvbmZpcm0gdGhlIHZpc3VhbGx5IGlkZW50aWZpZWQgZGF0YSBza2V3cyB0aGUgbWVhbnMgYW5kIG1lZGlhbnMgb2YgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXMgd2VyZSBjb21wYXJlZDoNCg0KICAtIE51bWJlciBvZiBGbGlnaHRzDQogIC0gU3VwcG9ydCBDcmV3cyBBdmFpbGFibGUNCiAgLSBMYXRlIEluY29taW5nIFBsYW5lcw0KICAtIEJhZ2dhZ2UgTG9hZGluZyBUaW1lDQogIC0gQ2xlYW5pbmcNCg0KVXBvbiBjb21wYXJpc29uIG9mIHRoZSBtZWFuIGFuZCBtZWRpYW4gb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIGl0IHdhcyBpZGVudGlmaWVkIHRoYXQgdGhlIGxhdGUgaW5jb21pbmcgcGxhbmVzIHZhcmlhYmxlIHdhcyBub3Qgc2tld2VkIGFzIHRoZSBtZWRpYW4gYW5kIG1lYW4gd2VyZSBpZGVudGljYWwuICBGcmVxdWVuY3kgY291bnRzIGZvciB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFsc28gaWRlbnRpZmllZCB0aGF0IHRoZSBtb2RlIG9mIHRoZXNlIHZhcmlhYmxlcyBjb3Jyb2JvcmF0ZWQgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGlzIGxlZnQgdGhlIG51bWJlciBvZiBmbGlnaHRzIGFuZCBzdXBwb3J0IGNyZXdzIGF2YWlsYWJsZSBhcyB0aGUgdHdvIHRydWx5IHNrZXdlZCB2YXJpYWJsZXMgcmVxdWlyaW5nIHRyYW5zZm9ybWF0aW9uLiANCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KHJvdW5kKG1pY2UuZGVsYXlbYygyLDQsNildKSwgZGlnaXRzID0gMSksIGNhcHRpb24gPSAiVGFibGUgMTE6DQo8YnI+U3VtbWFyeSBzdGF0aXN0aWNzIGZvciB2YXJpYWJsZXMgd2l0aCBza2V3ZWQgZGlzdHJpYnV0aW9ucy4gUm91bmRlZCB0byB3aG9sZSBudW1iZXJzIGFzIHRoZSBmcmFjdGlvbnMgb2YgZmxpZ2h0cyBhbmQgZnJhY3Rpb25zIG9mIHN1cHBvcnQgY3JldyBtZW1iZXJzIGNhbm5vdCBleGlzdCBuYXR1cmFsbHkuIiwgY29sLm5hbWVzID0gYyh2YXIuZGV0YWlsc1szLDFdLCB2YXIuZGV0YWlsc1s1LDFdLCB2YXIuZGV0YWlsc1s3LDFdKSkgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCg0Ka2FibGUoc3VtbWFyeShtaWNlLmRlbGF5W2MoNSwgNyldKSwgY2FwdGlvbiA9ICJUYWJsZSAxMjoNCjxicj5GcmVxdWVuY3kgY291bnQgdG8gY2FwdHVyZSB0aGUgbW9kZSBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBza2V3ZWQgZGlzdHJpYnV0aW9ucy4iLCBjb2wubmFtZXMgPSBjKHZhci5kZXRhaWxzWzYsMV0sIHZhci5kZXRhaWxzWzgsMV0pKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpgYGANCg0KDQoNCg0KIyMjIFNrZXcgQ29ycmVjdGlvbiBUcm91Z2ggVmFyaWFibGUgTm9ybWFsaXphdGlvbg0KDQpJbiBvcmRlciB0byBub3JtYWxpemUgdGhlIHZhcmlhYmxlcyBmb3IgZnV0dXJlIHByZWRpY3RpdmUgbW9kZWwgdXNlLCBtaW4tbWF4IG5vcm1hbGl6YXRpb24gd2FzIGVtcGxveWVkIHRvIHJlZHVjZSBhbGwgdmFyaWFibGVzIHRvIGEgcmFuZ2Ugb2YgWzAsMV0uIFRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2VyZSBhbHJlYWR5IGVuY29kZWQgYXMgZmFjdG9yIHZhcmlhYmxlcywgYWxsb3dpbmcgZm9yIGVhc3kgbGFiZWwgZW5jb2RpbmcgdG8gYWxsb3cgZm9yIGNvb3BlcmF0aW9uIHdpdGggdGhlIG1pbi1tYXggc3RhbmRhcmRpemF0aW9uLiBUaGUgZm9sbG93aW5nIGVxdWF0aW9uIHdhcyBhcHBsaWVkIHRvIGFsbCBudW1lcmljIHZhcmlhYmxlcyAoYm90aCBvcmlnaW5hbGx5IG51bWVyaWMgYW5kIGZvcmNlZCBudW1lcmljIGZyb20gZmFjdG9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcykuDQoNCiQkeF97c3RhbmRhcmRpemVkfSA9IFxmcmFje3ggLSBtaW4oeCl9e21heCh4KSAtIG1pbih4KX0kJA0KDQpgYGB7cn0NCm1pY2UuZGVsYXkkY2xlYW5pbmcgPC0gYXMubnVtZXJpYyhtaWNlLmRlbGF5JENsZWFuaW5nX28pDQptaWNlLmRlbGF5JGJhZ2dhZ2UgPC0gYXMubnVtZXJpYyhtaWNlLmRlbGF5JEJhZ2dhZ2VfbG9hZGluZ190aW1lKQ0KDQpkZWxheS5zdGQgPC0gZGF0YS5mcmFtZSgNCiAgYWlycG9ydF9kaXN0ID0gKG1pY2UuZGVsYXkkQWlycG9ydF9EaXN0YW5jZSAtIG1pbihtaWNlLmRlbGF5JEFpcnBvcnRfRGlzdGFuY2UpKSAvIChtYXgobWljZS5kZWxheSRBaXJwb3J0X0Rpc3RhbmNlKSAtIG1pbihtaWNlLmRlbGF5JEFpcnBvcnRfRGlzdGFuY2UpKSwNCiAgbnVtX2ZsaWdodHMgPSAobWljZS5kZWxheSROdW1iZXJfb2ZfZmxpZ2h0cyAtIG1pbihtaWNlLmRlbGF5JE51bWJlcl9vZl9mbGlnaHRzKSkgLyAobWF4KG1pY2UuZGVsYXkkTnVtYmVyX29mX2ZsaWdodHMpIC0gbWluKG1pY2UuZGVsYXkkTnVtYmVyX29mX2ZsaWdodHMpKSwNCiAgd2VhdGhlciA9IChtaWNlLmRlbGF5JFdlYXRoZXIgLSBtaW4obWljZS5kZWxheSRXZWF0aGVyKSkgLyAobWF4KG1pY2UuZGVsYXkkV2VhdGhlcikgLSBtaW4obWljZS5kZWxheSRXZWF0aGVyKSksDQogIHN1cF9jcmV3ID0gKG1pY2UuZGVsYXkkU3VwcG9ydF9DcmV3X0F2YWlsYWJsZSAtIG1pbihtaWNlLmRlbGF5JFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpKSAvIChtYXgobWljZS5kZWxheSRTdXBwb3J0X0NyZXdfQXZhaWxhYmxlKSAtIG1pbihtaWNlLmRlbGF5JFN1cHBvcnRfQ3Jld19BdmFpbGFibGUpKSwNCiAgbGF0ZV9pbmNvbWluZyA9IChtaWNlLmRlbGF5JExhdGVfQXJyaXZhbF9vIC0gbWluKG1pY2UuZGVsYXkkTGF0ZV9BcnJpdmFsX28pKSAvIChtYXgobWljZS5kZWxheSRMYXRlX0Fycml2YWxfbykgLSBtaW4obWljZS5kZWxheSRMYXRlX0Fycml2YWxfbykpLA0KICBmdWVsaW5nID0gKG1pY2UuZGVsYXkkRnVlbGluZ19vIC0gbWluKG1pY2UuZGVsYXkkRnVlbGluZ19vKSkgLyAobWF4KG1pY2UuZGVsYXkkRnVlbGluZ19vKSAtIG1pbihtaWNlLmRlbGF5JEZ1ZWxpbmdfbykpLA0KICBzZWN1cml0eSA9IChtaWNlLmRlbGF5JFNlY3VyaXR5X28gLSBtaW4obWljZS5kZWxheSRTZWN1cml0eV9vKSkgLyAobWF4KG1pY2UuZGVsYXkkU2VjdXJpdHlfbykgLSBtaW4obWljZS5kZWxheSRTZWN1cml0eV9vKSksDQogIGFycl9kZWxheSA9IChtaWNlLmRlbGF5JEFycl9EZWxheSAtIG1pbihtaWNlLmRlbGF5JEFycl9EZWxheSkpIC8gKG1heChtaWNlLmRlbGF5JEFycl9EZWxheSkgLSBtaW4obWljZS5kZWxheSRBcnJfRGVsYXkpKSwNCiAgY2xlYW5pbmcgPSAobWljZS5kZWxheSRjbGVhbmluZyAtIG1pbihtaWNlLmRlbGF5JGNsZWFuaW5nKSkgLyAobWF4KG1pY2UuZGVsYXkkY2xlYW5pbmcpIC0gbWluKG1pY2UuZGVsYXkkY2xlYW5pbmcpKSwNCiAgYmFnZ2FnZSA9IChtaWNlLmRlbGF5JGJhZ2dhZ2UgLSBtaW4obWljZS5kZWxheSRiYWdnYWdlKSkgLyAobWF4KG1pY2UuZGVsYXkkYmFnZ2FnZSkgLSBtaW4obWljZS5kZWxheSRiYWdnYWdlKSkNCikNCmBgYA0KDQpgYGB7cn0NCmthYmxlKHN1bW1hcnkoZGVsYXkuc3RkKSwgY29sLm5hbWVzID0gYyh2YXIuZGV0YWlsc1syLDFdLCB2YXIuZGV0YWlsc1szLDFdLCB2YXIuZGV0YWlsc1s0LDFdLCB2YXIuZGV0YWlsc1s1LDFdLCB2YXIuZGV0YWlsc1s2LDFdLCB2YXIuZGV0YWlsc1s3LDFdLCB2YXIuZGV0YWlsc1s4LDFdLCB2YXIuZGV0YWlsc1s5LDFdLCB2YXIuZGV0YWlsc1sxMCwxXSwgdmFyLmRldGFpbHNbMTEsMV0pLCBjYXB0aW9uID0gIlRhYmxlIDEzOg0KICAgICAgPGJyPlN1bW1hcnkgc3RhdGlzdGljcyBvZiBub3JtYWxpemVkIGZsaWdodCBkYXRhIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkgJT4lIA0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCg0KTWluLW1heCBub3JtYWxpemF0aW9uIHdhcyBjaG9zZW4gdG8gYWxsb3cgZm9yIGZ1dHVyZSB1c2Ugd2l0aCBmZWF0dXJlIG1hZ25pdHVkZSBzZW5zaXRpdmUgYWxnb3JpdGhtcywgc3VjaCBhcyBrIE5lYXJlc3QgTmVpZ2hib3JzIGNsdXN0ZXIgYW5hbHlzaXMuDQoNCg0KIyMgRmVhdHVyZSBTZWxlY3Rpb24NCg0KSW4gb3JkZXIgdG8gc2ltcGxpZnkgdGhlIGRhdGEgZm9yIGFuYWx5c2lzIGFuZCBwcmVkaWN0aXZlIG1vZGVsaW5nLCA8Yj5SZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoUkZFKTwvYj4gd2FzIGVtcGxveWVkIHV0aWxpemluZyB0aGUgY2FyZXQgcGFja2FnZS4gVGhpcyBhbGxvd3MgZm9yIHJlbW92YWwgb2YgcmVkdW5kYW5jeSBpbiB0aGUgZGF0YSBzZXQgdGhyb3VnaCBhIHJhbmRvbSBmb3Jlc3QgbW9kZWwgdXNpbmcgNSBpdGVyYXRpb25zIG9mIGNyb3NzIHZhbGlkYXRpb24gdG8gZGV0ZXJtaW5lIHdoaWNoIHZhcmlhYmxlcyBhcmUgbW9zdCBlc3NlbnRpYWwgZm9yIGNyZWF0aW5nIHByZWRpY3Rpb24gbW9kZWxzIGZvciBmbGlnaHQgZGVsYXlzLiBSZWR1Y2luZyByZWR1bmRhbmN5IG5vdCBvbmx5IHJlZHVjZXMgdGhlIGNvbXB1dGF0aW9uYWwgYnVyZGVuIG9mIHByZWRpY3RpdmUgbW9kZWxzIHdoaWxlIGFjY291bnRpbmcgZm9yIGFueSBwb3RlbnRpYWwgZmVhdHVyZSBpbnRlcmFjdGlvbnMgYmV0d2VlbiB2YXJpYWJsZXMuIEluIG9yZGVyIHRvIHByZXZlbnQgb3ZlciBmaXR0aW5nIHVzaW5nIFJGRSwgYW4gODA6MjAgdHJhaW5pbmc6dGVzdCBzZXQgd2FzIGNyZWF0ZWQgdXNpbmcgYGRlbGF5LmNhcmV0YCB0byBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIGZvciBwcmVkaWN0aW9uIG9mIG92ZXJhbGwgZmxpZ2h0IGRlbGF5cyAoYXJyX2RlbGF5KS4gDQoNCkEgdmlzdWFsaXphdGlvbiBvZiB0aGUgaW1wb3J0YW5jZSBvZiBlYWNoIHZhcmlhYmxlIGluIHBvdGVudGlhbCBwcmVkaWN0aW9uIG9mIG92ZXJhbGwgZmxpZ2h0IGRlbGF5cyBpcyBpbmNsdWRlZCBiZWxvdyBpbiBgRmlndXJlIDRgLiAgQXMgZGVtb25zdHJhdGVkIGJlbG93IHRoZXJlIGlzIGEgc3RlZXAgZHJvcCBvZmYgaW4gaW1wb3J0YW5jZSBmb3IgdG90YWwgY2xlYW5pbmcgdGltZSAoY2xlYW5pbmcpLCBmdWVsaW5nIHRpbWVzIChmdWVsaW5nKSwgJiBzZWN1cml0eSB0aW1lcyAoc2VjdXJpdHkpOyB0aGVyZWZvcmUgdGhlc2UgdmFyaWFibGVzIHdpbGwgYmUgb21pdHRlZCBmcm9tIGZ1dHVyZSBhbmFseXNpcyB0byByZWR1Y2UgY29tcHV0YXRpb25hbCBidXJkZW4gYW5kIHByZXZlbnQgcmVkdW5kYW5jaWVzLiAgDQoNCg0KYGBge3J9DQp4LmNhcmV0IDwtIGRlbGF5LnN0ZFssLThdDQp5LmNhcmV0IDwtIGRlbGF5LnN0ZFssOF0NCg0Kc2V0LnNlZWQoMTIzNDUpDQppblRyYWluIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeS5jYXJldCwgcCA9IDAuODAsIGxpc3QgPSBGQUxTRSlbLDFdDQoNCnguY2FyZXQudHJhaW4gPC0geC5jYXJldFtpblRyYWluLCBdDQp4LmNhcmV0LnRlc3QgPC0geC5jYXJldFstaW5UcmFpbiwgXQ0KeS5jYXJldC50cmFpbiA8LSB5LmNhcmV0W2luVHJhaW5dDQp5LmNhcmV0LnRlc3QgPC0geS5jYXJldFstaW5UcmFpbl0NCg0KDQpjb250cm9sIDwtIHJmZUNvbnRyb2woZnVuY3Rpb25zID0gcmZGdW5jcywgbWV0aG9kID0gImN2IiwgcmVwZWF0cyA9IDUsIG51bWJlciA9IDEwKQ0KDQpkZWxheS5jYXJldCA8LSByZmUoeCA9IHguY2FyZXQudHJhaW4sIHkuY2FyZXQudHJhaW4gLCBzaXplcyA9IGMoMTo1KSwgcmZlQ29udHJvbCA9IGNvbnRyb2wpDQpgYGANCg0KDQpgYGB7cn0NCmRlbGF5LmNhcmV0LnZhciA8LSBkYXRhLmZyYW1lKGZlYXR1cmUgPSByb3cubmFtZXModmFySW1wKGRlbGF5LmNhcmV0KSlbMTo5XSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltcG9ydGFuY2UgPSB2YXJJbXAoZGVsYXkuY2FyZXQpWzE6OSwgMV0pDQoNCmFjY3VyYWN5LmNhcmV0IDwtIHBvc3RSZXNhbXBsZShwcmVkaWN0KGRlbGF5LmNhcmV0LCB4LmNhcmV0LnRlc3QpLCB5LmNhcmV0LnRlc3QpDQoNCmdncGxvdChkZWxheS5jYXJldC52YXIsIGFlcyh4ID0gcmVvcmRlcihmZWF0dXJlLCAtaW1wb3J0YW5jZSksIHkgPSBpbXBvcnRhbmNlLCBmaWxsID0gZmVhdHVyZSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnMoeCA9ICJGZWF0dXJlcyIsIHkgPSAiVmFyaWFibGUgSW1wb3J0YW5jZSIsIHRpdGxlID0gIlZhcmlhYmxlIEltcG9ydGFuY2UgZm9yIEFsbCBGZWF0dXJlcyIsIGNhcHRpb24gPSAiRmlndXJlIDQ6DQogICAgICAgSW1wb3J0YW5jZSBvZiBlYWNoIHZhcmlhYmxlIGluIGdsb2JhbCBmbGlnaHQgZGF0YSBmb3IgcHJlZGljdGluZyBmbGlnaHQgZGVsYXlzIikgKyANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGltcG9ydGFuY2UsIDIpKSwgdmp1c3QgPSAwLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHRoZW1lX2J3KCkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KICANCmBgYA0KDQojIyBGZWF0dXJlIENyZWF0aW9uIA0KDQpJbiBvcmRlciB0byBwcm9wZXJseSB1dGlsaXplIHRoZSA2IG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcyBmb3IgcHJlZGljdGlvbiBvZiBvdmVyYWxsIGZsaWdodCBkZWxheXMgYXMgb3V0bGluZWQgYWJvdmUgaW4gYEZpZ3VyZSA0YCBhIHR3byBzdGVwIGZlYXR1cmUgY3JlYXRpb24gcHJvY2VzcyB3YXMgdXRpbGl6ZWQuICBJbml0aWFsbHkgYSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIHdhcyBjb25kdWN0ZWQgdG8gY29tYmluZSB0aGVzZSA2IHZhcmlhYmxlcyBpbnRvIHByaW5jaXBhbCBjb21wb25lbnRzIHRoYXQgY2FwdHVyZSBhIGNvbWJpbmF0aW9uIG9mIHRoZXNlIDYgdmFyaWFibGVzLiBVdGlsaXppbmcgdGhlIFBDQSBhcHByb2FjaCBhbGxvd3MgZm9yIGEgbW9yZSBzdHJlYW1saW5lZCBhcHByb2FjaCBmb3IgY3JlYXRpbmcgcHJlZGljdGlvbiBtb2RlbHMgZm9yIG92ZXJhbGwgZmxpZ2h0IGRlbGF5IHRpbWVzLiAgT25jZSB0cmFuc2Zvcm1lZCBpbnRvIHByaW5jaXBhbCBjb21wb25lbnRzLCBhIGstbWVhbnMgY2x1c3RlciBhbmFseXNpcyB3YXMgdGhlbiBjb25kdWN0ZWQgdG8gYWRkIGZvciBhbiBhZGRpdGlvbmFsIGZlYXR1cmUgdG8gdXRpbGl6ZSB3aGVuIGNyZWF0aW5nIGZsaWdodCBkZWxheSBwcmVkaWN0aW9uIG1vZGVscy4gDQoNCiMjIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzDQoNClV0aWxpemluZyB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIGFzIGlkZW50aWZpZWQgaW4gYEZpZ3VyZSA0YCAobnVtYmVyIG9mIGZsaWdodHMgcGVyIGFpcnBvcnQsIGxhdGUgaW5jb21pbmcgcGxhbmUsIGJhZ2dhZ2UgbG9hZGluZyB0aW1lLCBkaXN0YW5jZSBiZXR3ZWVuIGFpcnBvcnRzLCBzdXBwb3J0IGNyZXdzIGF2YWlsYWJsZSwgYW5kIHdlYXRoZXIpIGEgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyB3YXMgY29uZHVjdGVkIHRvIGZ1cnRoZXIgcmVkdWNlIHJlZHVuZGFuY2llcy4gQW4gaW5pdGlhbCBzY3JlZSBwbG90IChgRmlndXJlIDVgKSB3YXMgdXRpbGl6ZWQgdG8gZGV0ZXJtaW5lIHRoZSAiZWxib3ciIGN1dG9mZiBvZiBQQzEgJiBQQzIgd2hlcmUgdGhlIHZhcmlhbmNlIHN0ZWVwbHkgY3V0cyBvZmYuICBVcG9uIGNhbGN1bGF0aW9uIG9mIHRoZSB0b3RhbCBwZXJjZW50YWdlIG9mIHZhcmlhbmNlLCBpdCB3YXMgZm91bmQgdGhhdCBpbiBvcmRlciB0byBjcmVhdGUgYSBtb2RlbCB0aGF0IGFjY291bnRzIGZvciA+ODAlIG9mIHRoZSB2YXJpYW5jZSBpbiB0aGUgdG90YWwgZmxpZ2h0IGRlbGF5IGRhdGEsIHRoZSBmaXJzdCA0IFBDcyBtdXN0IGJlIHV0aWxpemVkLiANCg0KYGBge3J9DQpkZWxheS5zdGQua20gPC0gZGVsYXkuc3RkWywtYyg2LCA3LDkpXQ0KDQpwY2EuZGVsYXkgPC0gcHJjb21wKGRlbGF5LnN0ZC5rbVssLTZdLCBzY2FsZSA9IFRSVUUpDQpwY2EuZGVsYXkuMSA8LSBjYmluZChkZWxheS5zdGQua20sIHBjYS5kZWxheSR4KQ0KDQpwY2EudmFyIDwtIGFwcGx5KHBjYS5kZWxheSR4LCAyLCB2YXIpDQpgYGANCg0KYGBge3J9DQplaWdlbi5wY2EgPC0gc3VtbWFyeShwY2EuZGVsYXkpDQoNCg0KcGxvdChwY2EuZGVsYXksIHR5cGUgPSJsIiwgeWxpbT1jKDAsMyksIHhsaW09YygxLDYuNSksIG1haW4gPSBOVUxMKQ0KdGV4dCgoMToxMCkrMC4xNSwgcGNhLnZhciswLjIsIGFzLmNoYXJhY3Rlcihyb3VuZChwY2EudmFyLCAzKSksIGNleCA9IDAuOCkNCnRpdGxlKG1haW4gPSAiVmFyaWFuY2Ugb2YgSW5kaXZpZHVhbCBQQ3MiLCB4bGFiID0iUHJpbmNpcGFsIENvbXBvbmVudHMiLCBzdWIgPSAiRmlndXJlIDU6IFNjcmVlIFBsb3QgdG8gZGV0ZXJtaW5lIG51bWJlciBvZiBQQ3MgdG8gdXNlIikNCnBvaW50cyh4ID0gMiwgeSA9IChwY2EuZGVsYXkkc2RldlsyXSleMiwgcGNoID0gMTksIGNvbCA9ICJwdXJwbGUiLCBjZXggPSAxLjUpDQp0ZXh0KHggPSAyLCB5ID0gMC43LCAiNTguNjU2JQ0KICAgICBvZiBWYXIuIiwgY29sID0gInB1cnBsZSIsIGNleCA9IDAuOCkNCnBvaW50cyh4ID0gNCwgeSA9IChwY2EuZGVsYXkkc2Rldls0XSleMiwgcGNoID0gMTksIGNvbCA9ICJkYXJrcmVkIiwgY2V4ID0gMS41KQ0KdGV4dCh4ID0gNCwgeSA9IDAuNSwgIjg0LjYxMyUNCiAgICAgb2YgVmFyLiIsIGNvbCA9ICJkYXJrcmVkIiwgY2V4ID0gMC44KQ0KDQprYWJsZShwY2EuZGVsYXkkcm90YXRpb25bLGMoMTo1KV0sIGNhcHRpb24gPSAiVGFibGUgMTQ6IA0KICAgICAgPGJyPlByaW5jaXBhbCBDb21wb25lbnQgQ29lZmZpY2llbnRzIChyb3RhdGlvbiBtYXRyaXgpIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCg0Ka2FibGUoZWlnZW4ucGNhJGltcG9ydGFuY2UsIGNhcHRpb24gPSAiVGFibGUgMTU6IA0KICAgICAgPGJyPlZhcmlhbmNlIGNhcHR1cmVkIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCwgYXMgY2FsY3VsYXRlZCBieSB1c2luZyBlaWdlbnZhbHVlcyIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIyMgay1NZWFucyBDbHVzdGVyaW5nIEZlYXR1cmUgQ3JlYXRpb24NCg0KVXRpbGl6aW5nIHRoZSA1IGNyZWF0ZWQgcHJpbmNpcGFsIGNvbXBvbmVudHMsIGstTWVhbnMgY2x1c3RlcmluZyB3YXMgY29tcGxldGVkIHRvIGNyZWF0ZSBjbHVzdGVycyB0byBhaWQgaW4gdG90YWwgZmxpZ2h0IGRlbGF5IHByZWRpY3Rpb24uIEluIG9yZGVyIHRvIGRldGVybWluZSB0aGUgYmVzdCBmaXQgbnVtYmVyIG9mIGNsdXN0ZXJzLCBhIGB3c3NgIGFuZCBgc2lsaG91ZXR0ZWAgcGxvdHMgd2VyZSBjcmVhdGVkIGFzIGFuIGluaXRpYWwgYW5hbHlzaXMgb2YgdGhlIFBDQSBkYXRhLiBBcyBkZW1vbnN0cmF0ZWQgYmVsb3cgaW4gYEZpZ3VyZSA3YCB3YXMgZGV0ZXJtaW5lZCB0aGF0IHRoZSBiZXN0IGZpdCBudW1iZXIgb2YgY2x1c3RlcnMgZm9yIHRoaXMgdHJhbnNmb3JtZWQgUENBIGRhdGEgd2FzIDIgY2x1c3RlcnMuICBVdGlsaXppbmcgdGhhdCBwcmUtZGV0ZXJtaW5lZCBudW1iZXIgb2YgY2x1c3RlcnMsIHRoZSBQQ0EgZGF0YSB3YXMgdGhlbiBjbHVzdGVyZWQgaW50byAyIGdyb3VwcywgdXNpbmcgYSB0b3RhbCBvZiAyNSBydW4tdGhyb3VnaHMgb2YgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyB0byBiZXN0IGRldGVybWluZSB0aGUgY2VudGVyIHBvaW50IG9mIHRoZSBjbHVzdGVycyBhbmQgcmVkdWNlIG92ZXJhbGwgY2x1c3RlciBvdmVybGFwLiANCg0KYGBge3J9DQoNCmttZWFucy53c3MgPC0gZnZpel9uYmNsdXN0KHBjYS5kZWxheS4xWy1jKDE6NywgMTI6MTMpXSwgRlVOID0gaGN1dCwgbWV0aG9kID0gIndzcyIpDQprbWVhbnMuc2lsIDwtIGZ2aXpfbmJjbHVzdChwY2EuZGVsYXkuMVstYygxOjcsIDEyOjEzKV0sIEZVTiA9IGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIikNCmBgYA0KDQpgYGB7cn0NCmdyaWQuYXJyYW5nZSgNCiAgYXJyYW5nZUdyb2Ioa21lYW5zLndzcywga21lYW5zLnNpbCwgbmNvbCA9IDIsbnJvdyA9IDEpLA0KICB0b3AgPSB0ZXh0R3JvYigiT3B0aW1hbCBDbHVzdGVyIERldGVybWluYXRpb24iLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpKSwNCiAgYm90dG9tID0gdGV4dEdyb2IoIg0KICBGaWd1cmUgNjoNCiAgRGV0ZXJtaW5hdGlvbiBvZiBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBmb3IgUENBIG92ZXJhbGwgZmxpZ2h0IGRhdGEiKSkNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMzQ1KQ0KcGNhLmRlbGF5LjEkY2x1c3RlciA8LSBrbWVhbnMocGNhLmRlbGF5LjFbLWMoMTo3LCAxMjoxMyldLCBjZW50ZXJzID0gIDIsIG5zdGFydCA9IDI1KSRjbHVzdGVyDQpwY2EuZGVsYXkuMSRjZW50ZXJzIDwtIGttZWFucyhwY2EuZGVsYXkuMVstYygxOjcsIDEyOjEzKV0sIGNlbnRlcnMgPSAgMiwgbnN0YXJ0ID0gMjUpJGNlbnRlclsxXQ0KYGBgDQoNCg0KYGBge3J9DQplaWdlbiA8LSByb3VuZChnZXRfZWlnZW52YWx1ZShwY2EuZGVsYXkpLCAxKQ0KdmFyLmVpZ2VuIDwtIGVpZ2VuJHZhcmlhbmNlLnBlcmNlbnQNCmNsdXN0ZXJfY2VudGVycyA8LSBhZ2dyZWdhdGUocGNhLmRlbGF5LjEsIGJ5PWxpc3QoY2x1c3RlciA9IHBjYS5kZWxheS4xJGNsdXN0ZXIpLCBGVU4gPSBtZWFuKQ0KcGNhLmRlbGF5LjEkY2x1c3RlciA8LSBhcy5mYWN0b3IocGNhLmRlbGF5LjEkY2x1c3RlcikNCmBgYA0KDQpgYGB7cn0NCmdnc2NhdHRlcihwY2EuZGVsYXkuMSwgeCA9ICJQQzEiLCB5ID0gIlBDMiIsDQogICAgICAgICAgY29sb3IgPSAiY2x1c3RlciIsDQogICAgICAgICAgcGFsZXR0ZSA9ICJucGciLA0KICAgICAgICAgIGVsbGlwc2UgPSBUUlVFLA0KICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLA0KICAgICAgICAgIGxlZ2VuZCA9ICJyaWdodCIsDQogICAgICAgICAgeGxhYiA9IHBhc3RlKCJQQzEgKCIsdmFyLmVpZ2VuWzFdLCIlKSIpLA0KICAgICAgICAgIHlsYWIgPSBwYXN0ZSgiUEMyICgiLCB2YXIuZWlnZW5bMl0sIiUpIikpICsNCiAgZ2VvbV9wb2ludCh4ID0gY2x1c3Rlcl9jZW50ZXJzJFBDMVsxXSwgeSA9IGNsdXN0ZXJfY2VudGVycyRQQzJbMV0sIHNoYXBlID0gMTgsIHNpemUgPSA1LCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV9wb2ludCh4ID0gY2x1c3Rlcl9jZW50ZXJzJFBDMVsyXSwgeSA9IGNsdXN0ZXJfY2VudGVycyRQQzJbMl0sIHNoYXBlID0gMTgsIHNpemUgPSA1LCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJDbHVzdGVyIEFuYWx5c2lzIiwgY2FwdGlvbiA9ICINCiAgRmlndXJlIDc6DQogIENsdXN0ZXJpbmcgb2YgUEMxICYgUEMyIHVzaW5nIGstTWVhbnMgQ2x1c3RlciBhbmFseXNpcy4gQmxhY2sgZGlhbW9uZHMgcmVwcmVzZW50IGNsdXN0ZXIgbWVhbnMuIikgKyANCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmBgYA0KDQoNCg0KIyBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCBmb3IgQXJyaXZhbCBEZWxheSBQcmVkaWN0aW9uDQoNCiMjIFJlZ3Jlc3Npb24gTW9kZWwgQ3JlYXRpb24NCg0KSW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHRoZSBiZXN0IGxpbmVhciByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwgZm9yIHVzZSBpbiBkZXRlcm1pbmluZyBzdGFmZiByZXNvdXJjaW5nIGRlY2lzaW9ucyB0byByZWR1Y2UgZ2xvYmFsIGRlbGF5IHRpbWVzLiBUaGUgZm9sbG93aW5nIG1vZGVscyB3ZXJlIGRldmVsb3BlZCBhbmQgYW5hbHl6ZWQgdG8gZGV0ZXJtaW5lIHRoZSBiZXN0IGZpdCBtb2RlbCB0byBjb25kdWN0IHRoZXNlIHRlc3RzIGFuZCBkZXRlcm1pbmF0aW9uczoNCg0KICAtIDxiPk1vZGVsIDE6PC9iPiBGdWxsIHJlZ3Jlc3Npb24gbW9kZWwgKE92ZXJhbGwgRmxpZ2h0IERlbGF5IH4gQWxsIFZhcmlhYmxlcyBpbiBgRmluYWwuRGVsYXlgIGRhdGEgc2V0KQ0KICAgIC0gVGhlIGZ1bGwgbW9kZWwgd2FzIHNlbGVjdGVkIHRvIHNlcnZlIGFzIGFuIGluaXRpYWwgYW5hbHlzaXMgYW5kIHBvaW50IG9mIHJlZmVyZW5jZSBmb3IgbW9kZWxzIDIgLSA0Lg0KICAtIDxiPk1vZGVsIDI6PC9iPiBQQ0EgJiBDbHVzdGVyIHJlZ3Jlc3Npb24gbW9kZWwgKE92ZXJhbGwgRmxpZ2h0IERlbGF5IH4gUEMxICsgUEMyICsgay1NZWFucyBDbHVzdGVyIElEKQ0KICAgIC0gVGhlIFBDQS1DbHVzdGVyIG1vZGVsIHdhcyBzZWxlY3RlZCBhcyBQQ0EgYWNjb3VudHMgZm9yIGFsbCB2YXJpYWJsZXMgaW4gdGhlIGZ1bGwgbW9kZWwgd2l0aCBwcmVsaW1pbmFyeSB0cmFuc2Zvcm1hdGlvbiB0byBiZXN0IGFsaWduIHRoZSBmdWxsIG1vZGVsIHZhcmlhYmxlcyBmb3Igb3B0aW1pemVkIE92ZXJhbGwgRmxpZ2h0IERlbGF5IHByZWRpY3Rpb24uICBBZGRpdGlvbmFsbHkgYnkgdXRpbGl6aW5nIHRoZSBrLU1lYW5zIGNsdXN0ZXIgSUQgYm90aCBjcmVhdGVkIHZhcmlhYmxlcyBhcmUgcHJlc2VudCBpbiB0aGlzIG1vZGVsIHdpdGggcmVkdWNlZCByZWR1bmRhbmN5IHRoYW4gd291bGQgYmUgZm91bmQgaW4gYSBtb2RlbCBjb250YWluaW5nIG11bHRpcGxlIFBDcw0KICAtIDxiPk1vZGVsIDM6PC9iPiBTdGVwLXdpc2UgYXV0b21hdGVkIHNlbGVjdGlvbiByZWdyZXNzaW9uIG1vZGVsIChPdmVyYWxsIEZsaWdodCBEZWxheSB+IEFpcnBvcnQgRGlzdGFuY2UgKyAgTnVtYmVyIG9mIGZsaWdodHMgKyBXZWF0aGVyICsgU3VwcG9ydCBDcmV3IEF2YWlsYWJsZSArIExhdGUgIEluY29taW5nIFBsYW5lIEFycml2YWwgKyBCYWdnYWdlIExvYWRpbmcgVGltZSArIENsdXN0ZXIpDQogICAgLSBUaGUgc3RlcC13aXNlIGF1dG9tYXRlZCBzZWxlY3Rpb24gbW9kZWwgd2FzIGNob3NlbiB0byByZW1vdmUgcG90ZW50aWFsIGh1bWFuIGJpYXMgYW5kIGVycm9yIGluIGNyZWF0aW5nIHRoZSBiZXN0IGZpdCBwcmVkaWN0aW9uIG1vZGVsLiAgQm90aCBmb3J3YXJkIGFuZCBiYWNrd2FyZHMgc3RlcC13aXNlIGFuYWx5c2lzIHdhcyBjb21wbGV0ZWQgdG8gaWRlbnRpZnkgdGhlIG9wdGltYWwgbW9kZWwgZm9yIHByZWRpY3Rpb24uDQogIC0gPGI+TW9kZWwgNDo8L2I+IFJlY3Vyc2l2ZSBmZWF0dXJlIGVsaW1pbmF0aW9uIHJlZ3Jlc3Npb24gbW9kZWwgKE92ZXJhbGwgRmxpZ2h0IERlbGF5IH4gTGF0ZSBJbmNvbWluZyBQbGFuZXMgKyBOdW1iZXIgb2YgRmxpZ2h0cyArIEJhZ2dhZ2UgTG9hZGluZyBUaW1lcyArIGstTWVhbnMgQ2x1c3RlciBJRCkNCiAgICAtIFRoZSBSRkUgbW9kZWwgd2FzIGNyZWF0ZWQgdXNpbmcgdGhlIHRvcCA0IHZhcmlhYmxlcyBpZGVudGlmaWVkIGFzIGltcG9ydGFudCBpbiBhcnJpdmFsIGRlbGF5IHByZWRpY3Rpb24gYXMgcHJldmlvdXNseSBpZGVudGlmaWVkIGluIGBGaWd1cmUgNGANCg0KDQoNCmBgYHtyfQ0KDQpmaW5hbC5kZWxheSA8LSBwY2EuZGVsYXkuMVstYygxMjoxMywgMTUpXQ0KZmluYWwuZGVsYXkkY2x1c3RlciA8LSBhcy5udW1lcmljKGZpbmFsLmRlbGF5JGNsdXN0ZXIpDQoNCmxtLmRlbGF5LmZ1bGwgPC0gbG0oYXJyX2RlbGF5IH4gLiwgZGF0YSA9IGZpbmFsLmRlbGF5KQ0KbG0uZGVsYXkuc3RlcCA8LSBzdGVwKGxtLmRlbGF5LmZ1bGwsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAwKQ0KYGBgDQoNCiMjIE1vZGVsIENvbXBhcnJpc29uIFRocm91Z2ggQ3Jvc3MgVmFsaWRhdGlvbg0KDQpXaXRoIGFsbCBmb3VyIHBvdGVudGlhbCByZWdyZXNzaW9uIG1vZGVscyBjcmVhdGVkLCA1LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiB3YXMgY29uZHVjdGVkIHRvIGNhbGN1bGF0ZSB0aGUgTWVhbiBTcXVhcmUgRXJyb3IgKE1TRSkgb2YgZWFjaCB2YWxpZGF0aW9uIHJ1biwgZm9sbG93ZWQgYnkgdGhlIG1lYW4gTVNFIG9mIGFsbCB2YWxpZGF0aW9uIHJ1bnMgcGVyIHByZWRpY3Rpb24gbW9kZWwuICBUaGUgZGF0YSB3YXMgc3BsaXQgODA6MjAgaW50byB0cmFpbjp0ZXN0IGRhdGEgc3Vic2V0cyByZXNwZWN0aXZlbHksIHV0aWxpemluZyB0aGUgdHJhaW5pbmcgc3Vic2V0cyBmb3IgdGhlIGNyb3NzIHZhbGlkYXRpb24gcHJvY2Vzcy4gVGhyb3VnaCBjb21wYXJpbmcgdGhlIGNhbGN1bGF0ZWQgdHJhaW5pbmcgZGF0YSBtZWFuIE1TRSB2YWx1ZXMsIHRoZSBiZXN0IGZpdCBwcmVkaWN0aW9uIG1vZGVsIHdhcyBzZWxlY3RlZCBmb3IgZnVydGhlciB0ZXN0aW5nIHV0aWxpemluZyB0aGUgdGVzdCBkYXRhLiAgQXMgTVNFIHJlcHJlc2VudHMgdGhlIG1lYW4gc3F1YXJlZCBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBtb2RlbCdzIHByZWRpY3RlZCB2YWx1ZSBhbmQgdGhlIGFjdHVhbCBhc3NvY2lhdGVkIGRhdGEgdmFsdWUsIE1TRSBjYW4gYmUgdXRpbGl6ZWQgdG8gY29tcGFyZSB0aGUgcHJlZGljdGlvbiBhY2N1cmFjeSBvZiBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgYW5kIGlkZW50aWZ5IHRoZSBiZXN0IG1vZGVsIGZvciBzZWxlY3Rpb24gKGxvd2VyIE1TRSA9IG1vcmUgYWNjdXJhdGUgbW9kZWwpLg0KDQpBcyBpZGVudGlmaWVkIGJlbG93IGluIGBGaWd1cmUgOGAgYW5kIGBUYWJsZSAxNmAsIG1vZGVsIDIgcmV0dXJuZWQgdGhlIGxvd2VzdCBtZWFuIE1TRSBhbmQgbG93ZXN0IGluZGl2aWR1YWwgTVNFIHZhbHVlcyBmb3IgZWFjaCBjcm9zcy12YWxpZGF0aW9uIGZvbGQ7IHRoZXJlZm9yZSBtb2RlbCAyIChQQ0EgbW9kZWwpIHdhcyBzZWxlY3RlZCBhcyB0aGUgcHJpbWFyeSBwcmVkaWN0aW9uIG1vZGVsIGZvciBsaXZlIG92ZXJhbGwgYXJyaXZhbCBkZWxheSB0aW1lcy4gIEFzIGRpc2N1c3NlZCBlYXJsaWVyIGluIHNlY3Rpb24gMy4zLjEgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcywgZWFjaCBwcmluY2lwYWwgY29tcG9uZW50IGNvbnRhaW5zIGEgZmluZWx5IHR1bmVkIHRyYW5zZm9ybWF0aW9uIG9mIGFsbCBpbXBvcnRhbnQgdmFyaWFibGVzIGluIHRoZSBnbG9iYWwgZmxpZ2h0IGRlbGF5IGRhdGEuIFRoaXMgaG9saXN0aWMgYXBwcm9hY2ggc3VwcG9ydHMgdGhlIHJldHVybmVkIHJlc3VsdHMgb2YgbW9kZWwgMiByZXR1cm5pbmcgdGhlIGxvd2VzdCBNU0UgYXMgdGhlIFBDQSBjb21wb25lbnQgb2YgdGhpcyBtb2RlbCB3YXMgcHJlbGltaW5hcnkgdHVuZWQgZm9yIHRoaXMgcHVycG9zZS4gQWRkaXRpb25hbGx5IHRoZSBrLU1lYW5zIGNsdXN0ZXIgSUQgd2FzIGNyZWF0ZWQgdXRpbGl6aW5nIG9ubHkgdGhlIGNyZWF0ZWQgUENBIGNvbXBvbmVudHMsIGZ1cnRoZXIgZW5zdXJpbmcgYWxsIGFzcGVjdHMgb2YgdGhlIG5vcm1hbGl6ZWQgZGF0YSBpcyByZXByZXNlbnRlZCBpbiB0aGUgcmVncmVzc2lvbiBtb2RlbCB3aXRoIG1pbmltYWwgbm9pc2UuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KI3NodWZmbGluZyB0aGUgZGF0YQ0KbiA8LSBkaW0oZmluYWwuZGVsYXkpWzFdICNzZXQgc2FtcGxlIHNpemUgIA0Kb2JzLklEIDwtIDE6biAgICNjcmVhdGUgb2JzZXJ2YXRpb24gSUQgdG8gYWxsb3cgZm9yIHJhbmRvbSBzcGxpdHRpbmcNCm4udHJhaW4gPC0gcm91bmQoMC44Km4pICAjY3JlYXRlIHNhbXBsZSBzaXplIGZvciB0cmFpbmluZyBkYXRhLiByb3VuZCB0byBrZWVwIHdob2xlIG51bWJlciBzaW5jZSBubyBwYXJ0aWFsIG9ic2VydmF0aW9ucw0KDQpzaHVmZmxlLmlkICA8LSAgc2FtcGxlKG9icy5JRCwgbiwgcmVwbGFjZSA9IEZBTFNFKSAgI3JhbmRvbWl6ZSBjcmVhdGVkIG9ic2VydmF0aW9uIElELCBkb2VzIG5vdCBhbGxvdyBmb3IgZHVwZXMgaW4gbGlzdC4NCg0Kc2h1ZmZsZS5kZWxheSA8LSBmaW5hbC5kZWxheVtzaHVmZmxlLmlkLCBdICNvcmRlcnMgcm93cyBvZiBkYXRhZnJhbWUgdG8gYmUgaW4gb3JkZXIgb2Ygc2h1ZmZsZS5pZCBjcmVhdGVkIHJhbmRvbSBvYnMuaWQgbGlzdA0KDQojc3BsaXR0aW5nIHRoZSBkYXRhDQoNCnRyYWluLmRhdGEgPC0gc2h1ZmZsZS5kZWxheVsxOm4udHJhaW4sIF0gI3NwbGl0dGluZyBzaHVmZmxlZCBkYXRhIGZyb20gMXN0IG9icyB0byBlbmQgb2YgdHJhaW5pbmcgZGF0YSAoODAlKQ0KdGVzdC5kYXRhIDwtIHNodWZmbGUuZGVsYXlbKG4udHJhaW4gKyAxKTpuLCBdICNzcGxpdHRpbmcgc2h1ZmZsZWQgZGF0YSBmcm9tIDFzdCBvYnMgYWZ0ZXIgZW5kIG9mIHRyYWluaW5nIGRhdGEgdG8gZW5kIG9mIHJlbWFpbmluZyBkYXRhICh0ZXN0KQ0KDQpuLmZvbGQgPC0gcm91bmQobi50cmFpbi81KS0xICAgI251bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBvZiB0aGUgNSBmb2xkcyBvZiB2YWxpZGF0aW9uDQoNCg0KI3ZhbGlkYXRpb24gbG9vcCAtIDUgZm9sZA0KDQptc2UubGluLjEgPC0gcmVwKDAsNSkNCm1zZS5saW4uMiA8LSByZXAoMCw1KQ0KbXNlLmxpbi4zIDwtIHJlcCgwLDUpDQptc2UubGluLjQgPC0gcmVwKDAsNSkNCg0KZm9yIChpIGluIDE6NSl7DQogIHZhbGlkLmlkID0gKChpLTEpICogbi5mb2xkICsgMSk6KDEgKiBuLmZvbGQpICAjYnJlYWtzIGRvd24gZm9sZHMgaW50byA1IGdyb3VwcyANCiAgbGluLnRyYWluID0gdHJhaW4uZGF0YVstdmFsaWQuaWQsIF0NCiAgbGluLnZhbGlkID0gdHJhaW4uZGF0YVt2YWxpZC5pZCwgXQ0KICANCiAgI2J1aWxkaW5nIG1vZGVscyB0byBydW4gb2ZmIG9mIGZvbGRlZCBkYXRhDQogIG0xID0gbG0oYXJyX2RlbGF5IH4gLiAsIGRhdGEgPSBsaW4udHJhaW4pDQogIG0yID0gbG0oYXJyX2RlbGF5IH4gUEMxICsgY2x1c3RlciwgZGF0YSA9IGxpbi50cmFpbikNCiAgbTMgPSBsbShhcnJfZGVsYXkgfiBhaXJwb3J0X2Rpc3QgKyBudW1fZmxpZ2h0cyArIHdlYXRoZXIgKyBzdXBfY3JldyArIGxhdGVfaW5jb21pbmcgKyBiYWdnYWdlICsgY2x1c3RlciwgZGF0YSA9IGxpbi50cmFpbikNCiAgbTQgPSBsbShhcnJfZGVsYXkgfiBsYXRlX2luY29taW5nICsgbnVtX2ZsaWdodHMgKyBiYWdnYWdlICsgY2x1c3RlciwgZGF0YSA9IGxpbi50cmFpbikNCiAgDQogICNwcmVkaWN0aW5nIGZsaWdodCBkZWxheSB1c2luZyBtb2RlbHMNCiAgcHJlZG0xID0gcHJlZGljdChtMSwgbmV3ZGF0YSA9IGxpbi50cmFpbikNCiAgcHJlZG0yID0gcHJlZGljdChtMiwgbmV3ZGF0YSA9IGxpbi50cmFpbikNCiAgcHJlZG0zID0gcHJlZGljdChtMywgbmV3ZGF0YSA9IGxpbi50cmFpbikNCiAgcHJlZG00ID0gcHJlZGljdChtNCwgbmV3ZGF0YSA9IGxpbi50cmFpbikNCiAgDQogICNjYWxjdWxhdGUgbWVhbiBzcXVhcmUgZXJyb3IgYmV0d2VlbiBwcmVkaWN0ZWQgdmFsdWVzIGFuZCB2YWx1ZXMgZnJvbSB2YWxpZGF0aW9uIGRhdGENCiAgbXNlLmxpbi4xW2ldID0gbWVhbigocHJlZG0xIC0gbGluLnZhbGlkJGFycl9kZWxheSleMikNCiAgbXNlLmxpbi4yW2ldID0gbWVhbigocHJlZG0yIC0gbGluLnZhbGlkJGFycl9kZWxheSleMikNCiAgbXNlLmxpbi4zW2ldID0gbWVhbigocHJlZG0zIC0gbGluLnZhbGlkJGFycl9kZWxheSleMikNCiAgbXNlLmxpbi40W2ldID0gbWVhbigocHJlZG00IC0gbGluLnZhbGlkJGFycl9kZWxheSleMikNCn0NCg0KbC5tc2UuMSA9IG1lYW4obXNlLmxpbi4xKQ0KbC5tc2UuMiA9IG1lYW4obXNlLmxpbi4yKQ0KbC5tc2UuMyA9IG1lYW4obXNlLmxpbi4zKQ0KbC5tc2UuNCA9IG1lYW4obXNlLmxpbi40KQ0KYGBgDQoNCmBgYHtyfQ0KbXNlLm1vZDEgPC0gZGF0YS5mcmFtZSAoDQogIHggPSAxOjUsDQogIHkgPSBtc2UubGluLjENCikNCm1zZS5tb2QyIDwtIGRhdGEuZnJhbWUgKA0KICB4ID0gMTo1LA0KICB5ID0gbXNlLmxpbi4yDQopDQptc2UubW9kMyA8LSBkYXRhLmZyYW1lICgNCiAgeCA9IDE6NSwNCiAgeSA9IG1zZS5saW4uMw0KKQ0KbXNlLm1vZDQgPC0gZGF0YS5mcmFtZSAoDQogIHggPSAxOjUsDQogIHkgPSBtc2UubGluLjQNCikNCm1zZS5tb2QxLnBsb3QgPC0gZ2dwbG90IChtc2UubW9kMSwgYWVzKHggPSB4LCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IG1zZS5tb2QxJHgsIG1zZS5tb2QxJHksIGNvbG9yID0gIk1vZGVsIDEiKSkrDQogIGdlb21fbGluZShhZXMoeCA9IG1zZS5tb2QxJHgsIG1zZS5tb2QxJHksIGNvbG9yID0gIk1vZGVsIDEiKSkgKw0KICBnZW9tX3BvaW50IChhZXMgKHggPSBtc2UubW9kMiR4LCB5ID0gbXNlLm1vZDIkeSwgY29sb3IgPSAiTW9kZWwgMiIpKSArIA0KICBnZW9tX2xpbmUgKGFlcyAoeCA9IG1zZS5tb2QyJHgsIHkgPSBtc2UubW9kMiR5LCBjb2xvciA9ICJNb2RlbCAyIikpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IG1zZS5tb2QzJHgsIHkgPSBtc2UubW9kMyR5LCBjb2xvciA9ICJNb2RlbCAzIikpKyANCiAgZ2VvbV9saW5lIChhZXMoeCA9IG1zZS5tb2QzJHgsIHkgPSBtc2UubW9kMyR5LCBjb2xvciA9ICJNb2RlbCAzIiksIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gbXNlLm1vZDQkeCwgeSA9IG1zZS5tb2Q0JHksIGNvbG9yID0gIk1vZGVsIDQiKSkrIA0KICBnZW9tX2xpbmUgKGFlcyh4ID0gbXNlLm1vZDQkeCwgeSA9IG1zZS5tb2Q0JHksIGNvbG9yID0gIk1vZGVsIDQiKSkgKw0KICBsYWJzICh4ID0gIlZhbGlkYXRpb24gRm9sZCIsIHkgPSAiQ2FsY3VsYXRlZCBNU0UiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCAodmFsdWVzID0gYygiTW9kZWwgMSIgPSAiYmxhY2siLCAiTW9kZWwgMiIgPSAiYmx1ZSIsICJNb2RlbCAzIiA9ICJkZWVwcGluayIsICJNb2RlbCA0IiA9ICJvcmFuZ2U0IiApKSsNCiAgdGhlbWVfYncoKQ0KDQpsLm1zZSA8LSBkYXRhLmZyYW1lKA0KICBtb2RlbDEgPSBsLm1zZS4xLA0KICBtb2RlbDIgPSBsLm1zZS4yLA0KICBtb2RlbDMgPSBsLm1zZS4zLA0KICBtb2RlbDQgPSBsLm1zZS40DQopDQoNCmBgYA0KDQpgYGB7cn0NCg0KZ3JpZC5hcnJhbmdlKA0KICBhcnJhbmdlR3JvYihtc2UubW9kMS5wbG90LCBuY29sID0gMSxucm93ID0gMSksDQogIHRvcCA9IHRleHRHcm9iKCJQcmVkaWN0aW9uIE1vZGVsIENyb3NzIFZhbGlkYXRpb24iLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpKSwNCiAgYm90dG9tID0gdGV4dEdyb2IoIg0KICBGaWd1cmUgODoNCiAgQ29tcGFycmlzb24gb2YgbW9kZWwgTWVhbiBTcXVhcmUgRXJyb3IgdmFsdWVzIGFjcm9zcyA1LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiIpKQ0KDQprYWJsZShsLm1zZSwgY29sLm5hbWVzID0gYygiTW9kZWwgMSAoRnVsbCkiLCAiTW9kZWwgMiAoUENBKSIsICJNb2RlbCAzIChTdGVwLXdpc2UpIiwgIk1vZGVsIDQgKFJGRSkiKSwgY2FwdGlvbiA9ICJUYWJsZSAxNjoNCiAgICAgIDxicj5NZWFuIE1TRSB2YWx1ZXMgZm9yIGVhY2ggbW9kZWwgY2FsY3VsYXRlZCBhY3Jvc3MgNSBjcm9zcy12YWxpZGF0aW9uIHJ1bnMiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCg0KIyMgUENBIE1vZGVsIEFuYWx5c2lzICYgRXZhbHVhdGlvbg0KDQpPbmNlIHNlbGVjdGVkIGFzIHRoZSBiZXN0IGZpdCBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsLCB0aGUgUENBIG1vZGVsIChgTW9kZWwgMmApIHdhcyByZS1ldmFsdWF0ZWQgdXRpbGl6aW5nIHRoZSB0ZXN0IGRhdGEgcmF0aGVyIHRoYW4gdGhlIHRyYWluaW5nIGRhdGEgdG8gZW5zdXJlIGFjY3VyYWN5IGFuZCBlbnN1cmUgdGhlIG1vZGVsIHdhcyBub3Qgb3Zlci1maXR0ZWQuIEFzIGEgcGFydCBvZiB0aGUgZGV2ZWxvcG1lbnQgb2YgZWFjaCBQQ0EsIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMgd2VyZSBsaW5lYXJseSBjb21iaW5lZCB0byBjcmVhdGUgdGhlc2UgcHJpbmNpcGFsIGNvbXBvbmVudHMsIGVmZmVjdGl2ZWx5IHNldHRpbmcgZWFjaCBQQyB0byBiZSBhIHVuaXF1ZSBjb21iaW5hdGlvbiBvZiA2IG5vcm1hbGl6ZWQgdmFyaWFibGVzIChBaXJwb3J0IERpc3RhbmNlLCBOdW1iZXIgb2YgRmxpZ2h0cywgV2VhdGhlciwgU3VwcG9ydCBDcmV3cyBBdmFpbGFibGUsIExhdGUgSW5jb21pbmcgUGxhbmUgQXJyaXZhbCwgJiBCYWdnYWdlIExvYWRpbmcgVGltZSkuICANCg0KQXMgaWRlbnRpZmllZCBpbiBgVGFibGUgMTdgIGJlbG93LCBlYWNoIGRlcGVuZGVudCB2YXJpYWJsZSByZXR1cm5lZCBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcCB2YWx1ZSBvZiB3ZWxsIGJlbG93IDAuMDUgd2hlbiBgTW9kZWwgMmAgd2FzIHJ1biB1dGlsaXppbmcgdGhlIHRlc3QgZGF0YSBhcyB3ZWxsIGFzIHRoZS4gIFRoaXMgaGFzIGVuc3VyZWQgdGhhdCBhbGwgdmFyaWFibGVzIHByZXNlbnQgaW4gdGhlIHJlZ3Jlc3Npb24gbW9kZWwgYXJlIHNpZ25pZmljYW50IHRvIHRoZSBwcmVkaWN0aW9uIG1vZGVsIGFuZCBhcmUgbm90IGNvbnRyaWJ1dGluZyBub2lzZSBvciByZWR1bmRhbmN5IHRvIHRoZSBtb2RlbC4gDQoNCmBgYHtyfQ0KDQpsbS5wY2EudHJhaW4gPC0gbG0oYXJyX2RlbGF5IH4gUEMxICsgY2x1c3RlciwgZGF0YSA9IHRyYWluLmRhdGEpDQpsbS5wY2EudGVzdCA8LSB0ZXN0LmRhdGENCmxtLnBjYS50ZXN0JGFycl9kZWxheV9wcmVkIDwtIHByZWRpY3QobG0ucGNhLnRyYWluLCBuZXdkYXRhID0gbG0ucGNhLnRlc3QpDQpsbS5wY2EgPC0gZmluYWwuZGVsYXkNCmxtLnBjYSRhcnJfZGVsYXlfcHJlZCA8LSBwcmVkaWN0KGxtLnBjYS50cmFpbiwgbmV3ZGF0YSA9IGxtLnBjYSkNCg0KcnNxdWFyZXMubGluIDwtIGRhdGEuZnJhbWUoDQogIERhdGEgPSBjKCJUcmFpbmluZyBEYXRhIiwgIlRlc3QgRGF0YSIsICJGdWxsIERlbGF5IERhdGEiKSwNCiAgUi5zcXVhcmVkID0gYyhzdW1tYXJ5KGxtLnBjYS50cmFpbikkci5zcXVhcmVkICwoY29yKGxtLnBjYS50ZXN0JGFycl9kZWxheSwgbG0ucGNhLnRlc3QkYXJyX2RlbGF5X3ByZWQpKSoqMiwoY29yKGxtLnBjYSRhcnJfZGVsYXksIGxtLnBjYSRhcnJfZGVsYXlfcHJlZCkpKioyKQ0KKQ0Kc2hyaW5rYWdlLmxpbiA8LSByc3F1YXJlcy5saW4NCnNocmlua2FnZS5saW4kU2hyaW5rYWdlIDwtIGMoIiIsIHJzcXVhcmVzLmxpblsxLDJdIC0gcnNxdWFyZXMubGluWzIsMl0sIHJzcXVhcmVzLmxpblsxLDJdIC0gcnNxdWFyZXMubGluWzMsMl0pDQoNCnNocmlua2FnZS5saW4kcmVseSA8LSBOVUxMDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzaHJpbmthZ2UubGluKSl7DQogIGlmKHNocmlua2FnZS5saW5baSwzXSA9PSAiIil7DQogICAgbiA8LSAgIiJ9DQogIGVsc2UgaWYoc2hyaW5rYWdlLmxpbltpLDNdIDwgMC4xKXsNCiAgICBuIDwtICAiUmVsaWFibGUgTW9kZWwifQ0KICBlbHNlIGlmKHNocmlua2FnZS5saW5baSwzXSA+IDAuOSl7DQogICAgbiA8LSAgIlZlcnkgVW5yZWxpYWJsZSJ9DQogIGVsc2Uge24gIDwtICAiUG90ZW50aWFsbHkgUmVsaWFibGUifQ0KICANCiAgc2hyaW5rYWdlLmxpbiRyZWx5W2ldIDwtIG4gIA0KfQ0KDQoNCg0Ka2FibGUoc3VtbWFyeShsbS5wY2EudHJhaW4pJGNvZWZmaWNpZW50cywgY29sLm5hbWVzID0gYygiRXN0aW1hdGUiLCAiU3RkLiBFcnJvciIsICJ0IHZhbHVlIiwgIlByKD4gSSB0IEkpIiksIGNhcHRpb24gPSAiVGFibGUgMTc6DQogICAgICA8YnI+UmVncmVzc2lvbiBjb2VmZmljaWVudHMgZm9yIGxpbmVhciByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwgMiAoUENBKSBydW4gb24gPGI+dGVzdCBkYXRhPC9iPiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCmBgYA0KDQpVdGlsaXppbmcgdGhlIGFib3ZlIG91dGxpbmVkIGZpbmFsIHRlc3QgZGF0YSBhbmQgZnVsbCBkYXRhIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGZyb20gdXNpbmcgdGhlIGBNb2RlbCAyYCBwcmVkaWN0aW9uIG1vZGVsLCB0aGUgYE1vZGVsIDJgIGVxdWF0aW9ucyBjYW4gYmUgZGVmaW5lZCBhczogDQoNCiQkDQpcYmVnaW57YWxpZ24qfQ0KQXJyaXZhbCBcIERlbGF5ICZcc2ltIFBDMSBcICsgXCBDbHVzdGVyIFwgKyBcIEludGVyY2VwdFxcIFxcDQpBcnJpdmFsIFwgRGVsYXkgXCBfe3RyYWlufSAmXHNpbSAwLjA5MDIxNzQqUEMxIFwgKyBcIDAuMDE2MzAzMSpDbHVzdGVyIFwgKyBcIDAuMzY2NTY0OCANClxlbmR7YWxpZ24qfQ0KJCQNCg0KQXMgY2FwdHVyZWQgYmVsb3cgaW4gYFRhYmxlIDE4YCB0aGUgY2FsY3VsYXRlZCAkUl4yJCB2YWx1ZXMgZm9yIGBNb2RlbCAyYCBhcyBmaXQgdG8gdGhlIHRyYWluaW5nIGRhdGEsIHRlc3QgZGF0YSwgYW5kIGZ1bGwgbW9kZWwgZGVsYXkgZGF0YSBpbmRpY2F0ZSBgTW9kZWwgMmAgaXMgYSBnb29kIGZpdCBmb3IgT3ZlcmFsbCBGbGlnaHQgRGVsYXkgcHJlZGljdGlvbi4gVGhlIHNocmlua2FnZSB3YXMgY2FsY3VsYXRlZCBmb3IgYm90aCB0aGUgdGVzdCBkYXRhIGFuZCB0aGUgZnVsbCBkZWxheSBkYXRhIHRvIG1lYXN1cmUgdGhlIHJlbGlhYmlsaXR5IG9mIHRoZSBwcmVkaWN0aW9uIG1vZGVsLiBVdGlsaXppbmcgdGhlIHN0YW5kYXJkIGFjY2VwdGVkIGN1dG9mZiBvZiAkU2hyaW5rYWdlIDwgMC4xJCwgIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCwgYE1vZGVsIDJgIChhYm92ZSBlcXVhdGlvbnMpIHdhcyBmb3VuZCB0byBiZSByZWxpYWJsZSBhcyBhIHByZWRpY3Rpb24gbW9kZWwgZm9yIG92ZXJhbGwgZmxpZ2h0IGRlbGF5cy4NCg0KYGBge3J9DQoNCmthYmxlKHNocmlua2FnZS5saW4sIGNvbC5uYW1lcyA9IGMoIkRhdGEgVXRpbGl6ZWQiLCAiUiReMiQgVmFsdWUiLCAiQ2FsY3VsYXRlZCBTaHJpbmthZ2UiLCAiUmVsaWFiaWxpdHkiKSwgIGNhcHRpb24gPSAiVGFibGUgMTg6DQogICAgICA8YnI+QXNzZXNzbWVudCBvZiBtb2RlbCBmaXQgYW5kIHJlbGlhYmlsaXR5IikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpJbiByZXZpZXdpbmcgdGhlIHJlc2lkdWFsIHBsb3RzLCBRUSBwbG90cywgYW5kIENvb2sncyBkaXN0YW5jZSBhcyBvYnRhaW5lZCBmcm9tIG1vZGVsIDIgcnVuIG9uIHRoZSB0ZXN0IGRhdGEgdGhlIGZvbGxvd2luZyBpbmZvcm1hdGlvbiB3YXMgb2J0YWluZWQ6DQoNCiAgLSBXaGlsZSB0aGVyZSBpcyBhIG1pbm9yIGN1cnZlIHBsb3R0ZWQgaW4gdGhlIFJlc2lkdWFscyB2cy4gRml0dGVkIHBsb3QsIHRoZSBjdXJ2ZSBpcyBtaW5vciByYW5naW5nIGZyb20gMC4wIHRvIDAuMQ0KICAtIFRoZSBSZXNpZHVhbCB2cy4gRml0dGVkIHBsb3QgZGVtb25zdHJhdGVzIGEgcmFuZG9tIHNjYXR0ZXIgY2VudGVyZWQgYXJvdW5kIHk9MCBpbmRpY2F0aW5nIHRoZSBhc3N1bXB0aW9uIG9mIGxpbmVhciByZWdyZXNzaW9uIHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBhIG1lYW4gb2YgMCBpcyBtZXQNCiAgLSBUaGUgUS1RIHBsb3QgaW5kaWNhdGVzIGEgbmVhciBwZXJmZWN0IG1hdGNoIGJldHdlZW4gdGhlIHJlc2lkdWFscyBkaXN0cmlidXRpb24gYW5kIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgcHJvdmluZyB0aGUgYXNzdW1wdGlvbiBvZiBsaW5lYXIgcmVncmVzc2lvbiB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGlzIG1ldA0KICAtIFRoZSBTY2FsZS1sb2NhdGlvbiBwbG90IGRlbW9uc3RyYXRlcyBhIHJhbmRvbSBzY2F0dGVyIG9mIGFsbCByZXNpZHVhbHMgaW5kaWNhdGluZyB0aGF0IHRoZSBhc3N1bXB0aW9uIG9mIGxpbmVhciByZWdyZXNzaW9uIHRoYXQgdGhlIHJlc2lkdWFscyBwb3NzZXMgaG9tb3NjZWRhc3RpY2l0eSAoY29uc3RhbnQgdmFyaWFuY2UpIGlzIG1ldA0KICAtIFRoZSBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UgKENvb2sncyBEaXN0YW5jZSkgcGxvdCBpbmRpY2F0ZXMgdGhhdCBhbGwgdmFsdWVzIGFyZSB3aXRoaW4gdGhlIENvb2sncyBEaXN0YW5jZSB0aGVyZWZvcmUgZW5zdXJpbmcgbm8gdmFyaWFibGVzIGFyZSB0cnVseSBpbmZsdWVudGlhbCAvIG91dGxpZXIgcmVzaWR1YWxzDQogIA0KVGhlc2UgZmluZGluZ3MgaW5kaWNhdGUgdGhhdCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gcHJlZGljdGlvbiBtb2RlbCAoYEZpZ3VyZSA5YCkgaXMgYSBnb29kIGZpdCBmb3IgdGhlIE92ZXJhbGwgRmxpZ2h0IERlbGF5IGRhdGEuICBBcyB0aGlzIG1vZGVsIGlzIGEgZ29vZCBmaXQgYW5kIG1lZXRzIGFsbCBhc3N1bXB0aW9ucyBvZiBsaW5lYXIgcmVncmVzc2lvbiwgdGhpcyBtb2RlbCBoYXMgcHJvdmVuIHVzZWZ1bCBmb3IgbGl2ZSBwcmVkaWN0aW9uIG9mIG92ZXJhbGwgZmxpZ2h0IGRlbGF5ICYgZGV0ZXJtaW5hdGlvbiBvZiBmb2N1cyBpbiBhaXJsaW5lIGJ1ZGdldHMgYW5kIHN0YWZmIHRvIHByZWVtcHRpdmVseSBtaXRpZ2F0ZSBvdmVyYWxsIGZsaWdodCBkZWxheXMuIA0KDQpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9MTAsIG91dC5oZWlnaHQ9IjEwMCUifQ0KbG0ucGNhLnRlc3QucGxvdHMgPC0gbG0oYXJyX2RlbGF5IH4gYXJyX2RlbGF5X3ByZWQsIGRhdGEgPSBsbS5wY2EudGVzdCkNCg0KcGFyKG1mcm93ID0gYygyLDIpLG9tYSA9IGMoNCwgMCwgMCwgMCkpDQpwbG90KGxtLnBjYS50ZXN0LnBsb3RzKQ0KbXRleHQoIkZpZ3VyZSA5Og0KICAgICAgUmVzaWR1YWwgcGxvdHMgZm9yIGxpbmVhciByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwgMiBydW4gb24gdGVzdCBkYXRhIiwgc2lkZT0gMSwgbGluZSA9IDEsIG91dGVyPVRSVUUpDQpgYGANCg0KDQojIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwgZm9yIExpdmUgQXJyaXZhbCBEZWxheSBQcmVkaWN0aW9uDQoNCkxpdmUgZGV0ZXJtaW5pbmcgcHJlZGljdGVkIGRlbGF5IHRpbWVzIGlzIGltcGVyYXRpdmUgZm9yIGFpcnBvcnQgcGxhbm5pbmcgYW5kIHJlc291cmNpbmcgdG8gcmVkdWNlIGEgZG93bnN0cmVhbSBjYXNjYWRlIG9mIGRlbGF5ZWQgZmxpZ2h0cyB0aHJvdWdob3V0IHRoZSBlbnRpcmUgZmxpZ2h0IG5ldHdvcmsuICBJbiBvcmRlciB0byBlZmZpY2llbnRseSBwaXZvdCB0byBlbnN1cmUgYSBjYXNjYWRlIGRvZXMgbm90IG9jY3VyLCBpdCBpcyBpbXBlcmF0aXZlIGFpcmxpbmVzIGFuZCBhaXJwb3J0cyBjYW4gYWNjdXJhdGVseSBhbmQgcXVpY2tseSBmZWVkIGRhdGEgaW50byBwcmVkaWN0aW9uIG1vZGVscyB0byBvYnRhaW4gb3ZlcmFsbCBkZWxheSBlc3RpbWF0ZXMuIEFzIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBjcmVhdGVkIGlzIGNvbXB1dGF0aW9uYWxseSBoZWF2eSwgYSBzZWNvbmQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB3YXMgY3JlYXRlZCB0byBwcmVkaWN0IGFuIGFpcmxpbmUncyBrLU1lYW5zIGNsdXN0ZXIgSUQgdG8gcmVkdWNlIG92ZXJhbGwgdGltZSBhbmQgY29tcHV0YXRpb25hbCBidXJkZW4uICBUaGlzIHdpbGwgYWxsb3cgZm9yIHJlZHVjZWQgY29tcHV0YXRpb25hbCBpbnB1dCB0byBjYWxjdWxhdGUgdGhlIG92ZXJhbGwgZmxpZ2h0IGRlbGF5IHV0aWxpemluZyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gZXF1YXRpb24gKGBNb2RlbCAyYCkgYXMgb3V0bGluZWQgYWJvdmUgaW4gc2VjdGlvbiA0LiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCBmb3IgQXJyaXZhbCBEZWxheSBQcmVkaWN0aW9uLiBUaGUgY29ycmVsYXRpb24gYmV0d2VlbiBPdmVyYWxsIEZsaWdodCBEZWxheSBhbmQgay1NZWFucyBDbHVzdGVyIElEIHdhcyBjaGVja2VkIGFuZCBhIHN0cm9uZyBuZWdhdGl2ZSBsaW5lYXIgcmVsYXRpb25zaGlwIHdhcyBpZGVudGlmaWVkLCBoaWdobGlnaHRpbmcgdGhlIGltcG9ydGFuY2UgaW4gcmFwaWRseSBkZXRlcm1pbmUgdGhpcyBrLU1lYW5zIENsdXN0ZXIgSUQgd2l0aCBtaW5pbWFsIGNvbXB1dGF0aW9uYWwgYnVyZGVuIHRvIGFsbG93IGZvciBsaXZlIE92ZXJhbGwgRmxpZ2h0IERlbGF5IHByZWRpY3Rpb24uDQoNCmBgYHtyfQ0KY29yci5kZWxheS5jbHVzdCA8LSBkYXRhLmZyYW1lKA0KICBDb3JyZWxhdGlvbiA9ICBjb3IoZmluYWwuZGVsYXkkYXJyX2RlbGF5LCBmaW5hbC5kZWxheSRjbHVzdGVyKQ0KKQ0KDQprYWJsZShjb3JyLmRlbGF5LmNsdXN0LCBjYXB0aW9uID0gIlRhYmxlIDE5Og0KICAgICAgPGJyPkNvcnJlbGF0aW9uIGJldHdlZW4gT3ZlcmFsbCBGbGlnaHQgRGVsYXkgYW5kIGstTWVhbnMgQ2x1c3RlciBJRCIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KIyMgUmVncmVzc2lvbiBNb2RlbCBDcmVhdGlvbg0KDQoNCkluIG9yZGVyIHRvIGRldGVybWluZSB0aGUgYmVzdCBsb2dpc3RpYyByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwgZm9yIHVzZSBpbiBrLU1lYW5zIGNsdXN0ZXIgSUQgcHJlZGljdGlvbiB0byBpbXByb3ZlIGxpdmUgT3ZlcmFsbCBGbGlnaHQgRGVsYXkgcHJlZGljdGlvbiB0aW1lcy4gVGhlIGZvbGxvd2luZyBtb2RlbHMgd2VyZSBkZXZlbG9wZWQgYW5kIGFuYWx5emVkIHRvIGRldGVybWluZSB0aGUgYmVzdCBmaXQgbW9kZWwgdG8gY29uZHVjdCB0aGVzZSB0ZXN0cyBhbmQgZGV0ZXJtaW5hdGlvbnM6DQoNCiAgLSA8Yj5Nb2RlbCAxOjwvYj4gRnVsbCByZWdyZXNzaW9uIG1vZGVsIChDbHVzdGVyIH4gQWxsIFZhcmlhYmxlcyBpbiBgRmluYWwuRGVsYXlgIGRhdGEgc2V0KQ0KICAgIC0gVGhlIGZ1bGwgbW9kZWwgd2FzIHNlbGVjdGVkIHRvIHNlcnZlIGFzIGFuIGluaXRpYWwgYW5hbHlzaXMgYW5kIHBvaW50IG9mIHJlZmVyZW5jZSBmb3IgbW9kZWxzIDIgLSA0Lg0KICAtIDxiPk1vZGVsIDI6PC9iPiBTdGVwLXdpc2UgYXV0b21hdGVkIHNlbGVjdGlvbiByZWdyZXNzaW9uIG1vZGVsIChDbHVzdGVyIH4gTGF0ZSBJbmNvbWluZyBQbGFuZXMpDQogICAgLSBUaGUgc3RlcC13aXNlIGF1dG9tYXRlZCBzZWxlY3Rpb24gbW9kZWwgd2FzIGNob3NlbiB0byByZW1vdmUgcG90ZW50aWFsIGh1bWFuIGJpYXMgYW5kIGVycm9yIGluIGNyZWF0aW5nIHRoZSBiZXN0IGZpdCBwcmVkaWN0aW9uIG1vZGVsLiAgQm90aCBmb3J3YXJkIGFuZCBiYWNrd2FyZHMgc3RlcC13aXNlIGFuYWx5c2lzIHdhcyBjb21wbGV0ZWQgdG8gaWRlbnRpZnkgdGhlIG9wdGltYWwgbW9kZWwgZm9yIHByZWRpY3Rpb24uDQogIC0gPGI+TW9kZWwgMzo8L2I+IEV4dGVybmFsIENvbmRpdGlvbnMgTW9kZWwgKENsdXN0ZXIgfiBMYXRlIEluY29taW5nIFBsYW5lcyArIFdlYXRoZXIgKyBOdW1iZXIgb2YgRmxpZ2h0cykNCiAgICAtIFRoaXMgbW9kZWwgd2FzIGNyZWF0ZWQgdG8gcHJlZGljdCBiYXNlZCBvbiBub24tc3RhZmZpbmcgcmVsYXRlZCBhaXJwb3J0IGNvbmRpdGlvbnMgDQogIC0gPGI+TW9kZWwgNDo8L2I+IFN0YWZmIE1vZGVsIChDbHVzdGVyIH4gU3VwcG9ydCBDcmV3cyBBdmFpbGFibGUgKyBCYWdnYWdlIExvYWRpbmcgVGltZSkNCiAgICAtIFRoaXMgbW9kZWwgd2FzIGNyZWF0ZWQgdG8gcHJlZGljdCBiYXNlZCBvbiBzdGFmZmluZyByZWxhdGVkIGFpcnBvcnQgY29uZGl0aW9ucyANCg0KYGBge3J9DQp0cmFpbi5kYXRhJGNsdXN0ZXIgPC0gYXMubnVtZXJpYyh0cmFpbi5kYXRhJGNsdXN0ZXIpDQp0ZXN0LmRhdGEkY2x1c3RlciA8LSBhcy5udW1lcmljKHRlc3QuZGF0YSRjbHVzdGVyKQ0KDQp0cmFpbi5kYXRhJGNsdXN0ZXIyIDwtIE5VTEwNCmZvcihpIGluIDE6bGVuZ3RoKHRyYWluLmRhdGEpKXsNCiAgaWYodHJhaW4uZGF0YSRjbHVzdGVyW2ldID09IDEpew0KICAgIG4gPC0gMH0NCiAgZWxzZSB7biA8LSAxfQ0KICANCiAgdHJhaW4uZGF0YSRjbHVzdGVyMltpXSA8LSBuDQp9DQoNCnRlc3QuZGF0YSRjbHVzdGVyMiA8LSBOVUxMDQpmb3IoaSBpbiAxOmxlbmd0aCh0cmFpbi5kYXRhKSl7DQogIGlmKHRlc3QuZGF0YSRjbHVzdGVyW2ldID09IDEpew0KICAgIG4gPC0gMH0NCiAgZWxzZSB7biA8LSAxfQ0KICANCiAgdGVzdC5kYXRhJGNsdXN0ZXIyW2ldIDwtIG4NCn0NCg0KZmluYWwuZGVsYXkkY2x1c3RlcjIgPC0gTlVMTA0KZm9yKGkgaW4gMTpsZW5ndGgoZmluYWwuZGVsYXkpKXsNCiAgaWYoZmluYWwuZGVsYXkkY2x1c3RlcltpXSA9PSAxKXsNCiAgICBuIDwtIDB9DQogIGVsc2Uge24gPC0gMX0NCiAgDQogIGZpbmFsLmRlbGF5JGNsdXN0ZXIyW2ldIDwtIG4NCn0NCmZtLnRyYWluIDwtIHRyYWluLmRhdGFbLTEyXQ0KcW0udHJhaW4gPC0gdHJhaW4uZGF0YVstYyg4OjEyKV0NCiAgDQpmdWxsLm1vZGVsLmxvZ2l0IDwtIGdsbShjbHVzdGVyMiB+LiwgZGF0YSA9IGZtLnRyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KcXVpY2subW9kZWwubG9naXQgPC0gZ2xtKGNsdXN0ZXIyfi4sIGRhdGEgPSBxbS50cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KbG9naXQuc3RlcCA8LSBzdGVwQUlDKGZ1bGwubW9kZWwubG9naXQsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSBGQUxTRSkNCmxvZ2l0LnFzdGVwIDwtIHN0ZXBBSUMocXVpY2subW9kZWwubG9naXQsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSBGQUxTRSkNCg0Kc3RlcC5sb2dpdCA8LSBnbG0oY2x1c3RlcjIgfiBsYXRlX2luY29taW5nLCBkYXRhID0gdHJhaW4uZGF0YSwgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KZXh0LmxvZ2l0IDwtIGdsbShjbHVzdGVyMiB+IHdlYXRoZXIqbGF0ZV9pbmNvbWluZypudW1fZmxpZ2h0cywgZGF0YSA9IHRyYWluLmRhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQoNCnN0YWZmLmxvZ2l0IDwtIGdsbShjbHVzdGVyMiB+IHN1cF9jcmV3KmJhZ2dhZ2UsIGRhdGEgPSB0cmFpbi5kYXRhLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KDQpgYGANCg0KIyMgTW9kZWwgQ29tcGFycmlzb24NCg0KV2l0aCBhbGwgZm91ciBwb3RlbnRpYWwgcmVncmVzc2lvbiBtb2RlbHMgY3JlYXRlZCwgNS1mb2xkIGNyb3NzIHZhbGlkYXRpb24gd2FzIGNvbmR1Y3RlZCBhbmQgdGhlIEFrYWlrZSBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEFJQykgdmFsdWVzIGZvciBlYWNoIHByZWRpY3Rpb24gbW9kZWwgd2VyZSBvYnRhaW5lZC4gIFRoZSBkYXRhIHdhcyBzcGxpdCA4MDoyMCBpbnRvIHRyYWluOnRlc3QgZGF0YSBzdWJzZXRzIHJlc3BlY3RpdmVseSwgdXRpbGl6aW5nIHRoZSB0cmFpbmluZyBzdWJzZXRzIGZvciB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBwcm9jZXNzLiBUaHJvdWdoIGNvbXBhcmluZyB0aGUgY2FsY3VsYXRlZCB0cmFpbmluZyBkYXRhIEFJQyB2YWx1ZXMgYXMgcmVwcmVzZW50ZWQgYmVsb3cgaW4gYFRhYmxlIDIwYCwgdGhlIGJlc3QgZml0IHByZWRpY3Rpb24gbW9kZWwgd2FzIGlkZW50aWZpZWQgYXMgYE1vZGVsIDNgIGFuZCBzdWJzZXF1ZW50bHkgc2VsZWN0ZWQgZm9yIGZ1cnRoZXIgdGVzdGluZyB1dGlsaXppbmcgdGhlIHRlc3QgZGF0YS4gIA0KDQpgYGB7cn0NCmFpYy5jb21wIDwtIGRhdGEuZnJhbWUoDQogIE1vZGVsID0gYygiRnVsbCBNb2RlbCAoTW9kZWwgMSkiLCAiU3RlcHdpc2UgTW9kZWwgKE1vZGVsIDIpIiwgIkV4dGVybmFsIENvbmQuIE1vZGVsIChNb2RlbCAzKSIsICJTdGFmZiBNb2RlbCAoTW9kZWwgNCkiKSwNCiAgQUlDID0gYyhmdWxsLm1vZGVsLmxvZ2l0JGFpYywgc3RlcC5sb2dpdCRhaWMsIGV4dC5sb2dpdCRhaWMsIHN0YWZmLmxvZ2l0JGFpYykNCikNCg0Ka2FibGUoYWljLmNvbXAsIGNhcHRpb24gPSAiVGFibGUgMjA6DQogICAgICA8YnI+Q29tcGFycmlzb24gb2YgY2FsY3VsYXRlZCBBSUMgdmFsdWVzIGZvciBwcm9wb3NlZCBsb2dpc3RpYyByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWxzIGZvciBjbHVzdGVyIElEIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpGaW5hbGx5IHRoZSBST0MgKFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpYykgY3VydmVzIGZvciBlYWNoIHByZWRpY3Rpb24gbW9kZWwgd2VyZSBjb21wYXJlZCBmb3IgYSBzZWNvbmQgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjb21wYXJpc29uIG9mIHRydWUgcG9zaXRpdmUgYW5kIGZhbHNlIHBvc2l0aXZlIHJhdGVzIGFzIHdlbGwgYXMgdGhlIGNhbGN1bGF0ZWQgYXJlYSB1bmRlciB0aGUgY3VydmUgKEFVQykgbWVhc3VyZXMgYmVsb3cgaW4gYEZpZ3VyZSAxMGAuICBBcyBzZWVuIHdpdGggdGhlIGNhbGN1bGF0ZWQgQVVDIGZvciBgTW9kZWwgM2AgdGhlIEV4dGVybmFsIENvbmQuIE1vZGVsIGRlbW9uc3RyYXRlcyBleGNlbGxlbnQgcHJlZGljdGlvbiBwZXJmb3JtYW5jZSBieSB0aGUgbW9kZWwuICBUaGUgYmVzdCBmaXQgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5IHZhbHVlcyBmb3IgdXNlIHdpdGggdGhlIG1vZGVsIGFuZCBpbmRpY2F0ZWQgaW4gYEZpZ3VyZSAxMGAgd2l0aCBhIHllbGxvdyB0cmlhbmdsZSBhbmQgaW4gYFRhYmxlIDIxYC4gIFRoZXNlIGN1dG9mZnMgYXJlIHRoZW4gdXRpbGl6ZWQgZm9yIHdlaWdodGluZyBvZiB0aGUgZmFsc2UgcG9zaXRpdmUgYW5kIHRydWUgcG9zaXRpdmUgcmF0ZXMgd2hlbiB1dGlsaXppbmcgdGhlIHByb2JhYmlsaXRpZXMgYXMgb3V0cHV0IGluIHRoZSBiaW5hcnkgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IHRoZSBjbHVzdGVyIElEIHdpdGhvdXQgZnVsbCBQQ0EgYW5kIGstTWVhbnMgY2x1c3RlciBJRCBjcmVhdGlvbi4gDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCBvdXQud2lkdGg9IjEwMCUifQ0KI21vZGVsIHByZWRpY3Rpb25zIA0KUk9DLnN0ZXAgPC0gcm9jKHN0ZXAubG9naXQkeSwgc3RlcC5sb2dpdCRmaXR0ZWQudmFsdWVzKQ0KDQpST0MuZXh0IDwtIHJvYyhleHQubG9naXQkeSwgZXh0LmxvZ2l0JGZpdHRlZC52YWx1ZXMpDQoNClJPQy5mdWxsIDwtIHJvYyhmdWxsLm1vZGVsLmxvZ2l0JHksIGZ1bGwubW9kZWwubG9naXQkZml0dGVkLnZhbHVlcykNCg0KUk9DLnN0YWZmIDwtIHJvYyhzdGFmZi5sb2dpdCR5LCBzdGFmZi5sb2dpdCRmaXR0ZWQudmFsdWVzKQ0KDQoNCg0Kcm9jLmNvbG9ycyA8LSBjKCJibHVlIiwgInB1cnBsZSIsICJibGFjayIsICJkYXJrZ3JlZW4iKQ0KY3AgPC0gY29vcmRzKFJPQy5leHQsICJiZXN0IiwgcmV0ID0gYygidGhyZXNob2xkIiwgInNlbnNpdGl2aXR5IiwgInNwZWNpZmljaXR5IikpDQoNCnBhcihvbWEgPSBjKDYsMiwyLDIpLCAgY2V4Lm1haW4gPSAyKQ0KcGxvdC5yb2MoUk9DLnN0YWZmLCBjb2wgPSAiYmx1ZSIsIHlsYWIgPSAiVHJ1ZSBQb3N0aXZlIFJhdGUgKFNlbnNpdGl2aXR5KSkiLCB4bGFiID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUgKDEgLSBTcGVjaWZpY2l0eSkiLCBsd2QgPSAyKQ0KcGxvdC5yb2MoUk9DLnN0ZXAsIGNvbCA9ICJwdXJwbGUiLCBhZGQgPSBUUlVFLCBsd2QgPSAyKQ0KcGxvdC5yb2MoUk9DLmZ1bGwsIGFkZCA9IFRSVUUsIGx3ZCA9IDIpDQpwbG90LnJvYyhST0MuZXh0LCBjb2wgPSAiZGFya2dyZWVuIiwgYWRkID0gVFJVRSwgbHdkID0gMikNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBsZWdlbmQgPSANCiAgICAgICAgIGMocGFzdGUoDQogICAgICAgICAgICAgIlN0YWZmIE1vZGVsICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFVQyA9Iiwgcm91bmQoUk9DLnN0YWZmJGF1Yyw0KSksIA0KICAgICAgICAgICBwYXN0ZSgNCiAgICAgICAgICAgICAiU3RlcHdpc2UgTW9kZWwgICAgICAgICAgICAgICAgICAgQVVDID0iLCByb3VuZChST0Muc3RlcCRhdWMsNCkpLCANCiAgICAgICAgICAgcGFzdGUoDQogICAgICAgICAgICAgICJGdWxsIE1vZGVsICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBVUMgPSIsIHJvdW5kKFJPQy5mdWxsJGF1YywgNCkpLCANCiAgICAgICAgICAgcGFzdGUoDQogICAgICAgICAgICAgIkV4dGVybmFsIENvbmQuIE1vZGVsICAgICAgICAgQVVDID0iLCByb3VuZChST0MuZXh0JGF1YywgNCkpKSwgY29sID0gcm9jLmNvbG9ycywgbHdkID0gMSkNCnRpdGxlKG1haW49IlJPQyBDb21wYXJyaXNvbiIsIG91dGVyID0gVCkNCnRpdGxlKHN1YiA9IHBhc3RlKCJGaWd1cmUgMTA6DQpDb21wYXJyaXNvbiBvZiBST0MgYW5kIEFVQyB2YWx1ZXMgZm9yIGVhY2ggbG9naXN0aWMgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsDQpZZWxsb3cgdHJpYW5nbGUgcmVwcmVzZW50cyBpZGVhbCBjdXRvZmYgcG9pbnQgb2YgYmVzdCBmaXQgbW9kZWwuIA0KDQpUaHJlc2hvbGQgPSIsIHJvdW5kKGNwJHRocmVzaG9sZCw0KSwgIiBTZW5zaXRpdml0eSA9ICIsIHJvdW5kKGNwJHNlbnNpdGl2aXR5LDQpLCAiIFNwZWNpZmljaXR5ID0iLCByb3VuZChjcCRzcGVjaWZpY2l0eSwgNCkpLCBvdXRlciA9IFQsIGxpbmU9NCkNCnBvaW50cyh4ID0gY3Akc3BlY2lmaWNpdHksIHkgPSBjcCRzZW5zaXRpdml0eSwgcGNoID0gMjQsIGJnID0gInllbGxvdyIsIGNleCA9IDIpDQoNCg0Ka2FibGUoY3AsIGNhcHRpb24gPSAiVGFibGUgMjE6DQogICAgICA8YnI+VGhyZXNob2xkIGFuZCBjdXRvZmYgdHJ1ZSBwb3NpdGl2ZSBhbmQgZmFsc2UgcG9zaXRpdmUgcmF0ZXMiLCBjb2wubmFtZXMgPSBjKCJUaHJlc2hvbGQiLCAiVHJ1ZSBQb3NpdGl2ZSAoU2Vuc2l0aXZpdHkpIiwgIkZhbHNlIFBvc2l0aXZlICgxLVNwZWNpZmljaXR5KSIpKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpgYGANCg0KIyBDb25jbHVzaW9ucw0KDQpJbiB0aGlzIGFuYWx5c2lzIG9mIGdsb2JhbCBmbGlnaHQgZGVsYXkgZGF0YSwgdHdvIHByZWRpY3Rpb24gbW9kZWxzIHdlcmUgY3JlYXRlZCBmb3Igc2VwYXJhdGUgdXRpbGl6YXRpb24gcHVycG9zZXMsIGJvdGggdHVuZWQgdG8gYXNzaXN0IGFpcmxpbmVzIGFuZCBhaXJwb3J0cyBpbiByZWR1Y3Rpb24gb2Ygb3ZlcmFsbCBmbGlnaHQgZGVsYXkgZGF0YS4gICBUaGUgUENBIGJhc2VkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHByb3ZlcyB0byBjcmVhdGUgYSBkZXRhaWxlZCB0b29sIGZvciBhaXJsaW5lcyB0byBwcm9hY3RpdmVseSBwbGFuIHN0YWZmaW5nIGNvbnNpZGVyYXRpb25zLCBvdmVyYWxsIGZsaWdodCBzY2hlZHVsaW5nLCBhbmQgZGVwYXJ0bWVudGFsIGJ1ZGdldHMgYnkgcHJlZGljdGluZyB0aGUgaW1wYWN0IG9mIG92ZXJhbGwgY2hhbmdlcyBpbiB0aGUgdGVybXMgb2YgYXZlcmFnZSBPdmVyYWxsIEZsaWdodCBEZWxheSB0aW1lcy4gIEFzIHRoaXMgbW9kZWwgaXMgb2YgYSBoaWdoIGNvbXB1dGF0aW9uYWwgYnVyZGVuLCBhIHNlY29uZCBtb2RlbCB3YXMgY3JlYXRlZCB0byBwcmVkaWN0IHRoZSBrLU1lYW5zIGNsdXN0ZXIgSUQgdXRpbGl6ZWQgaW4gdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGZvciBhIHJhcGlkIGFwcHJvYWNoIHRvIGxpdmUgZmxpZ2h0IGRlbGF5IHByZWRpY3Rpb25zLiAgQnkgZmlyc3QgdXNpbmcgdGhlIGxvdyBjb21wdXRhdGlvbmFsIGJ1cmRlbiBsb2dpc3RpYyByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwgdGhlIHByZWRpY3RlZCBrLU1lYW5zIGNsdXN0ZXIgSUQgY2FuIGJlIGZlZCBpbnRvIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsIHdoaWxlIHNpbXVsdGFuZW91c2x5IGRyb3BwaW5nIHRoZSBQQ0EgcHJvY2VzcyB0byAxIHZhcmlhYmxlIGFuZCBvbWl0dGluZyB0aGUgay1NZWFucyBjbHVzdGVyIElEIGNyZWF0aW9uIHRvIGxpdmUgcHJlZGljdCBmbGlnaHQgZGVsYXlzLiAgVGhpcyB3aWxsIGFsbG93IGFpcnBvcnRzIHRvIHByZWRpY3QgZG93bnN0cmVhbSBpbXBhY3RzIG9mIGRlbGF5cyBiYXNlZCBvbiBjdXJyZW50IGFpcnBvcnQgY29uZGl0aW9ucyAod2VhdGhlciwgc3RhZmYgYXZhaWxhYmlsaXR5LCBhdmVyYWdlIGluY29taW5nIGZsaWdodCBkZWxheXMsIGFuZCBudW1iZXIgb2YgZmxpZ2h0cyBhdCB0aGUgYWlycG9ydCB0aGF0IGRheSkuICBUaHJvdWdoIHN0YWNraW5nIHRoZXNlIHR3byByb2J1c3QgcHJlZGljdGlvbiBtb2RlbHMgYWlybGluZXMgYW5kIGFpcnBvcnRzIHNob3VsZCBiZSBtb3JlIHRoYW4gZXF1aXBwZWQgdG8gcHJlZGljdCBkZWxheXMgbGl2ZS9iZWZvcmUgdGhleSBoYXBwZW4gYW5kIHByZWVtcHRpdmVseSBwbGFuIHN0YWZmaW5nIGFuZCBidWRnZXRpbmcgY29uc2lkZXJhdGlvbnMgZm9yIGhpZ2ggdHJhdmVsIGRheXMgKGV4OiBDaHJpc3RtYXMgRXZlKSB0byByZWR1Y2UgYm90dGxlbmVja3MgYW5kIGRvd25zdHJlYW0gZGVsYXlzLiANCg0K