Introduction

Every year, nearly 25% of airline flights are delayed or cancelled, costing travellers over $30 billion in lost time and money Flight delays have long been a cause of dissatisfaction in the airline industry, as well as a source of annoyance for passengers and carriers.

Our goal is to use the massive amount of airline data to visualise and study the flight patterns and predict if a flight will be delayed. For this study, both Python and R will be used to investigate 2 years’ worth of data, since two full business cycles are adequate in reducing bias for one cycle.

This notebook aims to look at these questions regarding flight travel:

  1. When is the best time of day, day of the week, and time of year to fly to minimise delays?
  2. Do older planes suffer more delays?
  3. How does the number of people flying between different locations change over time?
  4. Can you detect cascading failures as delays in one airport create delays in others?
  5. Use the available variables to construct a model that predicts delays.

Data: Airlines - Harvard Dataverse

Import data and libraries

library(ggplot2)
library(dplyr)
library(zoo)
library(tidyr)
library("data.table")
library(plyr)
options(warn=-1)

setwd("D:/et4_e/coursework/2021")

# Import & prepare the 2006 and 2007 dataset
df_2006 = fread("2006.csv.bz2")
|--------------------------------------------------|
|==================================================|
df_2007 = fread("2007.csv.bz2")
|--------------------------------------------------|
|==================================================|
mergeddf = rbind(df_2006, df_2007)
airport_df <- read.csv2('airports.csv',sep = ",",header = TRUE)
carrier_df <- read.csv2('carriers.csv',sep = ",",header = TRUE)
planes_df <- read.csv2('D:/et4_e/coursework/2021/plane-data.csv',sep = ",",header = TRUE)


Understanding Data

# Create Date column
mergeddf$date <- as.Date(with(mergeddf, paste(Year, Month, DayofMonth, sep="-")), "%Y-%m-%d")
# Create copy of merged data
merged_df = mergeddf
head(merged_df)
dim(merged_df)
[1] 14595137       30


There are almost 14.6 million records with 29 variable columns. The columns include the airline and flight details etc. and are mostly time-related (in mins).

summary(merged_df)
      Year          Month          DayofMonth      DayOfWeek    
 Min.   :2006   Min.   : 1.000   Min.   : 1.00   Min.   :1.000  
 1st Qu.:2006   1st Qu.: 4.000   1st Qu.: 8.00   1st Qu.:2.000  
 Median :2007   Median : 7.000   Median :16.00   Median :4.000  
 Mean   :2007   Mean   : 6.538   Mean   :15.73   Mean   :3.942  
 3rd Qu.:2007   3rd Qu.:10.000   3rd Qu.:23.00   3rd Qu.:6.000  
 Max.   :2007   Max.   :12.000   Max.   :31.00   Max.   :7.000  
                                                                
    DepTime         CRSDepTime      ArrTime         CRSArrTime  
 Min.   :   1     Min.   :   0   Min.   :   1     Min.   :   0  
 1st Qu.: 930     1st Qu.: 930   1st Qu.:1108     1st Qu.:1115  
 Median :1329     Median :1324   Median :1515     Median :1520  
 Mean   :1340     Mean   :1331   Mean   :1484     Mean   :1496  
 3rd Qu.:1732     3rd Qu.:1720   3rd Qu.:1911     3rd Qu.:1906  
 Max.   :2930     Max.   :2359   Max.   :2955     Max.   :2400  
 NA's   :282682                  NA's   :316047                 
 UniqueCarrier        FlightNum      TailNum         
 Length:14595137    Min.   :   1   Length:14595137   
 Class :character   1st Qu.: 587   Class :character  
 Mode  :character   Median :1501   Mode  :character  
                    Mean   :2187                     
                    3rd Qu.:3499                     
                    Max.   :9619                     
                                                     
 ActualElapsedTime CRSElapsedTime       AirTime       
 Min.   :   5.0    Min.   :-1240.0   Min.   :-1425.0  
 1st Qu.:  75.0    1st Qu.:   77.0   1st Qu.:   54.0  
 Median : 108.0    Median :  109.0   Median :   84.0  
 Mean   : 126.2    Mean   :  127.2   Mean   :  102.8  
 3rd Qu.: 156.0    3rd Qu.:  157.0   3rd Qu.:  131.0  
 Max.   :1879.0    Max.   : 1430.0   Max.   : 1958.0  
 NA's   :316047    NA's   :998       NA's   :316047   
    ArrDelay          DepDelay           Origin         
 Min.   :-592.00   Min.   :-1200.00   Length:14595137   
 1st Qu.:  -9.00   1st Qu.:   -4.00   Class :character  
 Median :  -1.00   Median :    0.00   Mode  :character  
 Mean   :   9.45   Mean   :   10.76                     
 3rd Qu.:  13.00   3rd Qu.:   10.00                     
 Max.   :2598.00   Max.   : 2601.00                     
 NA's   :316047    NA's   :282682                       
     Dest              Distance          TaxiIn        
 Length:14595137    Min.   :  11.0   Min.   :   0.000  
 Class :character   1st Qu.: 317.0   1st Qu.:   4.000  
 Mode  :character   Median : 569.0   Median :   5.000  
                    Mean   : 723.8   Mean   :   6.873  
                    3rd Qu.: 951.0   3rd Qu.:   8.000  
                    Max.   :4962.0   Max.   :1501.000  
                                                       
    TaxiOut         Cancelled       CancellationCode  
 Min.   :  0.00   Min.   :0.00000   Length:14595137   
 1st Qu.: 10.00   1st Qu.:0.00000   Class :character  
 Median : 13.00   Median :0.00000   Mode  :character  
 Mean   : 16.03   Mean   :0.01937                     
 3rd Qu.: 19.00   3rd Qu.:0.00000                     
 Max.   :602.00   Max.   :1.00000                     
                                                      
    Diverted         CarrierDelay       WeatherDelay      
 Min.   :0.000000   Min.   :   0.000   Min.   :   0.0000  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:   0.0000  
 Median :0.000000   Median :   0.000   Median :   0.0000  
 Mean   :0.002286   Mean   :   3.636   Mean   :   0.7258  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:   0.0000  
 Max.   :1.000000   Max.   :2580.000   Max.   :1429.0000  
                                                          
    NASDelay        SecurityDelay      LateAircraftDelay 
 Min.   :   0.000   Min.   :  0.0000   Min.   :   0.000  
 1st Qu.:   0.000   1st Qu.:  0.0000   1st Qu.:   0.000  
 Median :   0.000   Median :  0.0000   Median :   0.000  
 Mean   :   3.687   Mean   :  0.0273   Mean   :   4.813  
 3rd Qu.:   0.000   3rd Qu.:  0.0000   3rd Qu.:   0.000  
 Max.   :1392.000   Max.   :382.0000   Max.   :1366.000  
                                                         
      date           
 Min.   :2006-01-01  
 1st Qu.:2006-07-08  
 Median :2007-01-08  
 Mean   :2007-01-04  
 3rd Qu.:2007-07-06  
 Max.   :2007-12-31  
                     
str(merged_df)
Classes ‘data.table’ and 'data.frame':  14595137 obs. of  30 variables:
 $ Year             : int  2006 2006 2006 2006 2006 2006 2006 2006 2006 2006 ...
 $ Month            : int  1 1 1 1 1 1 1 1 1 1 ...
 $ DayofMonth       : int  11 11 11 11 11 11 11 11 11 11 ...
 $ DayOfWeek        : int  3 3 3 3 3 3 3 3 3 3 ...
 $ DepTime          : int  743 1053 1915 1753 824 627 825 942 1239 1642 ...
 $ CRSDepTime       : int  745 1053 1915 1755 832 630 820 945 1245 1645 ...
 $ ArrTime          : int  1024 1313 2110 1925 1015 834 1041 1155 1438 1841 ...
 $ CRSArrTime       : int  1018 1318 2133 1933 1015 832 1021 1148 1445 1845 ...
 $ UniqueCarrier    : chr  "US" "US" "US" "US" ...
 $ FlightNum        : int  343 613 617 300 765 295 349 356 775 1002 ...
 $ TailNum          : chr  "N657AW" "N834AW" "N605AW" "N312AW" ...
 $ ActualElapsedTime: int  281 260 235 152 171 127 136 133 119 119 ...
 $ CRSElapsedTime   : int  273 265 258 158 163 122 121 123 120 120 ...
 $ AirTime          : int  223 214 220 126 132 108 111 121 103 105 ...
 $ ArrDelay         : int  6 -5 -23 -8 0 2 20 7 -7 -4 ...
 $ DepDelay         : int  -2 0 0 -2 -8 -3 5 -3 -6 -3 ...
 $ Origin           : chr  "ATL" "ATL" "ATL" "AUS" ...
 $ Dest             : chr  "PHX" "PHX" "PHX" "PHX" ...
 $ Distance         : int  1587 1587 1587 872 872 644 644 644 644 644 ...
 $ TaxiIn           : int  45 27 4 16 27 6 4 4 4 4 ...
 $ TaxiOut          : int  13 19 11 10 12 13 21 8 12 10 ...
 $ Cancelled        : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CancellationCode : chr  "" "" "" "" ...
 $ Diverted         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CarrierDelay     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ WeatherDelay     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ NASDelay         : int  0 0 0 0 0 0 20 0 0 0 ...
 $ SecurityDelay    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ LateAircraftDelay: int  0 0 0 0 0 0 0 0 0 0 ...
 $ date             : Date, format: "2006-01-11" ...
 - attr(*, ".internal.selfref")=<externalptr> 

Data Pre-Processing/Cleaning

Creating sample of entire data

Due to the large dataset requiring more time to execute, we will randomly select 10% of the data for quick analysis.

We then filter the data into Cancelled and Non-cancelled flights.

# Remove original data & take sample (10%) of merged data (save space & load faster)
set.seed(42)
merged_df = sample_frac(merged_df, 0.10, replace = FALSE)
# Rename CancellationCode Column
merged_df$CancellationCode <- mapvalues(merged_df$CancellationCode,
                           from = c("A", "B", "C", "D"),
                           to = c("Carrier", "Weather", "National Air System (NAS)", "Security"))

merged_df$Cancelled <- mapvalues(merged_df$Cancelled,
                           from = c(1, 0),
                           to = c("Cancelled", "Not Cancelled"))
merged_df$Diverted <- mapvalues(merged_df$Diverted,
                           from = c(1, 0),
                           to = c("Diverted", "Not Diverted"))
# Check for missing values
sum(is.na(merged_df))
[1] 184149
# Missing values per column
sapply(merged_df,function(x)sum(is.na(x)))
             Year             Month        DayofMonth 
                0                 0                 0 
        DayOfWeek           DepTime        CRSDepTime 
                0             28496                 0 
          ArrTime        CRSArrTime     UniqueCarrier 
            31764                 0                 0 
        FlightNum           TailNum ActualElapsedTime 
                0                 0             31764 
   CRSElapsedTime           AirTime          ArrDelay 
              101             31764             31764 
         DepDelay            Origin              Dest 
            28496                 0                 0 
         Distance            TaxiIn           TaxiOut 
                0                 0                 0 
        Cancelled  CancellationCode          Diverted 
                0                 0                 0 
     CarrierDelay      WeatherDelay          NASDelay 
                0                 0                 0 
    SecurityDelay LateAircraftDelay              date 
                0                 0                 0 
# Check for duplicates
sum(duplicated(merged_df))
[1] 0
distinct(merged_df)


Missing data is also handled using linear interpolation to estimate unknown data values between known data values, and duplicates are removed.

# Imputate Null values with interpolation

merged_df <- merged_df %>%
        mutate(DepTime = na.approx(DepTime))
merged_df <- merged_df %>%
        mutate(ArrTime = na.approx(ArrTime))
merged_df <- merged_df %>%
        mutate(ActualElapsedTime = na.approx(ActualElapsedTime))
merged_df <- merged_df %>%
        mutate(CRSElapsedTime = na.approx(CRSElapsedTime))
merged_df <- merged_df %>%
        mutate(AirTime = na.approx(AirTime))
merged_df <- merged_df %>%
        mutate(ArrDelay = na.approx(ArrDelay))
merged_df <- merged_df %>%
        mutate(DepDelay = na.approx(DepDelay))
# Change selected data types (Numeric to Categorical)
merged_df$Year <- as.factor(merged_df$Year)
merged_df$Month <- as.factor(merged_df$Month)
merged_df$DayofMonth <- as.factor(merged_df$DayofMonth)
merged_df$DayOfWeek <- as.factor(merged_df$DayOfWeek)
merged_df$FlightNum <- as.factor(merged_df$FlightNum)
merged_df$Cancelled <- as.factor(merged_df$Cancelled)
merged_df$Diverted <- as.factor(merged_df$Diverted)
# Create labelled column for easier visualisation
merged_df$Month_label <- month.abb[merged_df$Month]
# Check for missing values per column
sapply(merged_df,function(x)sum(is.na(x)))
             Year             Month        DayofMonth 
                0                 0                 0 
        DayOfWeek           DepTime        CRSDepTime 
                0                 0                 0 
          ArrTime        CRSArrTime     UniqueCarrier 
                0                 0                 0 
        FlightNum           TailNum ActualElapsedTime 
                0                 0                 0 
   CRSElapsedTime           AirTime          ArrDelay 
                0                 0                 0 
         DepDelay            Origin              Dest 
                0                 0                 0 
         Distance            TaxiIn           TaxiOut 
                0                 0                 0 
        Cancelled  CancellationCode          Diverted 
                0                 0                 0 
     CarrierDelay      WeatherDelay          NASDelay 
                0                 0                 0 
    SecurityDelay LateAircraftDelay              date 
                0                 0                 0 
      Month_label 
                0 


The null values have all been removed from the dataframe.

flight_cancelled <- merged_df %>% filter_at(vars(Cancelled), any_vars(. %in% c('Cancelled')))
flight_notcancelled <- merged_df %>% filter_at(vars(Cancelled), any_vars(. %in% c('Not Cancelled')))


Creating Delay Status

We assume that a delayed flight is equivalent to arriving late for more than 15 minutes at its destination. (ArrDelay > 15 mins)

Since flights can be delayed on its Departure but still arrive on time, hence we do not classify those as a delayed flight.

Hence we create a DelayStatus column into the main dataframe (merged_df),where 0 = No Delay, 1 = Delay.

# Creating new column showing ArrDelay > 15mins
# 0 = No Delay, 1= Delay
flight_notcancelled$DelayStatus <- ifelse(flight_notcancelled$ArrDelay > 15, 1, 0)
table(flight_notcancelled$DelayStatus)

      0       1 
1102581  328437 
prop.table(table(flight_notcancelled$DelayStatus))

        0         1 
0.7704872 0.2295128 

This shows that 77% have no delays (ArrDelay > 15 minutes), where they either arrived early or on time. Also, 23% of flights were delayed. Equivalent to about 1 out of every 5 flights being delayed.


Exploratory Data Analysis (EDA)

We will be looking at the different variables to get a better understanding of the data.

  • Total Flight Distribution
  • Cancellation
  • Delay

Total Flight Distribution

Total Flight Distribution of Full Data by Month

prop.table(table(merged_df$Month))

         1          2          3          4          5          6 
0.08206499 0.07508801 0.08511806 0.08237742 0.08450758 0.08426093 
         7          8          9         10         11         12 
0.08654045 0.08835270 0.08098038 0.08534827 0.08177654 0.08358467 
prop.table(table(merged_df$DayOfWeek))

        1         2         3         4         5         6 
0.1483665 0.1443227 0.1459657 0.1475135 0.1475149 0.1257288 
        7 
0.1405879 
# Percentage Distribution of Month
ggplot(merged_df, aes(x = Month)) +  
  geom_bar(aes(y = (..count..)/sum(..count..)), fill = 'cornflowerblue') + ggtitle("Month (%)") +
  ylab("Percentage (%)")

# Percentage Distribution of DayOfWeek
ggplot(merged_df, aes(x = DayOfWeek)) +  
  geom_bar(aes(y = (..count..)/sum(..count..)),fill = 'cornflowerblue') + ggtitle("DayOfWeek (%)") +
  ylab("Percentage (%)")


The data is almost evenly distributed between Month and DayOfWeek, with February and Saturday having the least number of total flights.

#Total Number of flights per Airline
ggplot(merged_df, aes(x = forcats::fct_infreq(UniqueCarrier))) +  
  geom_bar(aes(y = (..count..)),fill = 'cornflowerblue') + ggtitle("Total Number of Flights per Airline") +
  xlab("UniqueCarrier")

carrier_df %>% filter_all(any_vars(. %in% c('WN', 'AA','OO','MQ','US')))

The top 5 airlines with the most flights are WN, AA, OO, MQ, UA.

  1. Southwest Airlines
  2. American Airlines
  3. Skywest Airlines
  4. American Eagle Airlines
  5. United Airlines
#Boxplot Distribution of Total ArrDelay per Carrier
boxplot(flight_notcancelled$ArrDelay~flight_notcancelled$UniqueCarrier,
        main = "Distribution of Total ArrDelay per Carrier",
        xlab = "Carrier/Airline",
        ylab = "Total ArrDelay (mins)",
        border = "black"
        )

Cancellation

# Percentage Distribution of Cancellation
ggplot(flight_cancelled, aes(x = CancellationCode)) +  
  geom_bar(aes(y = (..count..)/sum(..count..)), fill = 'cornflowerblue') + ggtitle("Cancellation Reasons (%)") +
  ylab("Percentage (%)")

We can conclude that Cancellations are mostly due to Carrier, Weather and NAS with around 43%, 35% and 20% respectively.

#Total Number of Cancelled flights per Airline
ggplot(flight_cancelled, aes(x = forcats::fct_infreq(UniqueCarrier))) +  
  geom_bar(aes(y = (..count..)),fill = 'cornflowerblue') + ggtitle("Total Number of Cancelled Flights per Airline") +
  xlab("UniqueCarrier")

carrier_df %>% filter_all(any_vars(. %in% c('MQ', 'AA','OO')))

The top 3 most cancelled flights throughout these 2 years are:

  1. American Eagle Airlines
  2. American Airlines
  3. Skywest Airlines
#Total Number of Cancelled flights per Month
ggplot(flight_cancelled, aes(x = forcats::fct_infreq(Month))) +  
  geom_bar(aes(y = (..count..)),fill = 'cornflowerblue') + ggtitle("Total Number of Cancelled Flights per Month") +
  xlab("UniqueCarrier")

#Total Number of Cancelled flights per DayOfWeek
ggplot(flight_cancelled, aes(x = forcats::fct_infreq(DayOfWeek))) +  
  geom_bar(aes(y = (..count..)),fill = 'cornflowerblue') + ggtitle("Total Number of Cancelled Flights per DayOfWeek") +
  xlab("UniqueCarrier")

The flights are mostly likely to be cancelled in December and February, on a Thursday and Friday.

Delay

cor.test(flight_notcancelled$ArrDelay, flight_notcancelled$DepDelay, method = "pearson")

    Pearson's product-moment correlation

data:  flight_notcancelled$ArrDelay and flight_notcancelled$DepDelay
t = 2841.3, df = 1431016, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.9213976 0.9218910
sample estimates:
      cor 
0.9216447 

ArrDelay and DepDelay have a strong positive linear relationship, implying that a Departure Delay will almost certainly result in an Arrival Delay.

# BarPlot
ggplot(flight_notcancelled, aes(x= UniqueCarrier)) + geom_bar(aes(fill=as.factor(DelayStatus))) + ggtitle("Barplot of DelayStatus counts per UniqueCarrier")

carrier_df %>% filter_all(any_vars(. %in% c('WN', 'AA','OO')))

The top 3 most delayed flights throughout these 2 years are:

  1. Southwest Airlines
  2. American Airlines
  3. Skywest Airlines

Q1. When is the best time of day, day of the week, and time of year to fly to minimise delays?

We will breakdown this question into three parts, where we will find the airline carrier and time period least likely to have delayed flights:

  • Best Time of the Day
  • Best Day of the Week
  • Best Month of the Year
  • Best Day of the Month

Best Time of the Day

Distribution of Average Delay by Time Period

# Create copy of dataframe
df_q1 <- flight_notcancelled


Time Intervals column ‘ArrPeriod’ was created based on ‘ArrTime’. 24 hours in a day will split into 6 different periods with at least 3 to 5-hour intervals since different timings like 5am and 11am are better not generalised together into a single timeframe. The period is split as such:

  • Midnight (12am - 5am)
  • Early Morning (5am - 9am)
  • Late Morning (9am - 12pm)
  • Afternoon (12pm - 5pm)
  • Evening (5pm - 9pm)
  • Night (9pm - 12am)
# Categorising ArrTime and DepTime by 6 periods: Midnight, Early Morning, Late Morning, Afternoon, Evening, Night
df_q1 <- df_q1 %>%
  mutate(ArrPeriod = case_when(ArrTime >= 500 & ArrTime < 900 ~ 'Early Morning', 
                          ArrTime >= 900 & ArrTime < 1200 ~ 'Late Morning',
                          ArrTime >= 1200 & ArrTime < 1700 ~ 'Afternoon',
                          ArrTime >= 1700 & ArrTime < 2100 ~ 'Evening',
                          ArrTime >= 2100 & ArrTime < 2400 ~ 'Night',
                          TRUE ~  'Midnight'))
# Create table counts
counts <- table(df_q1$DelayStatus,df_q1$ArrPeriod)

# Plot grouped barplot
barplot(counts, col = c("white","cornflowerblue"),
                       xlab = "Time Period", ylab = "Total Delay Count",
                       main = "Total Delay per Period",beside=TRUE, 
                       legend = rownames(counts))

Early Morning followed by Midnight has the lowest count for number of flights delayed. However, we will proceed on to check if having the lowest number of count will equate to the shortest average delayed time.

# Boxplot
ggplot(df_q1, aes(x= ArrDelay)) + geom_boxplot(aes(color=as.factor(ArrPeriod))) + ggtitle("Boxplot of ArrDelay (mins) vs ArrPeriod")

The boxplot shows that the mean of ArrDelay for midnight is more than the other time period. Hence, validating our assumption that lowest flight delay counts does not equate to lowest average delayed time.

#Set Sequence to ArrPeriod
df_q1$ArrPeriod <- factor(df_q1$ArrPeriod,levels = c("Midnight", "Early Morning", "Late Morning", "Afternoon", "Evening", "Night"))

df_q1  %>% group_by(ArrPeriod) %>% dplyr::summarize(mean = mean(ArrDelay)) %>%
  ggplot(aes(x= ArrPeriod, y = mean)) +
  geom_point() +
  geom_line(aes(group = 1), col = "cornflowerblue") + ggtitle("Average ArrDelay vs Time Period") +
  xlab("Time Period") + ylab("Average ArrDelay (mins)")

The best time period to minimise flight delays would be in the Early Morning from 5am to 9am.

Best Day of the Week

Distribution of Day Delay

# Create table for counts
counts <- table(df_q1$DelayStatus,df_q1$DayOfWeek)
# Plot grouped barplot
barplot(counts, col = c("white","cornflowerblue"),
                       xlab = "DayOfWeek",ylab = "Total Delay Count",
                       main = "Total Delay per DayOfWeek",beside=TRUE, 
                       legend = rownames(counts))

#Line Plot
df_q1  %>% group_by(DayOfWeek) %>% dplyr::summarize(mean = mean(ArrDelay)) %>%
  ggplot(aes(x= DayOfWeek, y = mean)) +
  geom_point() +
  geom_line(aes(group = 1), col = "cornflowerblue") + ggtitle("Average ArrDelay vs DayOfWeek") +
  xlab("DayOfWeek") + ylab("Average ArrDelay (mins)")

The Best Day of the Week to minimize delays is to travel on Saturday, followed by Tuesday, with average ArrDelay timing of less than 6 minutes and 8 minutes, respectively.

The longest average delays of 12-13 minutes are expected in the middle of the week, from Thursday to Friday.

Best Month of the Year

Distribution of Monthly Delay

# create table counts
counts <- table(df_q1$DelayStatus,df_q1$Month)
# Plot grouped barplot
barplot(counts, col = c("white","cornflowerblue"),
                       xlab = "Month",ylab = "Total Delay Count",
                       main = "Total Delay per Month",beside=TRUE, 
                       legend = rownames(counts))

df_q1 %>% group_by(Month) %>% dplyr::summarize(mean = mean(ArrDelay)) %>%
  ggplot(aes(x= Month, y = mean)) +
  geom_point() +
  geom_line(aes(group = 1), col = "cornflowerblue") + ggtitle("Average ArrDelay vs Month") +
  xlab("Month") + ylab("Average ArrDelay (mins)")

Based on ArrDelay, the best time of year to minimise travel delay is November, then September, with both averaging approximately 6 minutes of delay, as opposed to June and December, with more than twice the number of minutes delayed.

Due to the U.S. summer and winter vacation (School Holidays USA, 2022), June and December are projected to be popular months to travel.

Best Day of the Month

df_q1 %>% group_by(DayofMonth) %>% dplyr::summarize(mean = mean(ArrDelay)) %>%
  ggplot(aes(x= DayofMonth, y = mean)) +
  geom_point() +
  geom_line(aes(group = 1), col = "cornflowerblue") + ggtitle("Average ArrDelay vs DayofMonth") +
  xlab("DayofMonth") + ylab("Average ArrDelay (mins)")

Also, travelling in the first half of the month, around the 8th – 9th, results in an average ArrDelay of around 6 minutes, and the second half of the month with double the delay amount


Hence overall, the top 2 recommended time period to avoid flight delay is:

  • Early Morning (5am-9am) & Late Morning (9am-12pm) (≈0 minutes)
  • Saturday & Tuesday (≈6 minutes)
  • September & November (≈6 minutes)
  • Around 8th – 9th (Specifically on 8th) (≈6 minutes)

All the above periods are delayed by an average of 0 to 6 minutes. Passengers may reduce their flight delays even further by booking flights with airlines that are below the delay threshold (%).

# To save up space
rm(df_q1)


Q2. Do older planes suffer more delays?

The issue date of planes will be extracted from the planes_df data since we can judge the age of the aircraft and make our analysis based on that.

  • Distribution of Arr/Dep Delay by Issue Date
  • Distribution of Arr/Dep Delay by Issue Year
  • Comparison of Old and Normal Planes

To determine the age of the aircrafts, the engine’s issue date is obtained by mapping the plane-data.csv consisting of the plane’s ‘issue_date’, into a duplicated data frame of the ‘flight_notcancelled’ data as a new column.

# Create a new dataframe
df_q2 <- flight_notcancelled
# Check for missing value
sapply(df_q2,function(x)sum(is.na(x)))
             Year             Month        DayofMonth         DayOfWeek           DepTime        CRSDepTime 
                0                 0                 0                 0                 0                 0 
          ArrTime        CRSArrTime     UniqueCarrier         FlightNum           TailNum ActualElapsedTime 
                0                 0                 0                 0                 0                 0 
   CRSElapsedTime           AirTime          ArrDelay          DepDelay            Origin              Dest 
                0                 0                 0                 0                 0                 0 
         Distance            TaxiIn           TaxiOut         Cancelled  CancellationCode          Diverted 
                0                 0                 0                 0                 0                 0 
     CarrierDelay      WeatherDelay          NASDelay     SecurityDelay LateAircraftDelay              date 
                0                 0                 0                 0                 0                 0 
      Month_label       DelayStatus        issue_date 
                0                 0            166043 
# Check for missing value
sapply(df_q2,function(x)sum(is.na(x)))
             Year             Month        DayofMonth 
                0                 0                 0 
        DayOfWeek           DepTime        CRSDepTime 
                0                 0                 0 
          ArrTime        CRSArrTime     UniqueCarrier 
                0                 0                 0 
        FlightNum           TailNum ActualElapsedTime 
                0                 0                 0 
   CRSElapsedTime           AirTime          ArrDelay 
                0                 0                 0 
         DepDelay            Origin              Dest 
                0                 0                 0 
         Distance            TaxiIn           TaxiOut 
                0                 0                 0 
        Cancelled  CancellationCode          Diverted 
                0                 0                 0 
     CarrierDelay      WeatherDelay          NASDelay 
                0                 0                 0 
    SecurityDelay LateAircraftDelay              date 
                0                 0                 0 
      Month_label       DelayStatus        issue_date 
                0                 0            166043 


Since our dataset is huge with more than 2 million records, we will proceed to clean the 166k records of missing issue_date.

# Remove missing & None values
df_q2 <- na.omit(df_q2)
df_q2 <- df_q2 %>% 
  filter(!grepl('None', issue_date))

# Convert to datetime format
df_q2$issue_date <- as.Date(df_q2$issue_date, "%m/%d/%Y")

Distribution of Arr Delay with Issue Date

Total Sum of ArrDelay by Issue Date

# Total Sum of Delay by issue_date
ggplot(data = df_q2, aes(x=issue_date, y=ArrDelay))+
  geom_line() + ggtitle("Total ArrDelay with Issue Date") +
  xlab("Issue Date") + ylab("Total ArrDelay (mins)")

From the graph, we can see three peak points around year 1986, 2000 and 2004. Hence, we proceed to check with the mean value.

# Average of ArrDelay by issue_date
df_q2 %>% group_by(issue_date) %>% dplyr::summarize(mean = mean(ArrDelay)) %>%
ggplot(aes(x=issue_date, y=mean))+
  geom_line() + ggtitle("Average ArrDelay with Issue Date") +
  xlab("Issue Date") + ylab("Average ArrDelay (mins)")

Based on Issue Date, there seem to be more delay occurrences for planes issued after 1998. Also, the low total minutes delayed between 1976 and 1984 might be attributed to planes older than 24 years being removed owing to maintenance faults, or the airline scheduling them with ample time to avoid delays.

Distribution of Arr Delay with Issue Year

A new column of issue year and plane age is then extracted from the issue date.

df_q2$issue_year = format(as.Date(df_q2$issue_date, format="%m/%d/%Y"),"%Y")
df_q2 %>% group_by(issue_year) %>% dplyr::summarize(mean = mean(ArrDelay)) %>%
  ggplot(aes(x= issue_year, y = mean)) +
  geom_point() +
  geom_line(aes(group = 1), size=1, col = "cornflowerblue") + ggtitle("Average ArrDelay vs issue_year") +
  xlab("issue_year") + ylab("Average ArrDelay (mins)")

Older planes issued before 1984 have a higher average delay of more than 11 minutes, compared to those 1984 onwards with under 11 minutes of average delay.

Further investigations will then be made with Issue Date rather than its Year, as delays became increasingly prevalent after year 2000.

Comparing Old Planes and Normal Planes

The average age of U.S planes is 11 years, with about 25% of planes above 15 years old (Mayerowitz, 2011).

Hence, we will deem an aircraft to be old when in operation for 15+ years, where data will split into two, with planes issued before 1993 termed old, and those issued 1993 onwards termed normal..

Distribution of Old & Normal Planes

# Plot a subset of older planes
ggplot(data=subset(df_q2, issue_date < as.Date("1993-01-01")), 
       aes(x=issue_date, y=ArrDelay))+ geom_line() + ggtitle("Total ArrDelay with Issue Date (Old Planes (Before 1993))") +
  xlab("Issue Date") + ylab("Total ArrDelay (mins)")

# Plot a subset of normal planes
ggplot(data=subset(df_q2, issue_date > as.Date("1992-12-31")), 
       aes(x=issue_date, y=ArrDelay))+ geom_line() + ggtitle("Total ArrDelay with Issue Date (Normal Planes (1993 Onwards))") +
  xlab("Issue Date") + ylab("Total ArrDelay (mins)")

# Plot Average ArrDelay of older planes
ggplot(data=subset(df_q2, issue_date < as.Date("1993-01-01")), 
       aes(x=issue_date, y=ArrDelay))+  stat_summary(aes(y = ArrDelay,group=1), fun=mean, geom="line",group=1) + ggtitle("Average ArrDelay with Issue Date (Old Planes (Before 1993))") +
  xlab("Issue Date") + ylab("Average ArrDelay (mins)")

# Plot Average ArrDelay of Normal planes
ggplot(data=subset(df_q2, issue_date > as.Date("1992-12-31")), 
       aes(x=issue_date, y=ArrDelay))+  stat_summary(aes(y = ArrDelay,group=1), fun=mean, geom="line",group=1) + ggtitle("Average ArrDelay with Issue Date (Normal Planes (1993 Onwards))") +
  xlab("Issue Date") + ylab("Average ArrDelay (mins)")

# Plot Average ArrDelay by Distance of Old/Normal planes
ggplot() +  stat_summary(data=subset(df_q2, issue_date < as.Date("1993-01-01")), aes(x=Distance, y = ArrDelay,group=1, color = 'red'), fun=mean, geom="line",group=1, size=0.5) +  stat_summary(data=subset(df_q2, issue_date  > as.Date("1992-12-31")), aes(x=Distance, y = ArrDelay,group=1, color = 'cornflowerblue'), fun=mean, geom="line",group=1, size=0.5) + ggtitle("Average ArrDelay by Distance") +
  xlab("Distance (miles)") + ylab("Average ArrDelay (mins)") + 
    scale_colour_manual(name = 'Planes', 
         values =c('cornflowerblue'='cornflowerblue','red'='red'), labels = c('Normal Planes (1993 Onwards)','Old Planes (Before 1993)'))+ theme(legend.position = c(0.82,0.88))

Older planes are more likely than normal planes to have larger Average Delays as the Distance grows, with Average Arrival Delays surpassing 100 minutes or more. This further confirms that older planes tend to suffer from more delays when on a long-haul flight. Further investigations are done with Distance to identify the different delay factors that may impact old and normal planes.

library(reshape2)
package 㤼㸱reshape2㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱reshape2㤼㸲

The following objects are masked from 㤼㸱package:data.table㤼㸲:

    dcast, melt

The following object is masked from 㤼㸱package:tidyr㤼㸲:

    smiths
# Organizing data
df_q2a <- df_q2 %>% select(issue_date, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(df_q2a ,  id.vars = 'issue_date', variable.name = 'Delay')
# Plot Total Individual Delays for full data
ggplot(df_q2a, aes(issue_date, value)) +
  geom_line(aes(colour = Delay),size=0.8) + ggtitle("Total Delay by issue_date") +
  xlab("issue_date") + ylab("Total Delay (mins)")+ theme(legend.position = c(0.12,0.78))

# Organizing data
df_q2a <- df_q2 %>% select(issue_date, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(df_q2a ,  id.vars = 'issue_date', variable.name = 'Delay')
# Plot Average Individual Delays for full data
ggplot(data=df_q2a, 
       aes(x=issue_date, y=value))+  stat_summary(aes(y = value,group=1,colour = Delay), fun=mean, geom="line",group=1, size=1) + ggtitle("Average Delay with Issue Date") +
  xlab("Issue Date") + ylab("Average Delay (mins)") + theme(legend.position = c(0.12,0.78))

The full data shows Carrier Delay being prominent with planes issued 1990-1992 and 2000.

# Organizing data
df_q2a <- df_q2 %>% select(issue_date, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(df_q2a ,  id.vars = 'issue_date', variable.name = 'Delay')
# Plot Total Individual Delays for old planes
ggplot(data=subset(df_q2a, issue_date < as.Date("1993-01-01")), 
       aes(x=issue_date, y=value))+ geom_line(aes(colour = Delay),size=1) + ggtitle("Total Delay with Issue Date (Old Planes)") +
  xlab("Issue Date") + ylab("Total Delay (mins)") + theme(legend.position = c(0.12,0.78))

# Organizing data
df_q2a <- df_q2 %>% select(issue_date, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(df_q2a ,  id.vars = 'issue_date', variable.name = 'Delay')
# Plot Average Individual Delays for old planes
ggplot(data=subset(df_q2a, issue_date < as.Date("1993-01-01")), 
       aes(x=issue_date, y=value))+  stat_summary(aes(y = value,group=1,colour = Delay), fun=mean, geom="line",group=1, size=1) + ggtitle("Average Delay with Issue Date (Old Planes)") +
  xlab("Issue Date") + ylab("Average Delay (mins)")  + theme(legend.position = c(0.5,0.78))                   

Old Planes issued around 1990 are more likely to have delays due CarrierDelay, which includes maintainence etc.

# Organizing data
df_q2a <- df_q2 %>% select(issue_date, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(df_q2a ,  id.vars = 'issue_date', variable.name = 'Delay')
# Plot Total Individual Delays for normal planes
ggplot(data=subset(df_q2a, issue_date > as.Date("1992-12-31")), 
       aes(x=issue_date, y=value))+ geom_line(aes(colour = Delay),size=1) + ggtitle("Total Delay with Issue Date (Normal Planes)") +
  xlab("Issue Date") + ylab("Total Delay (mins)")                                                            

# Organizing data
df_q2a <- df_q2 %>% select(issue_date, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(df_q2a ,  id.vars = 'issue_date', variable.name = 'Delay')
# Plot Average Individual Delays for normal planes
ggplot(data=subset(df_q2a, issue_date  > as.Date("1992-12-31")), 
       aes(x=issue_date, y=value))+  stat_summary(aes(y = value,group=1,colour = Delay), fun=mean, geom="line",group=1, size =1) + ggtitle("Average Delay with Issue Date (Normal Planes)") +
  xlab("Issue Date") + ylab("Average Delay (mins)") + theme(legend.position = c(0.88,0.77))                     

Normal Planes are likely to have CarrierDelays as well.

Distance Distribution of Old & Normal Planes

Distance is then used to check the delays that Old and Normal Planes will have with respect to the number of miles that they have travelled.

# Organizing data
old_planes=subset(df_q2, issue_date < as.Date("1993-01-01")) 
old_planes <- old_planes %>% select(Distance, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(old_planes,  id.vars = 'Distance', variable.name = 'Delay')
# Plot Total Individual Delays by Distance for Old planes
ggplot(data = df_q2a, 
       aes(x=Distance, y=value))+ geom_line(aes(colour = Delay),size=1) + ggtitle("Total Delay with Distance (Old Planes)") +
  xlab("Distance (miles)") + ylab("Total Delay (mins)") + theme(legend.position = c(0.88,0.78))

# Organizing data
old_planes=subset(df_q2, issue_date < as.Date("1993-01-01")) 
old_planes <- old_planes %>% select(Distance, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(old_planes,  id.vars = 'Distance', variable.name = 'Delay')
# Plot Average Individual Delays by Distance for Old planes
ggplot(data = df_q2a, 
       aes(x=Distance, y=value))+  stat_summary(aes(y = value,group=1,colour = Delay), fun=mean, geom="line",group=1,size=1)+ ggtitle("Average Delay with Distance (Old Planes)") +
  xlab("Distance (miles)") + ylab("Average Delay (mins)") + theme(legend.position = c(0.88,0.78))

In R, although the LateAircraftDelay and NASDelay have higher average values, Carrier Delays also have occurrences of average delay ranging around 50 minutes at 1500 miles.

# Organizing data
normal_planes=subset(df_q2, issue_date > as.Date("1992-12-31")) 
normal_planes <- normal_planes %>% select(Distance, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(normal_planes,  id.vars = 'Distance', variable.name = 'Delay')
# Plot Total Individual Delays by Distance for Normal planes
ggplot(data = df_q2a, 
       aes(x=Distance, y=value))+ geom_line(aes(colour = Delay),size=1) + ggtitle("Total Delay with Distance (Normal Planes)") +
  xlab("Distance (miles)") + ylab("Total Delay (mins)") 

# Organizing data
normal_planes=subset(df_q2, issue_date > as.Date("1992-12-31")) 
normal_planes <- normal_planes %>% select(Distance, CarrierDelay, WeatherDelay, NASDelay, SecurityDelay, LateAircraftDelay)
df_q2a <- melt(normal_planes,  id.vars = 'Distance', variable.name = 'Delay')
# Plot Average Individual Delays by Distance for Normal planes
ggplot(data = df_q2a, 
       aes(x=Distance, y=value))+  stat_summary(aes(y = value,group=1,colour = Delay), fun=mean, geom="line",group=1)+ ggtitle("Average Delay with Distance (Normal Planes)") +
  xlab("Distance (miles)") + ylab("Average Delay (mins)") + theme(legend.position = c(0.88,0.78))

Normal planes have lower average values of Carrier Delays of less than 80 minutes.

Hence, it is evident that older planes would suffer more delays by having a larger value of Carrier Delay minutes while flying routes longer than 1500 miles.

Overall, the grouping by Issue Year was clear in showing that older planes do suffer from more delay, but only by up to 8 minutes on average, where LateAircraftDelay, followed by Carrier/NASDelay are the primary causes.

When utilized for long-haul flights, older planes are also more likely to have delays due to carrier delays that might have resulted from aircraft maintenance or inspection.

The difference in delay however is quite negligible and it might be due to airlines “padding” and scheduling extra time for flights to prevent flights from being classified as delayed (Kramer, 2019).

# To save up space
rm(df_q2)
rm(df_q2a)
rm(counts)
rm(normal_planes)
rm(old_planes)


Q3. How does the number of people flying between different locations change over time

We will find the most popular routes to gauge the number of people flying between these different locations.

  • Distribution by Flight Routes
  • Distribution by State
# Create new dataframe
df_q3 <- flight_notcancelled

Distribution by Flight Routes

To begin, the Origin and Destination are combined into a new ‘FlightRoute’ column (e.g., OGG to HNL).

# Create new column as its flight route
df_q3$FlightRoute <- paste(df_q3$Origin, "to", df_q3$Dest)
# Most popular routes
df_q3 %>% 
  group_by(df_q3$FlightRoute) %>%
  dplyr::summarize(Count=n()) %>%
  arrange(desc(Count))

The top five most popular routes are:

  1. OGG to HNL
  2. HNL to OGG
  3. LAX to LAS
  4. SAN to LAX
  5. LAX to SAN

Out of the 5196 distinct routes, the Top 5 routes with the highest count are identified to examine if the number of flights has changed over the course of the year.

# BarPlot
ggplot(df_q3, aes(x= Month)) + geom_bar(aes(fill=as.factor(FlightRoute))) + ggtitle("Barplot of FlightRoute counts")

# BarPlot
ggplot(df_q3, aes(x= Month)) + geom_bar(aes(fill=as.factor(FlightRoute))) + ggtitle("Barplot of FlightRoute counts")

# FlightRoute Line Plot
ggplot(df_q3, aes(x=Month, group = factor(FlightRoute) , colour=factor(FlightRoute))) + 
  geom_line(stat = 'count',size=1) + ggtitle("Count of flights per Flight Route by Month") +
  xlab("Month") + ylab("Total Flight Counts")

For the first five months, the flight routes [OGG to HNL] and [HNL to OGG] had roughly 50 fewer flights than the other three routes.

However, from June to August, both routes begin to increase in their number of flights, reaching over 100 more flights than the other routes.

Between September and December, these routes continue to have more flights than the others.

Distribution by State

To make the most of the data, the States were queried to justify the number of people flying interstate and intrastate.

# Query state from airport_df into dataframe
df_q3$state <- airport_df$state[match(df_q3$Origin, airport_df$iata)]
# Query state from airport_df into dataframe
df_q3$state <- airport_df$state[match(df_q3$Origin, airport_df$iata)]
# Most popular states
df_q3 %>% 
  group_by(df_q3$state) %>%
  dplyr::summarize(Count=n()) %>%
  arrange(desc(Count))

Top 3 popular states are:

  1. California
  2. Texas
  3. Illinois
# Select the top 3 states
df_q3 <- df_q3[df_q3$state %in% c('CA', 'TX', 'IL'), ]
# Create new column of state flight routes
df_q3$FlightRoute <- paste(df_q3$state, "to", df_q3$state[-1:0])
# Create new column of state flight routes
df_q3$FlightRoute <- paste(df_q3$state, "to", df_q3$state[-1:0])
# Flight Routes of Top 3 states
ggplot(df_q3, aes(x=Month, group = factor(FlightRoute) , colour=factor(FlightRoute))) + 
  geom_line(stat = 'count',size=1) + ggtitle("Count of flights per Flight Route by Month") +
  xlab("Month") + ylab("Total Flight Counts")

For both intrastate and interstate, February and September have the fewest flights with around 100 less than other months. As expected, the number of flights increases in the middle of the year, between June and August.

With two distinct methodologies, it is evident that February has the fewest flights, followed by September. The more popular travelling months are during June to August, which is likely due to the summer holidays in the USA which last 11 weeks from June to August (School Holidays USA, 2022).

# To save up space
rm(df_q3)


Q4. Can you detect cascading failures as delays in one airport create delays in others?

Cascading failures occur when a flight delay for one plane in an airport causes a flight delay in another.

This is explained by the existing variable ‘LateAircraftDelay’ which describes how a particular flight delayed in its Origin arrives late in its Destination, then affecting the next flight’s departure since the same plane was used. The ripple impact of a previous delay at downstream airports hence causes cascading failures.

Since Tail Numbers are identification numbers on aircraft, it will be easier to focus on data with ‘LateAircraftDelay’, then focus on a selected aircraft and observe its flight schedule.

We will approach the question as follow:

  • Methodology
  • Initial Test
  • Secondary Testing
# Create new dataframe
df_q4 <- merged_df
# Find Date with highest value counts
df_q4 %>% 
  group_by(df_q4$TailNum) %>%
  dplyr::summarize(Count=n()) %>%
  arrange(desc(Count))

‘0’ and ‘000000’ are likely private confidential TailNums, so we ignore those data and focus on the top 2 highest counts of TailNum:

  • N478HA
  • N308SW

Methodology

We will approach the dataset in the following way to effectively illustrate cascading failures, that is, delays in one airport will cause delays in another:

  1. Find Highest ‘TailNum’ count (Higher chance of continuous flights in a day)
  2. Extract data fitting condition of Highest ‘TailNum’ & ‘LateAircraftDelay’ > 15 minutes
  3. Find Highest ‘Date’ count from extracted data (Higher chance of continuous flights in a day)
  4. Extract overall data fitting conditions of Highest ‘TailNum’ & Highest ‘Date’ count
  5. Sort data by ‘DepTime’ (To see the flight schedule of same aircraft by Departure Timing)

This approach will allow us to assess if a delayed flight in one airport may cause a delay in another. To ensure that the data extraction and analysis approaches are valid, two separate tests will be conducted.

Initial Test

The initial test used N308SW to identify the highest date counts.

# First extraction of data with top most TailNum counts
df_q4_1 <- merged_df[merged_df$TailNum %in% c('N308SW') & merged_df$LateAircraftDelay > 15, ]
df_q4_1 %>% 
  group_by(df_q4_1$date) %>%
  dplyr::summarize(Count=n()) %>%
  arrange(desc(Count))


We will focus on the date with the highest count to see if there is any relation that leads to cascading failure.

# Extracting dataset that matches our findings
df_q4_1 <- merged_df[merged_df$TailNum %in% c('N308SW') & merged_df$date == ('2006-03-31'), ]
df_q4_1[order(DepTime)][-c(1),]

It is clear that one flight’s delay in an airport can cause cascading failures in another.

Taking Flight 458 as an example: has to depart at 1835 (6:35pm) but delayed for 52 mins till 1927 (7:27pm), flying from DAL to SAT. Supposed to reach 1935 (7:35pm) but delayed 50 mins and reached 2025 (8:25pm).

This 77 mins delay thereafter caused the next flight from SAT to DAL (FlightNum 178) to have departure delay for 45 minutes (43 mins for LateAircraftDelay) also since its scheduled departure time is at 2005 (8:05pm), but it only took off at 2050 (8:50pm). It also arrived 43 minutes later than expected time of 2100 at 2143.

We will further check on the full data (same date, same TailNum) to see the flights that were not included in the sample that we extracted, so as to make a more conclusive statement.

# Matching with full data to confirm assumption
df_q4_1 <- mergeddf[mergeddf$TailNum %in% c('N308SW') & mergeddf$date == ('2006-03-31'), ]
df_q4_1[order(DepTime)][-c(1:8, 10,12,14),] %>% select(10,5:8,29,15:18,30,9,11) #Select important columns

Considering Flight 755: there was a 52 minutes ArrDelay in the Destination Airport (LIT). Due to the delay, Flight 458, scheduled to depart at 1700 (5pm), was delayed for 60 mins till 1800 (6pm), flying from LIT to DAL.

Supposed to reach 1805 (6:05pm) but reached 1900 (7pm). With the 55-min delay, the next flight from DAL to SAT (FlightNum 458) was subsequently delayed for 52 minutes (50-min for LateAircraftDelay) since its scheduled departure time was at 1835 (6:35pm), but it only took off at 1927 (7:27pm). It then landed 50 minutes later, at 2143, than its expected time of 2100.

Since the DepTime overlapped with its previous flight’s CRSArrTime, the delay persisted and escalated till near midnight of the day, resulting in cascading failure.

With this, we can observe the cascading failure for Carrier WN of same TailNum, which persisted throughout the night at different airports (LIT, DAL, SAT, TUL etc.)

Since the CRSDepTime overlapped with its previous flight’s ArrTime, the delay persisted and escalated till near midnight of the day, resulting in cascading failures.

Secondary Testing

The secondary test used N478HA to identify the highest date counts.

# First extraction of data with top most TailNum counts
df_q4_2 <- merged_df[merged_df$TailNum %in% c('N478HA') & merged_df$LateAircraftDelay > 15, ]
df_q4_2 %>% 
  group_by(df_q4_2$date) %>%
  dplyr::summarize(Count=n()) %>%
  arrange(desc(Count))


We will focus on the date with the highest count to see if there is any relation that leads to cascading failure.

# Extracting dataset that matches our findings
df_q4_2 <- merged_df[merged_df$TailNum %in% c('N478HA') & merged_df$date == ('2006-03-31'), ]
df_q4_2[order(DepTime)]

Taking Flight 179 as an example: has to depart at 1328 (1:28pm) but delayed for 82 mins till 1450 (2:50pm), flying from KOA to OGG. Supposed to reach 1357 (1:57pm) but delayed 87 mins and reached 1524 (3:24pm).

We will further check on the full data (same date, same TailNum) to see the flights that were not included in the sample that we extracted, so as to make a more conclusive statement.

# Matching with full data to confirm assumption
df_q4_2 <- mergeddf[mergeddf$TailNum %in% c('N478HA') & mergeddf$date == ('2006-03-31'), ]
df_q4_2[order(DepTime)][-c(1:4),]  %>% select(10,5:8,29,15:18,30,9,11) #Select important columns
NA

This 87 mins arrival delay of FlightNum 179 thereafter caused the next flight from OGG to HNL (FlightNum 179) to have departure delay for 79 minutes (89 mins for LateAircraftDelay) also since its scheduled departure time is at 1425 (2:25pm), but it only took off at 1544 (3:44pm). It also arrived 89 minutes later than expected time of 1459 at 1628. We can also see from the day that one flight delay in an airport will cause cascading failures, contributing to ArrDelay and LateAircraftDelay for flights, hence confirming our observation.

It is also worth noting the instances where subsequent flights might not use the same TailNum, but they are assumed due to a lack of schedule information. A total of two tests were conducted in R, to demonstrate the same effect of cascading failures where delays at one airport causes delays in another. The previous flight’s late arrival, which used the same plane that would be departing, caused the subsequent flight’s delay. As a result, the current flight will depart late, setting off a chain reaction causing passengers at other airports to board the plane much later as well.

# To save space
rm(df_q4)
rm(df_q4_1)
rm(df_q4_2)


Q5. Use the available variables to construct a model that predicts delays.

With the flight data labelled, Supervised Learning algorithms such as Multiple Linear Regression and Random Forest are used to construct Regression and Classification prediction models in R.

This works by allowing the model to predict the label of new data points based on past data.

To predict delays, these few supervised learning models with selected variables are used:

  • Multiple Linear Regression
  • Random Forest
# Select variable columns
df_q5 <- df_q5 %>% select(Month, UniqueCarrier, DepTime, CRSDepTime, ArrTime, CRSArrTime, ArrDelay, DepDelay,Distance,TaxiIn,TaxiOut, ActualElapsedTime,AirTime, DelayStatus)
# Select variable columns
df_q5 <- df_q5 %>% select(Month, UniqueCarrier, DepTime, CRSDepTime, ArrTime, CRSArrTime, ArrDelay, DepDelay,Distance,TaxiIn,TaxiOut, ActualElapsedTime,AirTime, DelayStatus)

Time-related and Factor columns are selected as predictor varaibles for analysis.

# Change selected var as factors
df_q5$UniqueCarrier<- as.factor(df_q5$UniqueCarrier)
df_q5$Month <- as.factor(df_q5$Month)
# Hold-out Validation method
library(caret)
set.seed(42)

F_sample = createDataPartition(y=df_q5$ArrDelay, p = 0.7, list = F)
train = df_q5[F_sample,]
test = df_q5[-F_sample,]

First, ‘CreateDataPartition’ feature will create a train-test split to prevent overfitting, with Trainset accounting for 70%, and Testset for 30%. The model will be trained using the Trainset data, and its performance will be evaluated by predicting with the unseen Testset. For reproduction, random state is set to a random seed 42.

The response variable Y, for the Regression model will comprise of ArrDelay (minutes).

Multiple Linear Regression

MLR is a regression model which enables us to understand and estimate relationships between multiple variables.

# Load model
mlr_model <- lm(ArrDelay ~. -DelayStatus, data = train)
summary(mlr_model)

Call:
lm(formula = ArrDelay ~ . - DelayStatus, data = train)

Residuals:
    Min      1Q  Median      3Q     Max 
-544.76   -4.62    0.28    4.76  964.24 

Coefficients:
                    Estimate Std. Error  t value Pr(>|t|)    
(Intercept)       -2.291e+01  9.164e-02 -250.057  < 2e-16 ***
Month2             5.663e-01  5.064e-02   11.183  < 2e-16 ***
Month3             1.807e-01  4.890e-02    3.694  0.00022 ***
Month4             6.833e-01  4.921e-02   13.887  < 2e-16 ***
Month5             8.454e-01  4.885e-02   17.308  < 2e-16 ***
Month6             1.209e+00  4.908e-02   24.634  < 2e-16 ***
Month7             8.584e-01  4.870e-02   17.626  < 2e-16 ***
Month8             7.547e-01  4.840e-02   15.594  < 2e-16 ***
Month9             5.705e-01  4.946e-02   11.535  < 2e-16 ***
Month10            8.010e-01  4.882e-02   16.406  < 2e-16 ***
Month11           -5.906e-01  4.928e-02  -11.985  < 2e-16 ***
Month12            1.041e-02  4.924e-02    0.212  0.83248    
UniqueCarrierAA    3.581e+00  8.346e-02   42.911  < 2e-16 ***
UniqueCarrierAQ    1.144e+01  1.526e-01   74.939  < 2e-16 ***
UniqueCarrierAS    4.942e+00  1.014e-01   48.753  < 2e-16 ***
UniqueCarrierB6   -1.331e+00  9.994e-02  -13.316  < 2e-16 ***
UniqueCarrierCO   -1.652e+00  9.025e-02  -18.308  < 2e-16 ***
UniqueCarrierDL    8.364e-01  8.509e-02    9.830  < 2e-16 ***
UniqueCarrierEV    5.848e-01  9.131e-02    6.405 1.51e-10 ***
UniqueCarrierF9    6.085e+00  1.159e-01   52.504  < 2e-16 ***
UniqueCarrierFL    3.379e+00  9.269e-02   36.459  < 2e-16 ***
UniqueCarrierHA    1.273e+01  1.379e-01   92.327  < 2e-16 ***
UniqueCarrierMQ    2.741e+00  8.388e-02   32.683  < 2e-16 ***
UniqueCarrierNW    3.897e+00  8.614e-02   45.239  < 2e-16 ***
UniqueCarrierOH    1.801e-02  9.246e-02    0.195  0.84558    
UniqueCarrierOO    4.397e+00  8.338e-02   52.737  < 2e-16 ***
UniqueCarrierTZ    7.791e+00  2.775e-01   28.073  < 2e-16 ***
UniqueCarrierUA    2.812e+00  8.549e-02   32.895  < 2e-16 ***
UniqueCarrierUS    2.171e+00  8.489e-02   25.568  < 2e-16 ***
UniqueCarrierWN    4.935e+00  7.991e-02   61.761  < 2e-16 ***
UniqueCarrierXE    8.736e-02  8.571e-02    1.019  0.30808    
UniqueCarrierYV    1.168e+00  9.015e-02   12.951  < 2e-16 ***
DepTime           -4.595e-04  8.438e-05   -5.445 5.17e-08 ***
CRSDepTime         8.421e-04  8.466e-05    9.947  < 2e-16 ***
ArrTime            8.532e-04  4.092e-05   20.851  < 2e-16 ***
CRSArrTime        -1.974e-03  4.941e-05  -39.958  < 2e-16 ***
DepDelay           9.816e-01  3.036e-04 3233.017  < 2e-16 ***
Distance          -4.051e-02  7.202e-05 -562.542  < 2e-16 ***
TaxiIn             7.041e-02  1.078e-03   65.327  < 2e-16 ***
TaxiOut            5.560e-01  1.466e-03  379.253  < 2e-16 ***
ActualElapsedTime  2.747e-01  1.116e-03  246.238  < 2e-16 ***
AirTime            4.863e-02  9.890e-04   49.168  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.984 on 1001672 degrees of freedom
Multiple R-squared:  0.931, Adjusted R-squared:  0.931 
F-statistic: 3.295e+05 on 41 and 1001672 DF,  p-value: < 2.2e-16

The model summary is then used to identify the significant variables affecting ArrDelay, excluding DelayStatus since they are related.

The R2 score is 0.931, where predictor variables in the model explained 93.1% of the variation in Y (ArrDelay). RMSE is also considerably fitting at 9.98.

# Predicting delays
delayPred <- predict(mlr_model, test)  
# Create ActualnPred dataframe
ActualnPred <- data.frame(cbind(Actual=test$ArrDelay, Predicted=round(delayPred)))
ActualnPred
# 96.4% correlation accuracy of similar directional movement
correlation_accuracy <- cor(ActualnPred)
correlation_accuracy
             Actual Predicted
Actual    1.0000000 0.9641986
Predicted 0.9641986 1.0000000

The Correlation Accuracy of 96.4% shows how the actual and predicted values have similar directional movements.

sqrt(mean((test$ArrDelay - delayPred)^2))
[1] 9.996467

The R2 score is 0.9369 and Root Mean Squared Error (RMSE) is 9.676569. The R2 value shows that the predictor variables in the model are able to explain 93.69% of the variation in ArrDelay.

Random Forest

Used for Classification and Regression, Random Forest is a Supervised Learning algorithm that constructs many decision trees.

R uses classification model where the output is chosen by a majority vote among decision trees.

set.seed(42)
require(caTools)
sample = sample.split(df_q5$DelayStatus, SplitRatio = .70)
train = subset(df_q5, select = -c(ArrDelay), sample == TRUE)
test  = subset(df_q5, select = -c(ArrDelay), sample == FALSE)
train$DelayStatus <- as.character(train$DelayStatus)
train$DelayStatus <- as.factor(train$DelayStatus)

Hence, a new train-test set with response variable Y as DelayStatus of similar parameters (30% Test) is created, with the ArrDelay column removed to prevent multicollinearity.

rf

Call:
 randomForest(formula = DelayStatus ~ ., data = train, ntree = 10,      random_state = 42) 
               Type of random forest: classification
                     Number of trees: 10
No. of variables tried at each split: 3

        OOB estimate of  error rate: 6.9%
Confusion matrix:
       0      1 class.error
0 734411  29334  0.03840811
1  39044 188485  0.17160010
rf

Call:
 randomForest(formula = DelayStatus ~ ., data = train, ntree = 10,      random_state = 42) 
               Type of random forest: classification
                     Number of trees: 10
No. of variables tried at each split: 3

        OOB estimate of  error rate: 6.9%
Confusion matrix:
       0      1 class.error
0 734411  29334  0.03840811
1  39044 188485  0.17160010

The Out-of-bag error estimate is at 6.9%, where Accuracy = 1 -OOB error, so the model has an accuracy of 93.1%.

delayPred <- predict(rf, test)  
delayPred <- predict(rf, test)  
test$DelayStatus <- as.factor(test$DelayStatus)

# Create ActualnPred dataframe
ActualnPred <- data.frame(cbind(Actual=test$DelayStatus, Predicted=delayPred))
ActualnPred

RF Model without DepDelay

Since DepDelay and ArrDelay have a positive linear relationship which might lead to multicollinearity, we will investigate to see if there are changes to the factors that might affect DelayStatus.

rf <- randomForest(DelayStatus ~ .- DepDelay, data=train, ntree=10, random_state =42)
rf

Call:
 randomForest(formula = DelayStatus ~ . - DepDelay, data = train,      ntree = 10, random_state = 42) 
               Type of random forest: classification
                     Number of trees: 10
No. of variables tried at each split: 3

        OOB estimate of  error rate: 6.18%
Confusion matrix:
       0      1 class.error
0 740506  23440   0.0306828
1  37786 189765   0.1660551
delayPred <- predict(rf, test)  

Models with and without DepDelay show the changes in attributing factors causing DelayStatus, where the top 6 factors for both models are similar.

delayPred <- predict(rf, test)  
test$DelayStatus <- as.factor(test$DelayStatus)

# Create ActualnPred dataframe
ActualnPred <- data.frame(cbind(Actual=test$DelayStatus, Predicted=delayPred))
ActualnPred

In total, one regression and one classification method were tested out in R. Overall, the best model to predict the continuous ArrDelay would be MLR with 95.7% accuracy. To predict the binary outcome DelayStatus, Random Forest model is around 93.1% accurate.

LS0tDQp0aXRsZTogIkZsaWdodCBBbmFseXNpcyBpbiB0aGUgVS5TLiINCmF1dGhvcjogJ0V2YW5nZWxpbmUgVGFuJw0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0aGVtZTogdW5pdGVkDQotLS0NCg0KDQojIEludHJvZHVjdGlvbg0KDQpFdmVyeSB5ZWFyLCBuZWFybHkgMjUlIG9mIGFpcmxpbmUgZmxpZ2h0cyBhcmUgZGVsYXllZCBvciBjYW5jZWxsZWQsIGNvc3RpbmcgdHJhdmVsbGVycyBvdmVyICQzMCBiaWxsaW9uIGluIGxvc3QgdGltZSBhbmQgbW9uZXkgIEZsaWdodCBkZWxheXMgaGF2ZSBsb25nIGJlZW4gYSBjYXVzZSBvZiBkaXNzYXRpc2ZhY3Rpb24gaW4gdGhlIGFpcmxpbmUgaW5kdXN0cnksIGFzIHdlbGwgYXMgYSBzb3VyY2Ugb2YgYW5ub3lhbmNlIGZvciBwYXNzZW5nZXJzIGFuZCBjYXJyaWVycy4gPHA+DQpPdXIgZ29hbCBpcyB0byB1c2UgdGhlIG1hc3NpdmUgYW1vdW50IG9mIGFpcmxpbmUgZGF0YSB0byB2aXN1YWxpc2UgYW5kIHN0dWR5IHRoZSBmbGlnaHQgcGF0dGVybnMgYW5kIHByZWRpY3QgaWYgYSBmbGlnaHQgd2lsbCBiZSBkZWxheWVkLiBGb3IgdGhpcyBzdHVkeSwgYm90aCBQeXRob24gYW5kIFIgd2lsbCBiZSB1c2VkIHRvIGludmVzdGlnYXRlIDIgeWVhcnPigJkgd29ydGggb2YgZGF0YSwgc2luY2UgdHdvIGZ1bGwgYnVzaW5lc3MgY3ljbGVzIGFyZSBhZGVxdWF0ZSBpbiByZWR1Y2luZyBiaWFzIGZvciBvbmUgY3ljbGUuDQoNCg0KVGhpcyBub3RlYm9vayBhaW1zIHRvIGxvb2sgYXQgdGhlc2UgcXVlc3Rpb25zIHJlZ2FyZGluZyBmbGlnaHQgdHJhdmVsOg0KDQoxLiBXaGVuIGlzIHRoZSBiZXN0IHRpbWUgb2YgZGF5LCBkYXkgb2YgdGhlIHdlZWssIGFuZCB0aW1lIG9mIHllYXIgdG8gZmx5IHRvIG1pbmltaXNlIGRlbGF5cz8NCjIuIERvIG9sZGVyIHBsYW5lcyBzdWZmZXIgbW9yZSBkZWxheXM/DQozLiBIb3cgZG9lcyB0aGUgbnVtYmVyIG9mIHBlb3BsZSBmbHlpbmcgYmV0d2VlbiBkaWZmZXJlbnQgbG9jYXRpb25zIGNoYW5nZSBvdmVyIHRpbWU/DQo0LiBDYW4geW91IGRldGVjdCBjYXNjYWRpbmcgZmFpbHVyZXMgYXMgZGVsYXlzIGluIG9uZSBhaXJwb3J0IGNyZWF0ZSBkZWxheXMgaW4gb3RoZXJzPw0KNS4gVXNlIHRoZSBhdmFpbGFibGUgdmFyaWFibGVzIHRvIGNvbnN0cnVjdCBhIG1vZGVsIHRoYXQgcHJlZGljdHMgZGVsYXlzLg0KDQo8cD4NCg0KRGF0YTogW0FpcmxpbmVzIC0gSGFydmFyZCBEYXRhdmVyc2VdKGh0dHBzOi8vZG9pLm9yZy8xMC43OTEwL0RWTi9IRzdOVjcpDQo8YnI+DQoNCiMgSW1wb3J0IGRhdGEgYW5kIGxpYnJhcmllcw0KYGBge3IgZGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KCJkYXRhLnRhYmxlIikNCmxpYnJhcnkocGx5cikNCm9wdGlvbnMod2Fybj0tMSkNCg0Kc2V0d2QoIkQ6L2V0NF9lL2NvdXJzZXdvcmsvMjAyMSIpDQoNCiMgSW1wb3J0ICYgcHJlcGFyZSB0aGUgMjAwNiBhbmQgMjAwNyBkYXRhc2V0DQpkZl8yMDA2ID0gZnJlYWQoIjIwMDYuY3N2LmJ6MiIpDQpkZl8yMDA3ID0gZnJlYWQoIjIwMDcuY3N2LmJ6MiIpDQoNCm1lcmdlZGRmID0gcmJpbmQoZGZfMjAwNiwgZGZfMjAwNykNCmFpcnBvcnRfZGYgPC0gcmVhZC5jc3YyKCdhaXJwb3J0cy5jc3YnLHNlcCA9ICIsIixoZWFkZXIgPSBUUlVFKQ0KY2Fycmllcl9kZiA8LSByZWFkLmNzdjIoJ2NhcnJpZXJzLmNzdicsc2VwID0gIiwiLGhlYWRlciA9IFRSVUUpDQpwbGFuZXNfZGYgPC0gcmVhZC5jc3YyKCdEOi9ldDRfZS9jb3Vyc2V3b3JrLzIwMjEvcGxhbmUtZGF0YS5jc3YnLHNlcCA9ICIsIixoZWFkZXIgPSBUUlVFKQ0KYGBgDQoNCjxicj4NCg0KIyBVbmRlcnN0YW5kaW5nIERhdGENCmBgYHtyIGRldGFpbHN9DQojIENyZWF0ZSBEYXRlIGNvbHVtbg0KbWVyZ2VkZGYkZGF0ZSA8LSBhcy5EYXRlKHdpdGgobWVyZ2VkZGYsIHBhc3RlKFllYXIsIE1vbnRoLCBEYXlvZk1vbnRoLCBzZXA9Ii0iKSksICIlWS0lbS0lZCIpDQojIENyZWF0ZSBjb3B5IG9mIG1lcmdlZCBkYXRhDQptZXJnZWRfZGYgPSBtZXJnZWRkZg0KaGVhZChtZXJnZWRfZGYpDQpkaW0obWVyZ2VkX2RmKQ0KYGBgDQogPGJyPg0KVGhlcmUgYXJlIGFsbW9zdCAxNC42IG1pbGxpb24gcmVjb3JkcyB3aXRoIDI5IHZhcmlhYmxlIGNvbHVtbnMuIFRoZSBjb2x1bW5zIGluY2x1ZGUgdGhlIGFpcmxpbmUgYW5kIGZsaWdodCBkZXRhaWxzIGV0Yy4gYW5kIGFyZSBtb3N0bHkgdGltZS1yZWxhdGVkIChpbiBtaW5zKS4NCjxicj4NCg0KYGBge3Igc3VtbWFyeX0NCnN1bW1hcnkobWVyZ2VkX2RmKQ0Kc3RyKG1lcmdlZF9kZikNCmBgYA0KDQojIERhdGEgUHJlLVByb2Nlc3NpbmcvQ2xlYW5pbmcNCg0KIyMgQ3JlYXRpbmcgc2FtcGxlIG9mIGVudGlyZSBkYXRhIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNCkR1ZSB0byB0aGUgbGFyZ2UgZGF0YXNldCByZXF1aXJpbmcgbW9yZSB0aW1lIHRvIGV4ZWN1dGUsIHdlIHdpbGwgcmFuZG9tbHkgc2VsZWN0IDEwJSBvZiB0aGUgZGF0YSBmb3IgcXVpY2sgYW5hbHlzaXMuDQoNCldlIHRoZW4gZmlsdGVyIHRoZSBkYXRhIGludG8gQ2FuY2VsbGVkIGFuZCBOb24tY2FuY2VsbGVkIGZsaWdodHMuDQo8YnI+DQpgYGB7ciBzYW1wbGV9DQojIFJlbW92ZSBvcmlnaW5hbCBkYXRhICYgdGFrZSBzYW1wbGUgKDEwJSkgb2YgbWVyZ2VkIGRhdGEgKHNhdmUgc3BhY2UgJiBsb2FkIGZhc3RlcikNCnJtKGRmXzIwMDYpDQpybShkZl8yMDA3KQ0Kc2V0LnNlZWQoNDIpDQptZXJnZWRfZGYgPSBzYW1wbGVfZnJhYyhtZXJnZWRfZGYsIDAuMTAsIHJlcGxhY2UgPSBGQUxTRSkNCmBgYA0KDQoNCmBgYHtyIHJlbmFtZX0NCiMgUmVuYW1lIENhbmNlbGxhdGlvbkNvZGUgQ29sdW1uDQptZXJnZWRfZGYkQ2FuY2VsbGF0aW9uQ29kZSA8LSBtYXB2YWx1ZXMobWVyZ2VkX2RmJENhbmNlbGxhdGlvbkNvZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tID0gYygiQSIsICJCIiwgIkMiLCAiRCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBjKCJDYXJyaWVyIiwgIldlYXRoZXIiLCAiTmF0aW9uYWwgQWlyIFN5c3RlbSAoTkFTKSIsICJTZWN1cml0eSIpKQ0KDQptZXJnZWRfZGYkQ2FuY2VsbGVkIDwtIG1hcHZhbHVlcyhtZXJnZWRfZGYkQ2FuY2VsbGVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSA9IGMoMSwgMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IGMoIkNhbmNlbGxlZCIsICJOb3QgQ2FuY2VsbGVkIikpDQptZXJnZWRfZGYkRGl2ZXJ0ZWQgPC0gbWFwdmFsdWVzKG1lcmdlZF9kZiREaXZlcnRlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyb20gPSBjKDEsIDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBjKCJEaXZlcnRlZCIsICJOb3QgRGl2ZXJ0ZWQiKSkNCmBgYA0KDQoNCg0KDQpgYGB7ciBtaXNzaW5nfQ0KIyBDaGVjayBmb3IgbWlzc2luZyB2YWx1ZXMNCnN1bShpcy5uYShtZXJnZWRfZGYpKQ0KDQojIE1pc3NpbmcgdmFsdWVzIHBlciBjb2x1bW4NCnNhcHBseShtZXJnZWRfZGYsZnVuY3Rpb24oeClzdW0oaXMubmEoeCkpKQ0KYGBgDQoNCmBgYHtyIGNsZWFuaW5nIGR1cGxpY2F0ZXN9DQojIENoZWNrIGZvciBkdXBsaWNhdGVzDQpzdW0oZHVwbGljYXRlZChtZXJnZWRfZGYpKQ0KZGlzdGluY3QobWVyZ2VkX2RmKQ0KYGBgDQo8YnI+DQoNCk1pc3NpbmcgZGF0YSBpcyBhbHNvIGhhbmRsZWQgdXNpbmcgbGluZWFyIGludGVycG9sYXRpb24gdG8gZXN0aW1hdGUgdW5rbm93biBkYXRhIHZhbHVlcyBiZXR3ZWVuIGtub3duIGRhdGEgdmFsdWVzLCBhbmQgZHVwbGljYXRlcyBhcmUgcmVtb3ZlZC4gDQoNCmBgYHtyIGludGVycG9sYXRpb259DQojIEltcHV0YXRlIE51bGwgdmFsdWVzIHdpdGggaW50ZXJwb2xhdGlvbg0KDQptZXJnZWRfZGYgPC0gbWVyZ2VkX2RmICU+JQ0KICAgICAgICBtdXRhdGUoRGVwVGltZSA9IG5hLmFwcHJveChEZXBUaW1lKSkNCm1lcmdlZF9kZiA8LSBtZXJnZWRfZGYgJT4lDQogICAgICAgIG11dGF0ZShBcnJUaW1lID0gbmEuYXBwcm94KEFyclRpbWUpKQ0KbWVyZ2VkX2RmIDwtIG1lcmdlZF9kZiAlPiUNCiAgICAgICAgbXV0YXRlKEFjdHVhbEVsYXBzZWRUaW1lID0gbmEuYXBwcm94KEFjdHVhbEVsYXBzZWRUaW1lKSkNCm1lcmdlZF9kZiA8LSBtZXJnZWRfZGYgJT4lDQogICAgICAgIG11dGF0ZShDUlNFbGFwc2VkVGltZSA9IG5hLmFwcHJveChDUlNFbGFwc2VkVGltZSkpDQptZXJnZWRfZGYgPC0gbWVyZ2VkX2RmICU+JQ0KICAgICAgICBtdXRhdGUoQWlyVGltZSA9IG5hLmFwcHJveChBaXJUaW1lKSkNCm1lcmdlZF9kZiA8LSBtZXJnZWRfZGYgJT4lDQogICAgICAgIG11dGF0ZShBcnJEZWxheSA9IG5hLmFwcHJveChBcnJEZWxheSkpDQptZXJnZWRfZGYgPC0gbWVyZ2VkX2RmICU+JQ0KICAgICAgICBtdXRhdGUoRGVwRGVsYXkgPSBuYS5hcHByb3goRGVwRGVsYXkpKQ0KYGBgDQoNCmBgYHtyIHR5cGV9DQojIENoYW5nZSBzZWxlY3RlZCBkYXRhIHR5cGVzIChOdW1lcmljIHRvIENhdGVnb3JpY2FsKQ0KbWVyZ2VkX2RmJFllYXIgPC0gYXMuZmFjdG9yKG1lcmdlZF9kZiRZZWFyKQ0KbWVyZ2VkX2RmJE1vbnRoIDwtIGFzLmZhY3RvcihtZXJnZWRfZGYkTW9udGgpDQptZXJnZWRfZGYkRGF5b2ZNb250aCA8LSBhcy5mYWN0b3IobWVyZ2VkX2RmJERheW9mTW9udGgpDQptZXJnZWRfZGYkRGF5T2ZXZWVrIDwtIGFzLmZhY3RvcihtZXJnZWRfZGYkRGF5T2ZXZWVrKQ0KbWVyZ2VkX2RmJEZsaWdodE51bSA8LSBhcy5mYWN0b3IobWVyZ2VkX2RmJEZsaWdodE51bSkNCm1lcmdlZF9kZiRDYW5jZWxsZWQgPC0gYXMuZmFjdG9yKG1lcmdlZF9kZiRDYW5jZWxsZWQpDQptZXJnZWRfZGYkRGl2ZXJ0ZWQgPC0gYXMuZmFjdG9yKG1lcmdlZF9kZiREaXZlcnRlZCkNCmBgYA0KDQpgYGB7ciBtb250aCBsYWJlbH0NCiMgQ3JlYXRlIGxhYmVsbGVkIGNvbHVtbiBmb3IgZWFzaWVyIHZpc3VhbGlzYXRpb24NCm1lcmdlZF9kZiRNb250aF9sYWJlbCA8LSBtb250aC5hYmJbbWVyZ2VkX2RmJE1vbnRoXQ0KDQpgYGANCg0KDQpgYGB7ciBjaGVjayBtaXNzaW5nfQ0KIyBDaGVjayBmb3IgbWlzc2luZyB2YWx1ZXMgcGVyIGNvbHVtbg0Kc2FwcGx5KG1lcmdlZF9kZixmdW5jdGlvbih4KXN1bShpcy5uYSh4KSkpDQoNCmBgYA0KPGJyPg0KVGhlIG51bGwgdmFsdWVzIGhhdmUgYWxsIGJlZW4gcmVtb3ZlZCBmcm9tIHRoZSBkYXRhZnJhbWUuIA0KPGJyPg0KDQpgYGB7ciBmaWx0ZXJ9DQpmbGlnaHRfY2FuY2VsbGVkIDwtIG1lcmdlZF9kZiAlPiUgZmlsdGVyX2F0KHZhcnMoQ2FuY2VsbGVkKSwgYW55X3ZhcnMoLiAlaW4lIGMoJ0NhbmNlbGxlZCcpKSkNCmZsaWdodF9ub3RjYW5jZWxsZWQgPC0gbWVyZ2VkX2RmICU+JSBmaWx0ZXJfYXQodmFycyhDYW5jZWxsZWQpLCBhbnlfdmFycyguICVpbiUgYygnTm90IENhbmNlbGxlZCcpKSkNCg0KYGBgDQo8YnI+DQoNCiMjIENyZWF0aW5nIERlbGF5IFN0YXR1cw0KDQpXZSBhc3N1bWUgdGhhdCBhIGRlbGF5ZWQgZmxpZ2h0IGlzIGVxdWl2YWxlbnQgdG8gYXJyaXZpbmcgbGF0ZSBmb3IgbW9yZSB0aGFuIDE1IG1pbnV0ZXMgYXQgaXRzIGRlc3RpbmF0aW9uLiAoQXJyRGVsYXkgPiAxNSBtaW5zKQ0KDQpTaW5jZSBmbGlnaHRzIGNhbiBiZSBkZWxheWVkIG9uIGl0cyBEZXBhcnR1cmUgYnV0IHN0aWxsIGFycml2ZSBvbiB0aW1lLCBoZW5jZSB3ZSBkbyBub3QgY2xhc3NpZnkgdGhvc2UgYXMgYSBkZWxheWVkIGZsaWdodC4NCg0KSGVuY2Ugd2UgY3JlYXRlIGEgRGVsYXlTdGF0dXMgY29sdW1uIGludG8gdGhlIG1haW4gZGF0YWZyYW1lIChtZXJnZWRfZGYpLHdoZXJlIDAgPSBObyBEZWxheSwgMSA9IERlbGF5Lg0KDQpgYGB7ciBkZWxheSBzdGF0dXN9DQojIENyZWF0aW5nIG5ldyBjb2x1bW4gc2hvd2luZyBBcnJEZWxheSA+IDE1bWlucw0KIyAwID0gTm8gRGVsYXksIDE9IERlbGF5DQpmbGlnaHRfbm90Y2FuY2VsbGVkJERlbGF5U3RhdHVzIDwtIGlmZWxzZShmbGlnaHRfbm90Y2FuY2VsbGVkJEFyckRlbGF5ID4gMTUsIDEsIDApDQoNCmBgYA0KDQoNCmBgYHtyIHByb3BkZWxheWVkfQ0KdGFibGUoZmxpZ2h0X25vdGNhbmNlbGxlZCREZWxheVN0YXR1cykNCnByb3AudGFibGUodGFibGUoZmxpZ2h0X25vdGNhbmNlbGxlZCREZWxheVN0YXR1cykpDQoNCmBgYA0KDQoNClRoaXMgc2hvd3MgdGhhdCA3NyUgaGF2ZSBubyBkZWxheXMgKEFyckRlbGF5ID4gMTUgbWludXRlcyksIHdoZXJlIHRoZXkgZWl0aGVyIGFycml2ZWQgZWFybHkgb3Igb24gdGltZS4gQWxzbywgMjMlIG9mIGZsaWdodHMgd2VyZSBkZWxheWVkLiBFcXVpdmFsZW50IHRvIGFib3V0IDEgb3V0IG9mIGV2ZXJ5IDUgZmxpZ2h0cyBiZWluZyBkZWxheWVkLg0KDQo8YnI+DQoNCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKQ0KDQpXZSB3aWxsIGJlIGxvb2tpbmcgYXQgdGhlIGRpZmZlcmVudCB2YXJpYWJsZXMgdG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRhdGEuDQoNCiogVG90YWwgRmxpZ2h0IERpc3RyaWJ1dGlvbg0KKiBDYW5jZWxsYXRpb24NCiogRGVsYXkNCjxicj4NCg0KIyMgVG90YWwgRmxpZ2h0IERpc3RyaWJ1dGlvbg0KDQojIyMgVG90YWwgRmxpZ2h0IERpc3RyaWJ1dGlvbiBvZiBGdWxsIERhdGEgYnkgTW9udGgNCg0KYGBge3IgYWxsZGlzdHJ9DQpwcm9wLnRhYmxlKHRhYmxlKG1lcmdlZF9kZiRNb250aCkpDQpwcm9wLnRhYmxlKHRhYmxlKG1lcmdlZF9kZiREYXlPZldlZWspKQ0KYGBgDQoNCg0KDQpgYGB7ciBwbG90IHRvdGFsfQ0KIyBQZXJjZW50YWdlIERpc3RyaWJ1dGlvbiBvZiBNb250aA0KZ2dwbG90KG1lcmdlZF9kZiwgYWVzKHggPSBNb250aCkpICsgIA0KICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pL3N1bSguLmNvdW50Li4pKSwgZmlsbCA9ICdjb3JuZmxvd2VyYmx1ZScpICsgZ2d0aXRsZSgiTW9udGggKCUpIikgKw0KICB5bGFiKCJQZXJjZW50YWdlICglKSIpDQojIFBlcmNlbnRhZ2UgRGlzdHJpYnV0aW9uIG9mIERheU9mV2Vlaw0KZ2dwbG90KG1lcmdlZF9kZiwgYWVzKHggPSBEYXlPZldlZWspKSArICANCiAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKS9zdW0oLi5jb3VudC4uKSksZmlsbCA9ICdjb3JuZmxvd2VyYmx1ZScpICsgZ2d0aXRsZSgiRGF5T2ZXZWVrICglKSIpICsNCiAgeWxhYigiUGVyY2VudGFnZSAoJSkiKQ0KYGBgDQo8YnI+DQpUaGUgZGF0YSBpcyBhbG1vc3QgZXZlbmx5IGRpc3RyaWJ1dGVkIGJldHdlZW4gTW9udGggYW5kIERheU9mV2Vlaywgd2l0aCBGZWJydWFyeSBhbmQgU2F0dXJkYXkgaGF2aW5nIHRoZSBsZWFzdCBudW1iZXIgb2YgdG90YWwgZmxpZ2h0cy4NCjxicj4NCg0KYGBge3IgdG90YWxmbGlnaHRzIHBlciBhaXJsaW5lfQ0KI1RvdGFsIE51bWJlciBvZiBmbGlnaHRzIHBlciBBaXJsaW5lDQpnZ3Bsb3QobWVyZ2VkX2RmLCBhZXMoeCA9IGZvcmNhdHM6OmZjdF9pbmZyZXEoVW5pcXVlQ2FycmllcikpKSArICANCiAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSksZmlsbCA9ICdjb3JuZmxvd2VyYmx1ZScpICsgZ2d0aXRsZSgiVG90YWwgTnVtYmVyIG9mIEZsaWdodHMgcGVyIEFpcmxpbmUiKSArDQogIHhsYWIoIlVuaXF1ZUNhcnJpZXIiKQ0KYGBgDQoNCmBgYHtyIHF1ZXJ5IG1vc3RmbGlnaHRzfQ0KY2Fycmllcl9kZiAlPiUgZmlsdGVyX2FsbChhbnlfdmFycyguICVpbiUgYygnV04nLCAnQUEnLCdPTycsJ01RJywnVVMnKSkpDQpgYGANCg0KVGhlIHRvcCA1IGFpcmxpbmVzIHdpdGggdGhlIG1vc3QgZmxpZ2h0cyBhcmUgV04sIEFBLCBPTywgTVEsIFVBLg0KDQoxLiBTb3V0aHdlc3QgQWlybGluZXMNCjIuIEFtZXJpY2FuIEFpcmxpbmVzDQozLiBTa3l3ZXN0IEFpcmxpbmVzDQo0LiBBbWVyaWNhbiBFYWdsZSBBaXJsaW5lcw0KNS4gVW5pdGVkIEFpcmxpbmVzDQoNCmBgYHtyIGJveHBsb3QgZGVwZGVsYXl9DQojQm94cGxvdCBEaXN0cmlidXRpb24gb2YgVG90YWwgQXJyRGVsYXkgcGVyIENhcnJpZXINCmJveHBsb3QoZmxpZ2h0X25vdGNhbmNlbGxlZCRBcnJEZWxheX5mbGlnaHRfbm90Y2FuY2VsbGVkJFVuaXF1ZUNhcnJpZXIsDQogICAgICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIFRvdGFsIEFyckRlbGF5IHBlciBDYXJyaWVyIiwNCiAgICAgICAgeGxhYiA9ICJDYXJyaWVyL0FpcmxpbmUiLA0KICAgICAgICB5bGFiID0gIlRvdGFsIEFyckRlbGF5IChtaW5zKSIsDQogICAgICAgIGJvcmRlciA9ICJibGFjayINCiAgICAgICAgKQ0KYGBgDQoNCiMjIyBDYW5jZWxsYXRpb24NCmBgYHtyfQ0KIyBQZXJjZW50YWdlIERpc3RyaWJ1dGlvbiBvZiBDYW5jZWxsYXRpb24NCmdncGxvdChmbGlnaHRfY2FuY2VsbGVkLCBhZXMoeCA9IENhbmNlbGxhdGlvbkNvZGUpKSArICANCiAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKS9zdW0oLi5jb3VudC4uKSksIGZpbGwgPSAnY29ybmZsb3dlcmJsdWUnKSArIGdndGl0bGUoIkNhbmNlbGxhdGlvbiBSZWFzb25zICglKSIpICsNCiAgeWxhYigiUGVyY2VudGFnZSAoJSkiKQ0KYGBgDQoNCldlIGNhbiBjb25jbHVkZSB0aGF0IENhbmNlbGxhdGlvbnMgYXJlIG1vc3RseSBkdWUgdG8gQ2FycmllciwgV2VhdGhlciBhbmQgTkFTIHdpdGggYXJvdW5kIDQzJSwgMzUlIGFuZCAyMCUgcmVzcGVjdGl2ZWx5Lg0KDQpgYGB7ciB0b3RhbGNhbmNlbGxlZGZsaWdodHMgcGVyIGFpcmxpbmV9DQojVG90YWwgTnVtYmVyIG9mIENhbmNlbGxlZCBmbGlnaHRzIHBlciBBaXJsaW5lDQpnZ3Bsb3QoZmxpZ2h0X2NhbmNlbGxlZCwgYWVzKHggPSBmb3JjYXRzOjpmY3RfaW5mcmVxKFVuaXF1ZUNhcnJpZXIpKSkgKyAgDQogIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikpLGZpbGwgPSAnY29ybmZsb3dlcmJsdWUnKSArIGdndGl0bGUoIlRvdGFsIE51bWJlciBvZiBDYW5jZWxsZWQgRmxpZ2h0cyBwZXIgQWlybGluZSIpICsNCiAgeGxhYigiVW5pcXVlQ2FycmllciIpDQpgYGANCg0KYGBge3IgcXVlcnkgbW9zdGNhbmNlbGxlZGZsaWdodHN9DQpjYXJyaWVyX2RmICU+JSBmaWx0ZXJfYWxsKGFueV92YXJzKC4gJWluJSBjKCdNUScsICdBQScsJ09PJykpKQ0KYGBgDQoNClRoZSB0b3AgMyBtb3N0IGNhbmNlbGxlZCBmbGlnaHRzIHRocm91Z2hvdXQgdGhlc2UgMiB5ZWFycyBhcmU6DQoNCjEuIEFtZXJpY2FuIEVhZ2xlIEFpcmxpbmVzDQoyLiBBbWVyaWNhbiBBaXJsaW5lcw0KMy4gU2t5d2VzdCBBaXJsaW5lcw0KDQpgYGB7cn0NCiNUb3RhbCBOdW1iZXIgb2YgQ2FuY2VsbGVkIGZsaWdodHMgcGVyIE1vbnRoDQpnZ3Bsb3QoZmxpZ2h0X2NhbmNlbGxlZCwgYWVzKHggPSBmb3JjYXRzOjpmY3RfaW5mcmVxKE1vbnRoKSkpICsgIA0KICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pKSxmaWxsID0gJ2Nvcm5mbG93ZXJibHVlJykgKyBnZ3RpdGxlKCJUb3RhbCBOdW1iZXIgb2YgQ2FuY2VsbGVkIEZsaWdodHMgcGVyIE1vbnRoIikgKw0KICB4bGFiKCJVbmlxdWVDYXJyaWVyIikNCiNUb3RhbCBOdW1iZXIgb2YgQ2FuY2VsbGVkIGZsaWdodHMgcGVyIERheU9mV2Vlaw0KZ2dwbG90KGZsaWdodF9jYW5jZWxsZWQsIGFlcyh4ID0gZm9yY2F0czo6ZmN0X2luZnJlcShEYXlPZldlZWspKSkgKyAgDQogIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikpLGZpbGwgPSAnY29ybmZsb3dlcmJsdWUnKSArIGdndGl0bGUoIlRvdGFsIE51bWJlciBvZiBDYW5jZWxsZWQgRmxpZ2h0cyBwZXIgRGF5T2ZXZWVrIikgKw0KICB4bGFiKCJVbmlxdWVDYXJyaWVyIikNCmBgYA0KDQpUaGUgZmxpZ2h0cyBhcmUgbW9zdGx5IGxpa2VseSB0byBiZSBjYW5jZWxsZWQgaW4gRGVjZW1iZXIgYW5kIEZlYnJ1YXJ5LCBvbiBhIFRodXJzZGF5IGFuZCBGcmlkYXkuDQoNCjxwPg0KDQojIyMgRGVsYXkNCg0KYGBge3J9DQpjb3IudGVzdChmbGlnaHRfbm90Y2FuY2VsbGVkJEFyckRlbGF5LCBmbGlnaHRfbm90Y2FuY2VsbGVkJERlcERlbGF5LCBtZXRob2QgPSAicGVhcnNvbiIpDQpgYGANCg0KQXJyRGVsYXkgYW5kIERlcERlbGF5IGhhdmUgYSBzdHJvbmcgcG9zaXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCwgaW1wbHlpbmcgdGhhdCBhIERlcGFydHVyZSBEZWxheSB3aWxsIGFsbW9zdCBjZXJ0YWlubHkgcmVzdWx0IGluIGFuIEFycml2YWwgRGVsYXkuDQoNCmBgYHtyfQ0KIyBCYXJQbG90DQpnZ3Bsb3QoZmxpZ2h0X25vdGNhbmNlbGxlZCwgYWVzKHg9IFVuaXF1ZUNhcnJpZXIpKSArIGdlb21fYmFyKGFlcyhmaWxsPWFzLmZhY3RvcihEZWxheVN0YXR1cykpKSArIGdndGl0bGUoIkJhcnBsb3Qgb2YgRGVsYXlTdGF0dXMgY291bnRzIHBlciBVbmlxdWVDYXJyaWVyIikNCmBgYA0KDQpgYGB7ciBxdWVyeSBtb3N0ZGVsYXllZGZsaWdodHN9DQpjYXJyaWVyX2RmICU+JSBmaWx0ZXJfYWxsKGFueV92YXJzKC4gJWluJSBjKCdXTicsICdBQScsJ09PJykpKQ0KYGBgDQoNClRoZSB0b3AgMyBtb3N0IGRlbGF5ZWQgZmxpZ2h0cyB0aHJvdWdob3V0IHRoZXNlIDIgeWVhcnMgYXJlOg0KDQoxLiBTb3V0aHdlc3QgQWlybGluZXMNCjIuIEFtZXJpY2FuIEFpcmxpbmVzDQozLiBTa3l3ZXN0IEFpcmxpbmVzDQoNCg0KIyBRMS4gV2hlbiBpcyB0aGUgYmVzdCB0aW1lIG9mIGRheSwgZGF5IG9mIHRoZSB3ZWVrLCBhbmQgdGltZSBvZiB5ZWFyIHRvIGZseSB0byBtaW5pbWlzZSBkZWxheXM/DQoNCldlIHdpbGwgYnJlYWtkb3duIHRoaXMgcXVlc3Rpb24gaW50byB0aHJlZSBwYXJ0cywgd2hlcmUgd2Ugd2lsbCBmaW5kIHRoZSBhaXJsaW5lIGNhcnJpZXIgYW5kIHRpbWUgcGVyaW9kIGxlYXN0IGxpa2VseSB0byBoYXZlIGRlbGF5ZWQgZmxpZ2h0czoNCg0KKiBCZXN0IFRpbWUgb2YgdGhlIERheQ0KKiBCZXN0IERheSBvZiB0aGUgV2Vlaw0KKiBCZXN0IE1vbnRoIG9mIHRoZSBZZWFyDQoqIEJlc3QgRGF5IG9mIHRoZSBNb250aA0KPGJyPg0KDQojIyBCZXN0IFRpbWUgb2YgdGhlIERheQ0KIyMjIERpc3RyaWJ1dGlvbiBvZiBBdmVyYWdlIERlbGF5IGJ5IFRpbWUgUGVyaW9kDQpgYGB7ciBjb3B5ZGF0YTF9DQojIENyZWF0ZSBjb3B5IG9mIGRhdGFmcmFtZQ0KZGZfcTEgPC0gZmxpZ2h0X25vdGNhbmNlbGxlZA0KDQpgYGANCjxicj4NClRpbWUgSW50ZXJ2YWxzIGNvbHVtbiDigJhBcnJQZXJpb2TigJkgd2FzIGNyZWF0ZWQgYmFzZWQgb24g4oCYQXJyVGltZeKAmS4gMjQgaG91cnMgaW4gYSBkYXkgd2lsbCBzcGxpdCBpbnRvIDYgZGlmZmVyZW50IHBlcmlvZHMgd2l0aCBhdCBsZWFzdCAzIHRvIDUtaG91ciBpbnRlcnZhbHMgc2luY2UgZGlmZmVyZW50IHRpbWluZ3MgbGlrZSA1YW0gYW5kIDExYW0gYXJlIGJldHRlciBub3QgZ2VuZXJhbGlzZWQgdG9nZXRoZXIgaW50byBhIHNpbmdsZSB0aW1lZnJhbWUuIFRoZSBwZXJpb2QgaXMgc3BsaXQgYXMgc3VjaDogDQoNCiogTWlkbmlnaHQgKDEyYW0gLSA1YW0pDQoqIEVhcmx5IE1vcm5pbmcgKDVhbSAtIDlhbSkNCiogTGF0ZSBNb3JuaW5nICg5YW0gLSAxMnBtKQ0KKiBBZnRlcm5vb24gKDEycG0gLSA1cG0pDQoqIEV2ZW5pbmcgKDVwbSAtIDlwbSkNCiogTmlnaHQgKDlwbSAtIDEyYW0pDQoNCmBgYHtyIHRpbWVwZXJpb2R9DQojIENhdGVnb3Jpc2luZyBBcnJUaW1lIGFuZCBEZXBUaW1lIGJ5IDYgcGVyaW9kczogTWlkbmlnaHQsIEVhcmx5IE1vcm5pbmcsIExhdGUgTW9ybmluZywgQWZ0ZXJub29uLCBFdmVuaW5nLCBOaWdodA0KZGZfcTEgPC0gZGZfcTEgJT4lDQogIG11dGF0ZShBcnJQZXJpb2QgPSBjYXNlX3doZW4oQXJyVGltZSA+PSA1MDAgJiBBcnJUaW1lIDwgOTAwIH4gJ0Vhcmx5IE1vcm5pbmcnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgQXJyVGltZSA+PSA5MDAgJiBBcnJUaW1lIDwgMTIwMCB+ICdMYXRlIE1vcm5pbmcnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBBcnJUaW1lID49IDEyMDAgJiBBcnJUaW1lIDwgMTcwMCB+ICdBZnRlcm5vb24nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBBcnJUaW1lID49IDE3MDAgJiBBcnJUaW1lIDwgMjEwMCB+ICdFdmVuaW5nJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgQXJyVGltZSA+PSAyMTAwICYgQXJyVGltZSA8IDI0MDAgfiAnTmlnaHQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gICdNaWRuaWdodCcpKQ0KYGBgDQoNCg0KYGBge3IgdGltZWJhcn0NCiMgQ3JlYXRlIHRhYmxlIGNvdW50cw0KY291bnRzIDwtIHRhYmxlKGRmX3ExJERlbGF5U3RhdHVzLGRmX3ExJEFyclBlcmlvZCkNCg0KIyBQbG90IGdyb3VwZWQgYmFycGxvdA0KYmFycGxvdChjb3VudHMsIGNvbCA9IGMoIndoaXRlIiwiY29ybmZsb3dlcmJsdWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJUaW1lIFBlcmlvZCIsIHlsYWIgPSAiVG90YWwgRGVsYXkgQ291bnQiLA0KICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gIlRvdGFsIERlbGF5IHBlciBQZXJpb2QiLGJlc2lkZT1UUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kID0gcm93bmFtZXMoY291bnRzKSkNCmBgYA0KDQpFYXJseSBNb3JuaW5nIGZvbGxvd2VkIGJ5IE1pZG5pZ2h0IGhhcyB0aGUgbG93ZXN0IGNvdW50IGZvciBudW1iZXIgb2YgZmxpZ2h0cyBkZWxheWVkLiBIb3dldmVyLCB3ZSB3aWxsIHByb2NlZWQgb24gdG8gY2hlY2sgaWYgaGF2aW5nIHRoZSBsb3dlc3QgbnVtYmVyIG9mIGNvdW50IHdpbGwgZXF1YXRlIHRvIHRoZSBzaG9ydGVzdCBhdmVyYWdlIGRlbGF5ZWQgdGltZS4NCg0KYGBge3IgdGltZSBib3hwbG90fQ0KIyBCb3hwbG90DQpnZ3Bsb3QoZGZfcTEsIGFlcyh4PSBBcnJEZWxheSkpICsgZ2VvbV9ib3hwbG90KGFlcyhjb2xvcj1hcy5mYWN0b3IoQXJyUGVyaW9kKSkpICsgZ2d0aXRsZSgiQm94cGxvdCBvZiBBcnJEZWxheSAobWlucykgdnMgQXJyUGVyaW9kIikNCmBgYA0KDQpUaGUgYm94cGxvdCBzaG93cyB0aGF0IHRoZSBtZWFuIG9mIEFyckRlbGF5IGZvciBtaWRuaWdodCBpcyBtb3JlIHRoYW4gdGhlIG90aGVyIHRpbWUgcGVyaW9kLiBIZW5jZSwgdmFsaWRhdGluZyBvdXIgYXNzdW1wdGlvbiB0aGF0IGxvd2VzdCBmbGlnaHQgZGVsYXkgY291bnRzIGRvZXMgbm90IGVxdWF0ZSB0byBsb3dlc3QgYXZlcmFnZSBkZWxheWVkIHRpbWUuDQoNCmBgYHtyIHRpbWVsaW5lfQ0KI1NldCBTZXF1ZW5jZSB0byBBcnJQZXJpb2QNCmRmX3ExJEFyclBlcmlvZCA8LSBmYWN0b3IoZGZfcTEkQXJyUGVyaW9kLGxldmVscyA9IGMoIk1pZG5pZ2h0IiwgIkVhcmx5IE1vcm5pbmciLCAiTGF0ZSBNb3JuaW5nIiwgIkFmdGVybm9vbiIsICJFdmVuaW5nIiwgIk5pZ2h0IikpDQoNCmRmX3ExICAlPiUgZ3JvdXBfYnkoQXJyUGVyaW9kKSAlPiUgZHBseXI6OnN1bW1hcml6ZShtZWFuID0gbWVhbihBcnJEZWxheSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9IEFyclBlcmlvZCwgeSA9IG1lYW4pKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZShhZXMoZ3JvdXAgPSAxKSwgY29sID0gImNvcm5mbG93ZXJibHVlIikgKyBnZ3RpdGxlKCJBdmVyYWdlIEFyckRlbGF5IHZzIFRpbWUgUGVyaW9kIikgKw0KICB4bGFiKCJUaW1lIFBlcmlvZCIpICsgeWxhYigiQXZlcmFnZSBBcnJEZWxheSAobWlucykiKQ0KYGBgDQoNCg0KVGhlIGJlc3QgdGltZSBwZXJpb2QgdG8gbWluaW1pc2UgZmxpZ2h0IGRlbGF5cyB3b3VsZCBiZSBpbiB0aGUgRWFybHkgTW9ybmluZyBmcm9tIDVhbSB0byA5YW0uDQoNCg0KIDxwPg0KDQojIyBCZXN0IERheSBvZiB0aGUgV2Vlaw0KIyMjIERpc3RyaWJ1dGlvbiBvZiBEYXkgRGVsYXkNCmBgYHtyIGRheWJhcn0NCiMgQ3JlYXRlIHRhYmxlIGZvciBjb3VudHMNCmNvdW50cyA8LSB0YWJsZShkZl9xMSREZWxheVN0YXR1cyxkZl9xMSREYXlPZldlZWspDQojIFBsb3QgZ3JvdXBlZCBiYXJwbG90DQpiYXJwbG90KGNvdW50cywgY29sID0gYygid2hpdGUiLCJjb3JuZmxvd2VyYmx1ZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICB4bGFiID0gIkRheU9mV2VlayIseWxhYiA9ICJUb3RhbCBEZWxheSBDb3VudCIsDQogICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSAiVG90YWwgRGVsYXkgcGVyIERheU9mV2VlayIsYmVzaWRlPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQgPSByb3duYW1lcyhjb3VudHMpKQ0KYGBgDQoNCmBgYHtyIGRheWxpbmV9DQojTGluZSBQbG90DQpkZl9xMSAgJT4lIGdyb3VwX2J5KERheU9mV2VlaykgJT4lIGRwbHlyOjpzdW1tYXJpemUobWVhbiA9IG1lYW4oQXJyRGVsYXkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PSBEYXlPZldlZWssIHkgPSBtZWFuKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSksIGNvbCA9ICJjb3JuZmxvd2VyYmx1ZSIpICsgZ2d0aXRsZSgiQXZlcmFnZSBBcnJEZWxheSB2cyBEYXlPZldlZWsiKSArDQogIHhsYWIoIkRheU9mV2VlayIpICsgeWxhYigiQXZlcmFnZSBBcnJEZWxheSAobWlucykiKQ0KYGBgDQoNClRoZSBCZXN0IERheSBvZiB0aGUgV2VlayB0byBtaW5pbWl6ZSBkZWxheXMgaXMgdG8gdHJhdmVsIG9uIFNhdHVyZGF5LCBmb2xsb3dlZCBieSBUdWVzZGF5LCB3aXRoIGF2ZXJhZ2UgQXJyRGVsYXkgdGltaW5nIG9mIGxlc3MgdGhhbiA2IG1pbnV0ZXMgYW5kIDggbWludXRlcywgcmVzcGVjdGl2ZWx5Lg0KDQpUaGUgbG9uZ2VzdCBhdmVyYWdlIGRlbGF5cyBvZiAxMi0xMyBtaW51dGVzIGFyZSBleHBlY3RlZCBpbiB0aGUgbWlkZGxlIG9mIHRoZSB3ZWVrLCBmcm9tIFRodXJzZGF5IHRvIEZyaWRheS4NCg0KDQogPHA+DQoNCiMjIEJlc3QgTW9udGggb2YgdGhlIFllYXINCiMjIyBEaXN0cmlidXRpb24gb2YgTW9udGhseSBEZWxheQ0KYGBge3IgbW9udGhiYXJ9DQojIGNyZWF0ZSB0YWJsZSBjb3VudHMNCmNvdW50cyA8LSB0YWJsZShkZl9xMSREZWxheVN0YXR1cyxkZl9xMSRNb250aCkNCiMgUGxvdCBncm91cGVkIGJhcnBsb3QNCmJhcnBsb3QoY291bnRzLCBjb2wgPSBjKCJ3aGl0ZSIsImNvcm5mbG93ZXJibHVlIiksDQogICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiTW9udGgiLHlsYWIgPSAiVG90YWwgRGVsYXkgQ291bnQiLA0KICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gIlRvdGFsIERlbGF5IHBlciBNb250aCIsYmVzaWRlPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQgPSByb3duYW1lcyhjb3VudHMpKQ0KYGBgDQoNCmBgYHtyIG1vbnRobGluZX0NCmRmX3ExICU+JSBncm91cF9ieShNb250aCkgJT4lIGRwbHlyOjpzdW1tYXJpemUobWVhbiA9IG1lYW4oQXJyRGVsYXkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PSBNb250aCwgeSA9IG1lYW4pKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZShhZXMoZ3JvdXAgPSAxKSwgY29sID0gImNvcm5mbG93ZXJibHVlIikgKyBnZ3RpdGxlKCJBdmVyYWdlIEFyckRlbGF5IHZzIE1vbnRoIikgKw0KICB4bGFiKCJNb250aCIpICsgeWxhYigiQXZlcmFnZSBBcnJEZWxheSAobWlucykiKQ0KYGBgDQoNCkJhc2VkIG9uIEFyckRlbGF5LCB0aGUgYmVzdCB0aW1lIG9mIHllYXIgdG8gbWluaW1pc2UgdHJhdmVsIGRlbGF5IGlzIE5vdmVtYmVyLCB0aGVuIFNlcHRlbWJlciwgd2l0aCBib3RoIGF2ZXJhZ2luZyBhcHByb3hpbWF0ZWx5IDYgbWludXRlcyBvZiBkZWxheSwgYXMgb3Bwb3NlZCB0byBKdW5lIGFuZCBEZWNlbWJlciwgd2l0aCBtb3JlIHRoYW4gdHdpY2UgdGhlIG51bWJlciBvZiBtaW51dGVzIGRlbGF5ZWQuDQoNCkR1ZSB0byB0aGUgVS5TLiBzdW1tZXIgYW5kIHdpbnRlciB2YWNhdGlvbiAoU2Nob29sIEhvbGlkYXlzIFVTQSwgMjAyMiksIEp1bmUgYW5kIERlY2VtYmVyIGFyZSBwcm9qZWN0ZWQgdG8gYmUgcG9wdWxhciBtb250aHMgdG8gdHJhdmVsLg0KDQoNCiA8cD4NCg0KIyMgQmVzdCBEYXkgb2YgdGhlIE1vbnRoDQpgYGB7cn0NCmRmX3ExICU+JSBncm91cF9ieShEYXlvZk1vbnRoKSAlPiUgZHBseXI6OnN1bW1hcml6ZShtZWFuID0gbWVhbihBcnJEZWxheSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9IERheW9mTW9udGgsIHkgPSBtZWFuKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSksIGNvbCA9ICJjb3JuZmxvd2VyYmx1ZSIpICsgZ2d0aXRsZSgiQXZlcmFnZSBBcnJEZWxheSB2cyBEYXlvZk1vbnRoIikgKw0KICB4bGFiKCJEYXlvZk1vbnRoIikgKyB5bGFiKCJBdmVyYWdlIEFyckRlbGF5IChtaW5zKSIpDQpgYGANCkFsc28sIHRyYXZlbGxpbmcgaW4gdGhlIGZpcnN0IGhhbGYgb2YgdGhlIG1vbnRoLCBhcm91bmQgdGhlIDh0aCDigJMgOXRoLCByZXN1bHRzIGluIGFuIGF2ZXJhZ2UgQXJyRGVsYXkgb2YgYXJvdW5kIDYgbWludXRlcywgYW5kIHRoZSBzZWNvbmQgaGFsZiBvZiB0aGUgbW9udGggd2l0aCBkb3VibGUgdGhlIGRlbGF5IGFtb3VudA0KIA0KPGJyPg0KDQpIZW5jZSBvdmVyYWxsLCB0aGUgdG9wIDIgcmVjb21tZW5kZWQgdGltZSBwZXJpb2QgdG8gYXZvaWQgZmxpZ2h0IGRlbGF5IGlzOg0KDQoqIEVhcmx5IE1vcm5pbmcgKDVhbS05YW0pICYgTGF0ZSBNb3JuaW5nICg5YW0tMTJwbSkgKOKJiDAgbWludXRlcykNCiogU2F0dXJkYXkgJiBUdWVzZGF5ICjiiYg2IG1pbnV0ZXMpDQoqIFNlcHRlbWJlciAmIE5vdmVtYmVyICjiiYg2IG1pbnV0ZXMpDQoqIEFyb3VuZCA4dGgg4oCTIDl0aCAoU3BlY2lmaWNhbGx5IG9uIDh0aCkgKOKJiDYgbWludXRlcykNCg0KQWxsIHRoZSBhYm92ZSBwZXJpb2RzIGFyZSBkZWxheWVkIGJ5IGFuIGF2ZXJhZ2Ugb2YgMCB0byA2IG1pbnV0ZXMuIFBhc3NlbmdlcnMgbWF5IHJlZHVjZSB0aGVpciBmbGlnaHQgZGVsYXlzIGV2ZW4gZnVydGhlciBieSBib29raW5nIGZsaWdodHMgd2l0aCBhaXJsaW5lcyB0aGF0IGFyZSBiZWxvdyB0aGUgZGVsYXkgdGhyZXNob2xkICglKS4NCg0KDQpgYGB7ciByZW1vdmUgZGZxMX0NCiMgVG8gc2F2ZSB1cCBzcGFjZQ0Kcm0oZGZfcTEpDQpgYGANCjxicj4NCg0KIyBRMi4gRG8gb2xkZXIgcGxhbmVzIHN1ZmZlciBtb3JlIGRlbGF5cz8NCg0KVGhlIGlzc3VlIGRhdGUgb2YgcGxhbmVzIHdpbGwgYmUgZXh0cmFjdGVkIGZyb20gdGhlIHBsYW5lc19kZiBkYXRhIHNpbmNlIHdlIGNhbiBqdWRnZSB0aGUgYWdlIG9mIHRoZSBhaXJjcmFmdCBhbmQgbWFrZSBvdXIgYW5hbHlzaXMgYmFzZWQgb24gdGhhdC4NCg0KDQoqIERpc3RyaWJ1dGlvbiBvZiBBcnIvRGVwIERlbGF5IGJ5IElzc3VlIERhdGUNCiogRGlzdHJpYnV0aW9uIG9mIEFyci9EZXAgRGVsYXkgYnkgSXNzdWUgWWVhcg0KKiBDb21wYXJpc29uIG9mIE9sZCBhbmQgTm9ybWFsIFBsYW5lcw0KDQoNClRvIGRldGVybWluZSB0aGUgYWdlIG9mIHRoZSBhaXJjcmFmdHMsIHRoZSBlbmdpbmUncyBpc3N1ZSBkYXRlIGlzIG9idGFpbmVkIGJ5IG1hcHBpbmcgdGhlIHBsYW5lLWRhdGEuY3N2IGNvbnNpc3Rpbmcgb2YgdGhlIHBsYW5lJ3Mg4oCYaXNzdWVfZGF0ZeKAmSwgaW50byBhIGR1cGxpY2F0ZWQgZGF0YSBmcmFtZSBvZiB0aGUgJ2ZsaWdodF9ub3RjYW5jZWxsZWQnIGRhdGEgYXMgYSBuZXcgY29sdW1uLg0KDQpgYGB7ciBjb3B5ZGF0YTJ9DQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUNCmRmX3EyIDwtIGZsaWdodF9ub3RjYW5jZWxsZWQNCmBgYA0KDQoNCmBgYHtyIGV4dHJhY3Rpc3N1ZV9kYXRlfQ0KIyBFeHRyYWN0IGlzc3VlX2RhdGUgZnJvbSBwbGFuZV9kZg0KZGZfcTIkaXNzdWVfZGF0ZSA8LSBwbGFuZXNfZGYkaXNzdWVfZGF0ZVttYXRjaChkZl9xMiRUYWlsTnVtLCBwbGFuZXNfZGYkdGFpbG51bSldDQpgYGANCg0KYGBge3IgY2hlY2ttaXNzaW5nMn0NCiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWUNCnNhcHBseShkZl9xMixmdW5jdGlvbih4KXN1bShpcy5uYSh4KSkpDQpgYGANCjxicj4NCg0KU2luY2Ugb3VyIGRhdGFzZXQgaXMgaHVnZSB3aXRoIG1vcmUgdGhhbiAyIG1pbGxpb24gcmVjb3Jkcywgd2Ugd2lsbCBwcm9jZWVkIHRvIGNsZWFuIHRoZSAxNjZrIHJlY29yZHMgb2YgbWlzc2luZyBpc3N1ZV9kYXRlLg0KDQoNCmBgYHtyIHFuMiBjbGVhbmluZ30NCiMgUmVtb3ZlIG1pc3NpbmcgJiBOb25lIHZhbHVlcw0KZGZfcTIgPC0gbmEub21pdChkZl9xMikNCmRmX3EyIDwtIGRmX3EyICU+JSANCiAgZmlsdGVyKCFncmVwbCgnTm9uZScsIGlzc3VlX2RhdGUpKQ0KDQojIENvbnZlcnQgdG8gZGF0ZXRpbWUgZm9ybWF0DQpkZl9xMiRpc3N1ZV9kYXRlIDwtIGFzLkRhdGUoZGZfcTIkaXNzdWVfZGF0ZSwgIiVtLyVkLyVZIikNCmBgYA0KDQoNCiMjIERpc3RyaWJ1dGlvbiBvZiBBcnIgRGVsYXkgd2l0aCBJc3N1ZSBEYXRlDQoNCiMjIyBUb3RhbCBTdW0gb2YgQXJyRGVsYXkgYnkgSXNzdWUgRGF0ZQ0KYGBge3IgcGxvdCBpc3N1ZV9kYXRlfQ0KIyBUb3RhbCBTdW0gb2YgRGVsYXkgYnkgaXNzdWVfZGF0ZQ0KZ2dwbG90KGRhdGEgPSBkZl9xMiwgYWVzKHg9aXNzdWVfZGF0ZSwgeT1BcnJEZWxheSkpKw0KICBnZW9tX2xpbmUoKSArIGdndGl0bGUoIlRvdGFsIEFyckRlbGF5IHdpdGggSXNzdWUgRGF0ZSIpICsNCiAgeGxhYigiSXNzdWUgRGF0ZSIpICsgeWxhYigiVG90YWwgQXJyRGVsYXkgKG1pbnMpIikNCmBgYA0KDQpGcm9tIHRoZSBncmFwaCwgd2UgY2FuIHNlZSB0aHJlZSBwZWFrIHBvaW50cyBhcm91bmQgeWVhciAxOTg2LCAyMDAwIGFuZCAyMDA0LiBIZW5jZSwgd2UgcHJvY2VlZCB0byBjaGVjayB3aXRoIHRoZSBtZWFuIHZhbHVlLg0KDQpgYGB7ciBwbG90IG1lYW4gaXNzdWVfZGF0ZX0NCiMgQXZlcmFnZSBvZiBBcnJEZWxheSBieSBpc3N1ZV9kYXRlDQpkZl9xMiAlPiUgZ3JvdXBfYnkoaXNzdWVfZGF0ZSkgJT4lIGRwbHlyOjpzdW1tYXJpemUobWVhbiA9IG1lYW4oQXJyRGVsYXkpKSAlPiUNCmdncGxvdChhZXMoeD1pc3N1ZV9kYXRlLCB5PW1lYW4pKSsNCiAgZ2VvbV9saW5lKCkgKyBnZ3RpdGxlKCJBdmVyYWdlIEFyckRlbGF5IHdpdGggSXNzdWUgRGF0ZSIpICsNCiAgeGxhYigiSXNzdWUgRGF0ZSIpICsgeWxhYigiQXZlcmFnZSBBcnJEZWxheSAobWlucykiKQ0KYGBgDQoNCkJhc2VkIG9uIElzc3VlIERhdGUsIHRoZXJlIHNlZW0gdG8gYmUgbW9yZSBkZWxheSBvY2N1cnJlbmNlcyBmb3IgcGxhbmVzIGlzc3VlZCBhZnRlciAxOTk4LiBBbHNvLCB0aGUgbG93IHRvdGFsIG1pbnV0ZXMgZGVsYXllZCBiZXR3ZWVuIDE5NzYgYW5kIDE5ODQgbWlnaHQgYmUgYXR0cmlidXRlZCB0byBwbGFuZXMgb2xkZXIgdGhhbiAyNCB5ZWFycyBiZWluZyByZW1vdmVkIG93aW5nIHRvIG1haW50ZW5hbmNlIGZhdWx0cywgb3IgdGhlIGFpcmxpbmUgc2NoZWR1bGluZyB0aGVtIHdpdGggYW1wbGUgdGltZSB0byBhdm9pZCBkZWxheXMuDQoNCg0KPHA+DQoNCiMjIERpc3RyaWJ1dGlvbiBvZiBBcnIgRGVsYXkgd2l0aCBJc3N1ZSBZZWFyDQoNCg0KQSBuZXcgY29sdW1uIG9mIGlzc3VlIHllYXIgYW5kIHBsYW5lIGFnZSBpcyB0aGVuIGV4dHJhY3RlZCBmcm9tIHRoZSBpc3N1ZSBkYXRlLg0KYGBge3IgaXNzdWV5ZWFyfQ0KZGZfcTIkaXNzdWVfeWVhciA9IGZvcm1hdChhcy5EYXRlKGRmX3EyJGlzc3VlX2RhdGUsIGZvcm1hdD0iJW0vJWQvJVkiKSwiJVkiKQ0KYGBgDQoNCmBgYHtyIGlzc3VleWVhcmxpbmUsIGZpZy53aWR0aD0xMH0NCmRmX3EyICU+JSBncm91cF9ieShpc3N1ZV95ZWFyKSAlPiUgZHBseXI6OnN1bW1hcml6ZShtZWFuID0gbWVhbihBcnJEZWxheSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9IGlzc3VlX3llYXIsIHkgPSBtZWFuKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSksIHNpemU9MSwgY29sID0gImNvcm5mbG93ZXJibHVlIikgKyBnZ3RpdGxlKCJBdmVyYWdlIEFyckRlbGF5IHZzIGlzc3VlX3llYXIiKSArDQogIHhsYWIoImlzc3VlX3llYXIiKSArIHlsYWIoIkF2ZXJhZ2UgQXJyRGVsYXkgKG1pbnMpIikNCg0KYGBgDQoNCk9sZGVyIHBsYW5lcyBpc3N1ZWQgYmVmb3JlIDE5ODQgaGF2ZSBhIGhpZ2hlciBhdmVyYWdlIGRlbGF5IG9mIG1vcmUgdGhhbiAxMSBtaW51dGVzLCBjb21wYXJlZCB0byB0aG9zZSAxOTg0IG9ud2FyZHMgd2l0aCB1bmRlciAxMSBtaW51dGVzIG9mIGF2ZXJhZ2UgZGVsYXkuDQoNCkZ1cnRoZXIgaW52ZXN0aWdhdGlvbnMgd2lsbCB0aGVuIGJlIG1hZGUgd2l0aCBJc3N1ZSBEYXRlIHJhdGhlciB0aGFuIGl0cyBZZWFyLCBhcyBkZWxheXMgYmVjYW1lIGluY3JlYXNpbmdseSBwcmV2YWxlbnQgYWZ0ZXIgeWVhciAyMDAwLg0KDQoNCg0KIyMgQ29tcGFyaW5nIE9sZCBQbGFuZXMgYW5kIE5vcm1hbCBQbGFuZXMNCg0KVGhlIGF2ZXJhZ2UgYWdlIG9mIFUuUyBwbGFuZXMgaXMgMTEgeWVhcnMsIHdpdGggYWJvdXQgMjUlIG9mIHBsYW5lcyBhYm92ZSAxNSB5ZWFycyBvbGQgKE1heWVyb3dpdHosIDIwMTEpLiANCg0KSGVuY2UsIHdlIHdpbGwgZGVlbSBhbiBhaXJjcmFmdCB0byBiZSBvbGQgd2hlbiBpbiBvcGVyYXRpb24gZm9yIDE1KyB5ZWFycywgd2hlcmUgZGF0YSB3aWxsIHNwbGl0IGludG8gdHdvLCB3aXRoIHBsYW5lcyBpc3N1ZWQgYmVmb3JlIDE5OTMgdGVybWVkIG9sZCwgYW5kIHRob3NlIGlzc3VlZCAxOTkzIG9ud2FyZHMgdGVybWVkIG5vcm1hbC4uDQoNCjxwPg0KDQojIyMgRGlzdHJpYnV0aW9uIG9mIE9sZCAmIE5vcm1hbCBQbGFuZXMNCmBgYHtyIHRvdGFscGxvdGRpc3Qgb2xkbm9ybWFsfQ0KIyBQbG90IGEgc3Vic2V0IG9mIG9sZGVyIHBsYW5lcw0KZ2dwbG90KGRhdGE9c3Vic2V0KGRmX3EyLCBpc3N1ZV9kYXRlIDwgYXMuRGF0ZSgiMTk5My0wMS0wMSIpKSwgDQogICAgICAgYWVzKHg9aXNzdWVfZGF0ZSwgeT1BcnJEZWxheSkpKyBnZW9tX2xpbmUoKSArIGdndGl0bGUoIlRvdGFsIEFyckRlbGF5IHdpdGggSXNzdWUgRGF0ZSAoT2xkIFBsYW5lcyAoQmVmb3JlIDE5OTMpKSIpICsNCiAgeGxhYigiSXNzdWUgRGF0ZSIpICsgeWxhYigiVG90YWwgQXJyRGVsYXkgKG1pbnMpIikNCiMgUGxvdCBhIHN1YnNldCBvZiBub3JtYWwgcGxhbmVzDQpnZ3Bsb3QoZGF0YT1zdWJzZXQoZGZfcTIsIGlzc3VlX2RhdGUgPiBhcy5EYXRlKCIxOTkyLTEyLTMxIikpLCANCiAgICAgICBhZXMoeD1pc3N1ZV9kYXRlLCB5PUFyckRlbGF5KSkrIGdlb21fbGluZSgpICsgZ2d0aXRsZSgiVG90YWwgQXJyRGVsYXkgd2l0aCBJc3N1ZSBEYXRlIChOb3JtYWwgUGxhbmVzICgxOTkzIE9ud2FyZHMpKSIpICsNCiAgeGxhYigiSXNzdWUgRGF0ZSIpICsgeWxhYigiVG90YWwgQXJyRGVsYXkgKG1pbnMpIikNCmBgYA0KDQpgYGB7ciBtZWFucGxvdGRpc3Qgb2xkbm9ybWFsfQ0KIyBQbG90IEF2ZXJhZ2UgQXJyRGVsYXkgb2Ygb2xkZXIgcGxhbmVzDQpnZ3Bsb3QoZGF0YT1zdWJzZXQoZGZfcTIsIGlzc3VlX2RhdGUgPCBhcy5EYXRlKCIxOTkzLTAxLTAxIikpLCANCiAgICAgICBhZXMoeD1pc3N1ZV9kYXRlLCB5PUFyckRlbGF5KSkrICBzdGF0X3N1bW1hcnkoYWVzKHkgPSBBcnJEZWxheSxncm91cD0xKSwgZnVuPW1lYW4sIGdlb209ImxpbmUiLGdyb3VwPTEpICsgZ2d0aXRsZSgiQXZlcmFnZSBBcnJEZWxheSB3aXRoIElzc3VlIERhdGUgKE9sZCBQbGFuZXMgKEJlZm9yZSAxOTkzKSkiKSArDQogIHhsYWIoIklzc3VlIERhdGUiKSArIHlsYWIoIkF2ZXJhZ2UgQXJyRGVsYXkgKG1pbnMpIikNCiMgUGxvdCBBdmVyYWdlIEFyckRlbGF5IG9mIE5vcm1hbCBwbGFuZXMNCmdncGxvdChkYXRhPXN1YnNldChkZl9xMiwgaXNzdWVfZGF0ZSA+IGFzLkRhdGUoIjE5OTItMTItMzEiKSksIA0KICAgICAgIGFlcyh4PWlzc3VlX2RhdGUsIHk9QXJyRGVsYXkpKSsgIHN0YXRfc3VtbWFyeShhZXMoeSA9IEFyckRlbGF5LGdyb3VwPTEpLCBmdW49bWVhbiwgZ2VvbT0ibGluZSIsZ3JvdXA9MSkgKyBnZ3RpdGxlKCJBdmVyYWdlIEFyckRlbGF5IHdpdGggSXNzdWUgRGF0ZSAoTm9ybWFsIFBsYW5lcyAoMTk5MyBPbndhcmRzKSkiKSArDQogIHhsYWIoIklzc3VlIERhdGUiKSArIHlsYWIoIkF2ZXJhZ2UgQXJyRGVsYXkgKG1pbnMpIikNCiMgUGxvdCBBdmVyYWdlIEFyckRlbGF5IGJ5IERpc3RhbmNlIG9mIE9sZC9Ob3JtYWwgcGxhbmVzDQpnZ3Bsb3QoKSArICBzdGF0X3N1bW1hcnkoZGF0YT1zdWJzZXQoZGZfcTIsIGlzc3VlX2RhdGUgPCBhcy5EYXRlKCIxOTkzLTAxLTAxIikpLCBhZXMoeD1EaXN0YW5jZSwgeSA9IEFyckRlbGF5LGdyb3VwPTEsIGNvbG9yID0gJ3JlZCcpLCBmdW49bWVhbiwgZ2VvbT0ibGluZSIsZ3JvdXA9MSwgc2l6ZT0wLjUpICsgIHN0YXRfc3VtbWFyeShkYXRhPXN1YnNldChkZl9xMiwgaXNzdWVfZGF0ZSAgPiBhcy5EYXRlKCIxOTkyLTEyLTMxIikpLCBhZXMoeD1EaXN0YW5jZSwgeSA9IEFyckRlbGF5LGdyb3VwPTEsIGNvbG9yID0gJ2Nvcm5mbG93ZXJibHVlJyksIGZ1bj1tZWFuLCBnZW9tPSJsaW5lIixncm91cD0xLCBzaXplPTAuNSkgKyBnZ3RpdGxlKCJBdmVyYWdlIEFyckRlbGF5IGJ5IERpc3RhbmNlIikgKw0KICB4bGFiKCJEaXN0YW5jZSAobWlsZXMpIikgKyB5bGFiKCJBdmVyYWdlIEFyckRlbGF5IChtaW5zKSIpICsgDQogICAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lID0gJ1BsYW5lcycsIA0KICAgICAgICAgdmFsdWVzID1jKCdjb3JuZmxvd2VyYmx1ZSc9J2Nvcm5mbG93ZXJibHVlJywncmVkJz0ncmVkJyksIGxhYmVscyA9IGMoJ05vcm1hbCBQbGFuZXMgKDE5OTMgT253YXJkcyknLCdPbGQgUGxhbmVzIChCZWZvcmUgMTk5MyknKSkrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44MiwwLjg4KSkNCmBgYA0KDQpPbGRlciBwbGFuZXMgYXJlIG1vcmUgbGlrZWx5IHRoYW4gbm9ybWFsIHBsYW5lcyB0byBoYXZlIGxhcmdlciBBdmVyYWdlIERlbGF5cyBhcyB0aGUgRGlzdGFuY2UgZ3Jvd3MsIHdpdGggQXZlcmFnZSBBcnJpdmFsIERlbGF5cyBzdXJwYXNzaW5nIDEwMCBtaW51dGVzIG9yIG1vcmUuIFRoaXMgZnVydGhlciBjb25maXJtcyB0aGF0IG9sZGVyIHBsYW5lcyB0ZW5kIHRvIHN1ZmZlciBmcm9tIG1vcmUgZGVsYXlzIHdoZW4gb24gYSBsb25nLWhhdWwgZmxpZ2h0LiBGdXJ0aGVyIGludmVzdGlnYXRpb25zIGFyZSBkb25lIHdpdGggRGlzdGFuY2UgdG8gaWRlbnRpZnkgdGhlIGRpZmZlcmVudCBkZWxheSBmYWN0b3JzIHRoYXQgbWF5IGltcGFjdCBvbGQgYW5kIG5vcm1hbCBwbGFuZXMuIA0KDQpgYGB7ciBpbmR2IGRlbGF5IHRvdGFsfQ0KbGlicmFyeShyZXNoYXBlMikNCiMgT3JnYW5pemluZyBkYXRhDQpkZl9xMmEgPC0gZGZfcTIgJT4lIHNlbGVjdChpc3N1ZV9kYXRlLCBDYXJyaWVyRGVsYXksIFdlYXRoZXJEZWxheSwgTkFTRGVsYXksIFNlY3VyaXR5RGVsYXksIExhdGVBaXJjcmFmdERlbGF5KQ0KZGZfcTJhIDwtIG1lbHQoZGZfcTJhICwgIGlkLnZhcnMgPSAnaXNzdWVfZGF0ZScsIHZhcmlhYmxlLm5hbWUgPSAnRGVsYXknKQ0KIyBQbG90IFRvdGFsIEluZGl2aWR1YWwgRGVsYXlzIGZvciBmdWxsIGRhdGENCmdncGxvdChkZl9xMmEsIGFlcyhpc3N1ZV9kYXRlLCB2YWx1ZSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXIgPSBEZWxheSksc2l6ZT0wLjgpICsgZ2d0aXRsZSgiVG90YWwgRGVsYXkgYnkgaXNzdWVfZGF0ZSIpICsNCiAgeGxhYigiaXNzdWVfZGF0ZSIpICsgeWxhYigiVG90YWwgRGVsYXkgKG1pbnMpIikrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xMiwwLjc4KSkNCmBgYA0KDQpgYGB7ciBhdmcgaW5kdiBkZWxheSBmdWxsfQ0KIyBPcmdhbml6aW5nIGRhdGENCmRmX3EyYSA8LSBkZl9xMiAlPiUgc2VsZWN0KGlzc3VlX2RhdGUsIENhcnJpZXJEZWxheSwgV2VhdGhlckRlbGF5LCBOQVNEZWxheSwgU2VjdXJpdHlEZWxheSwgTGF0ZUFpcmNyYWZ0RGVsYXkpDQpkZl9xMmEgPC0gbWVsdChkZl9xMmEgLCAgaWQudmFycyA9ICdpc3N1ZV9kYXRlJywgdmFyaWFibGUubmFtZSA9ICdEZWxheScpDQojIFBsb3QgQXZlcmFnZSBJbmRpdmlkdWFsIERlbGF5cyBmb3IgZnVsbCBkYXRhDQpnZ3Bsb3QoZGF0YT1kZl9xMmEsIA0KICAgICAgIGFlcyh4PWlzc3VlX2RhdGUsIHk9dmFsdWUpKSsgIHN0YXRfc3VtbWFyeShhZXMoeSA9IHZhbHVlLGdyb3VwPTEsY29sb3VyID0gRGVsYXkpLCBmdW49bWVhbiwgZ2VvbT0ibGluZSIsZ3JvdXA9MSwgc2l6ZT0xKSArIGdndGl0bGUoIkF2ZXJhZ2UgRGVsYXkgd2l0aCBJc3N1ZSBEYXRlIikgKw0KICB4bGFiKCJJc3N1ZSBEYXRlIikgKyB5bGFiKCJBdmVyYWdlIERlbGF5IChtaW5zKSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjEyLDAuNzgpKQ0KYGBgDQoNClRoZSBmdWxsIGRhdGEgc2hvd3MgQ2FycmllciBEZWxheSBiZWluZyBwcm9taW5lbnQgd2l0aCBwbGFuZXMgaXNzdWVkIDE5OTAtMTk5MiBhbmQgMjAwMC4NCg0KYGBge3IgdG90YWwgaW5kdiBkZWxheSBvbGR9DQojIE9yZ2FuaXppbmcgZGF0YQ0KZGZfcTJhIDwtIGRmX3EyICU+JSBzZWxlY3QoaXNzdWVfZGF0ZSwgQ2FycmllckRlbGF5LCBXZWF0aGVyRGVsYXksIE5BU0RlbGF5LCBTZWN1cml0eURlbGF5LCBMYXRlQWlyY3JhZnREZWxheSkNCmRmX3EyYSA8LSBtZWx0KGRmX3EyYSAsICBpZC52YXJzID0gJ2lzc3VlX2RhdGUnLCB2YXJpYWJsZS5uYW1lID0gJ0RlbGF5JykNCiMgUGxvdCBUb3RhbCBJbmRpdmlkdWFsIERlbGF5cyBmb3Igb2xkIHBsYW5lcw0KZ2dwbG90KGRhdGE9c3Vic2V0KGRmX3EyYSwgaXNzdWVfZGF0ZSA8IGFzLkRhdGUoIjE5OTMtMDEtMDEiKSksIA0KICAgICAgIGFlcyh4PWlzc3VlX2RhdGUsIHk9dmFsdWUpKSsgZ2VvbV9saW5lKGFlcyhjb2xvdXIgPSBEZWxheSksc2l6ZT0xKSArIGdndGl0bGUoIlRvdGFsIERlbGF5IHdpdGggSXNzdWUgRGF0ZSAoT2xkIFBsYW5lcykiKSArDQogIHhsYWIoIklzc3VlIERhdGUiKSArIHlsYWIoIlRvdGFsIERlbGF5IChtaW5zKSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjEyLDAuNzgpKQ0KYGBgDQoNCmBgYHtyIGF2ZyBpbmR2IGRlbGF5IG9sZH0NCiMgT3JnYW5pemluZyBkYXRhDQpkZl9xMmEgPC0gZGZfcTIgJT4lIHNlbGVjdChpc3N1ZV9kYXRlLCBDYXJyaWVyRGVsYXksIFdlYXRoZXJEZWxheSwgTkFTRGVsYXksIFNlY3VyaXR5RGVsYXksIExhdGVBaXJjcmFmdERlbGF5KQ0KZGZfcTJhIDwtIG1lbHQoZGZfcTJhICwgIGlkLnZhcnMgPSAnaXNzdWVfZGF0ZScsIHZhcmlhYmxlLm5hbWUgPSAnRGVsYXknKQ0KIyBQbG90IEF2ZXJhZ2UgSW5kaXZpZHVhbCBEZWxheXMgZm9yIG9sZCBwbGFuZXMNCmdncGxvdChkYXRhPXN1YnNldChkZl9xMmEsIGlzc3VlX2RhdGUgPCBhcy5EYXRlKCIxOTkzLTAxLTAxIikpLCANCiAgICAgICBhZXMoeD1pc3N1ZV9kYXRlLCB5PXZhbHVlKSkrICBzdGF0X3N1bW1hcnkoYWVzKHkgPSB2YWx1ZSxncm91cD0xLGNvbG91ciA9IERlbGF5KSwgZnVuPW1lYW4sIGdlb209ImxpbmUiLGdyb3VwPTEsIHNpemU9MSkgKyBnZ3RpdGxlKCJBdmVyYWdlIERlbGF5IHdpdGggSXNzdWUgRGF0ZSAoT2xkIFBsYW5lcykiKSArDQogIHhsYWIoIklzc3VlIERhdGUiKSArIHlsYWIoIkF2ZXJhZ2UgRGVsYXkgKG1pbnMpIikgICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjUsMC43OCkpICAgICAgICAgICAgICAgICAgIA0KYGBgDQoNCk9sZCBQbGFuZXMgaXNzdWVkIGFyb3VuZCAxOTkwIGFyZSBtb3JlIGxpa2VseSB0byBoYXZlIGRlbGF5cyBkdWUgQ2FycmllckRlbGF5LCB3aGljaCBpbmNsdWRlcyBtYWludGFpbmVuY2UgZXRjLg0KDQpgYGB7ciB0b3RhbCBpbmR2IGRlbGF5IG5vcm1hbH0NCiMgT3JnYW5pemluZyBkYXRhDQpkZl9xMmEgPC0gZGZfcTIgJT4lIHNlbGVjdChpc3N1ZV9kYXRlLCBDYXJyaWVyRGVsYXksIFdlYXRoZXJEZWxheSwgTkFTRGVsYXksIFNlY3VyaXR5RGVsYXksIExhdGVBaXJjcmFmdERlbGF5KQ0KZGZfcTJhIDwtIG1lbHQoZGZfcTJhICwgIGlkLnZhcnMgPSAnaXNzdWVfZGF0ZScsIHZhcmlhYmxlLm5hbWUgPSAnRGVsYXknKQ0KIyBQbG90IFRvdGFsIEluZGl2aWR1YWwgRGVsYXlzIGZvciBub3JtYWwgcGxhbmVzDQpnZ3Bsb3QoZGF0YT1zdWJzZXQoZGZfcTJhLCBpc3N1ZV9kYXRlID4gYXMuRGF0ZSgiMTk5Mi0xMi0zMSIpKSwgDQogICAgICAgYWVzKHg9aXNzdWVfZGF0ZSwgeT12YWx1ZSkpKyBnZW9tX2xpbmUoYWVzKGNvbG91ciA9IERlbGF5KSxzaXplPTEpICsgZ2d0aXRsZSgiVG90YWwgRGVsYXkgd2l0aCBJc3N1ZSBEYXRlIChOb3JtYWwgUGxhbmVzKSIpICsNCiAgeGxhYigiSXNzdWUgRGF0ZSIpICsgeWxhYigiVG90YWwgRGVsYXkgKG1pbnMpIikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQpgYGB7ciBhdmcgaW5kdiBkZWxheSBub3JtYWx9DQojIE9yZ2FuaXppbmcgZGF0YQ0KZGZfcTJhIDwtIGRmX3EyICU+JSBzZWxlY3QoaXNzdWVfZGF0ZSwgQ2FycmllckRlbGF5LCBXZWF0aGVyRGVsYXksIE5BU0RlbGF5LCBTZWN1cml0eURlbGF5LCBMYXRlQWlyY3JhZnREZWxheSkNCmRmX3EyYSA8LSBtZWx0KGRmX3EyYSAsICBpZC52YXJzID0gJ2lzc3VlX2RhdGUnLCB2YXJpYWJsZS5uYW1lID0gJ0RlbGF5JykNCiMgUGxvdCBBdmVyYWdlIEluZGl2aWR1YWwgRGVsYXlzIGZvciBub3JtYWwgcGxhbmVzDQpnZ3Bsb3QoZGF0YT1zdWJzZXQoZGZfcTJhLCBpc3N1ZV9kYXRlICA+IGFzLkRhdGUoIjE5OTItMTItMzEiKSksIA0KICAgICAgIGFlcyh4PWlzc3VlX2RhdGUsIHk9dmFsdWUpKSsgIHN0YXRfc3VtbWFyeShhZXMoeSA9IHZhbHVlLGdyb3VwPTEsY29sb3VyID0gRGVsYXkpLCBmdW49bWVhbiwgZ2VvbT0ibGluZSIsZ3JvdXA9MSwgc2l6ZSA9MSkgKyBnZ3RpdGxlKCJBdmVyYWdlIERlbGF5IHdpdGggSXNzdWUgRGF0ZSAoTm9ybWFsIFBsYW5lcykiKSArDQogIHhsYWIoIklzc3VlIERhdGUiKSArIHlsYWIoIkF2ZXJhZ2UgRGVsYXkgKG1pbnMpIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODgsMC43NykpICAgICAgICAgICAgICAgICAgICAgDQpgYGANCg0KTm9ybWFsIFBsYW5lcyBhcmUgbGlrZWx5IHRvIGhhdmUgQ2FycmllckRlbGF5cyBhcyB3ZWxsLg0KDQoNCiMjIyBEaXN0YW5jZSBEaXN0cmlidXRpb24gb2YgT2xkICYgTm9ybWFsIFBsYW5lcw0KDQpEaXN0YW5jZSBpcyB0aGVuIHVzZWQgdG8gY2hlY2sgdGhlIGRlbGF5cyB0aGF0IE9sZCBhbmQgTm9ybWFsIFBsYW5lcyB3aWxsIGhhdmUgd2l0aCByZXNwZWN0IHRvIHRoZSBudW1iZXIgb2YgbWlsZXMgdGhhdCB0aGV5IGhhdmUgdHJhdmVsbGVkLg0KYGBge3IgdG90YWwgZGVsYXkgdyBkaXN0IG9sZH0NCiMgT3JnYW5pemluZyBkYXRhDQpvbGRfcGxhbmVzPXN1YnNldChkZl9xMiwgaXNzdWVfZGF0ZSA8IGFzLkRhdGUoIjE5OTMtMDEtMDEiKSkgDQpvbGRfcGxhbmVzIDwtIG9sZF9wbGFuZXMgJT4lIHNlbGVjdChEaXN0YW5jZSwgQ2FycmllckRlbGF5LCBXZWF0aGVyRGVsYXksIE5BU0RlbGF5LCBTZWN1cml0eURlbGF5LCBMYXRlQWlyY3JhZnREZWxheSkNCmRmX3EyYSA8LSBtZWx0KG9sZF9wbGFuZXMsICBpZC52YXJzID0gJ0Rpc3RhbmNlJywgdmFyaWFibGUubmFtZSA9ICdEZWxheScpDQojIFBsb3QgVG90YWwgSW5kaXZpZHVhbCBEZWxheXMgYnkgRGlzdGFuY2UgZm9yIE9sZCBwbGFuZXMNCmdncGxvdChkYXRhID0gZGZfcTJhLCANCiAgICAgICBhZXMoeD1EaXN0YW5jZSwgeT12YWx1ZSkpKyBnZW9tX2xpbmUoYWVzKGNvbG91ciA9IERlbGF5KSxzaXplPTEpICsgZ2d0aXRsZSgiVG90YWwgRGVsYXkgd2l0aCBEaXN0YW5jZSAoT2xkIFBsYW5lcykiKSArDQogIHhsYWIoIkRpc3RhbmNlIChtaWxlcykiKSArIHlsYWIoIlRvdGFsIERlbGF5IChtaW5zKSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg4LDAuNzgpKQ0KYGBgDQoNCmBgYHtyIGF2ZyBkZWxheSB3IGRpc3Qgb2xkfQ0KIyBPcmdhbml6aW5nIGRhdGENCm9sZF9wbGFuZXM9c3Vic2V0KGRmX3EyLCBpc3N1ZV9kYXRlIDwgYXMuRGF0ZSgiMTk5My0wMS0wMSIpKSANCm9sZF9wbGFuZXMgPC0gb2xkX3BsYW5lcyAlPiUgc2VsZWN0KERpc3RhbmNlLCBDYXJyaWVyRGVsYXksIFdlYXRoZXJEZWxheSwgTkFTRGVsYXksIFNlY3VyaXR5RGVsYXksIExhdGVBaXJjcmFmdERlbGF5KQ0KZGZfcTJhIDwtIG1lbHQob2xkX3BsYW5lcywgIGlkLnZhcnMgPSAnRGlzdGFuY2UnLCB2YXJpYWJsZS5uYW1lID0gJ0RlbGF5JykNCiMgUGxvdCBBdmVyYWdlIEluZGl2aWR1YWwgRGVsYXlzIGJ5IERpc3RhbmNlIGZvciBPbGQgcGxhbmVzDQpnZ3Bsb3QoZGF0YSA9IGRmX3EyYSwgDQogICAgICAgYWVzKHg9RGlzdGFuY2UsIHk9dmFsdWUpKSsgIHN0YXRfc3VtbWFyeShhZXMoeSA9IHZhbHVlLGdyb3VwPTEsY29sb3VyID0gRGVsYXkpLCBmdW49bWVhbiwgZ2VvbT0ibGluZSIsZ3JvdXA9MSxzaXplPTEpKyBnZ3RpdGxlKCJBdmVyYWdlIERlbGF5IHdpdGggRGlzdGFuY2UgKE9sZCBQbGFuZXMpIikgKw0KICB4bGFiKCJEaXN0YW5jZSAobWlsZXMpIikgKyB5bGFiKCJBdmVyYWdlIERlbGF5IChtaW5zKSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg4LDAuNzgpKQ0KYGBgDQoNCkluIFIsIGFsdGhvdWdoIHRoZSBMYXRlQWlyY3JhZnREZWxheSBhbmQgTkFTRGVsYXkgaGF2ZSBoaWdoZXIgYXZlcmFnZSB2YWx1ZXMsIENhcnJpZXIgRGVsYXlzIGFsc28gaGF2ZSBvY2N1cnJlbmNlcyBvZiBhdmVyYWdlIGRlbGF5IHJhbmdpbmcgYXJvdW5kIDUwIG1pbnV0ZXMgYXQgMTUwMCBtaWxlcy4gDQoNCmBgYHtyIHRvdGFsIGRlbGF5IHcgZGlzdCBub3JtYWx9DQojIE9yZ2FuaXppbmcgZGF0YQ0Kbm9ybWFsX3BsYW5lcz1zdWJzZXQoZGZfcTIsIGlzc3VlX2RhdGUgPiBhcy5EYXRlKCIxOTkyLTEyLTMxIikpIA0Kbm9ybWFsX3BsYW5lcyA8LSBub3JtYWxfcGxhbmVzICU+JSBzZWxlY3QoRGlzdGFuY2UsIENhcnJpZXJEZWxheSwgV2VhdGhlckRlbGF5LCBOQVNEZWxheSwgU2VjdXJpdHlEZWxheSwgTGF0ZUFpcmNyYWZ0RGVsYXkpDQpkZl9xMmEgPC0gbWVsdChub3JtYWxfcGxhbmVzLCAgaWQudmFycyA9ICdEaXN0YW5jZScsIHZhcmlhYmxlLm5hbWUgPSAnRGVsYXknKQ0KIyBQbG90IFRvdGFsIEluZGl2aWR1YWwgRGVsYXlzIGJ5IERpc3RhbmNlIGZvciBOb3JtYWwgcGxhbmVzDQpnZ3Bsb3QoZGF0YSA9IGRmX3EyYSwgDQogICAgICAgYWVzKHg9RGlzdGFuY2UsIHk9dmFsdWUpKSsgZ2VvbV9saW5lKGFlcyhjb2xvdXIgPSBEZWxheSksc2l6ZT0xKSArIGdndGl0bGUoIlRvdGFsIERlbGF5IHdpdGggRGlzdGFuY2UgKE5vcm1hbCBQbGFuZXMpIikgKw0KICB4bGFiKCJEaXN0YW5jZSAobWlsZXMpIikgKyB5bGFiKCJUb3RhbCBEZWxheSAobWlucykiKSANCmBgYA0KDQpgYGB7ciBhdmcgZGVsYXkgdyBkaXN0IG5vcm1hbH0NCiMgT3JnYW5pemluZyBkYXRhDQpub3JtYWxfcGxhbmVzPXN1YnNldChkZl9xMiwgaXNzdWVfZGF0ZSA+IGFzLkRhdGUoIjE5OTItMTItMzEiKSkgDQpub3JtYWxfcGxhbmVzIDwtIG5vcm1hbF9wbGFuZXMgJT4lIHNlbGVjdChEaXN0YW5jZSwgQ2FycmllckRlbGF5LCBXZWF0aGVyRGVsYXksIE5BU0RlbGF5LCBTZWN1cml0eURlbGF5LCBMYXRlQWlyY3JhZnREZWxheSkNCmRmX3EyYSA8LSBtZWx0KG5vcm1hbF9wbGFuZXMsICBpZC52YXJzID0gJ0Rpc3RhbmNlJywgdmFyaWFibGUubmFtZSA9ICdEZWxheScpDQojIFBsb3QgQXZlcmFnZSBJbmRpdmlkdWFsIERlbGF5cyBieSBEaXN0YW5jZSBmb3IgTm9ybWFsIHBsYW5lcw0KZ2dwbG90KGRhdGEgPSBkZl9xMmEsIA0KICAgICAgIGFlcyh4PURpc3RhbmNlLCB5PXZhbHVlKSkrICBzdGF0X3N1bW1hcnkoYWVzKHkgPSB2YWx1ZSxncm91cD0xLGNvbG91ciA9IERlbGF5KSwgZnVuPW1lYW4sIGdlb209ImxpbmUiLGdyb3VwPTEpKyBnZ3RpdGxlKCJBdmVyYWdlIERlbGF5IHdpdGggRGlzdGFuY2UgKE5vcm1hbCBQbGFuZXMpIikgKw0KICB4bGFiKCJEaXN0YW5jZSAobWlsZXMpIikgKyB5bGFiKCJBdmVyYWdlIERlbGF5IChtaW5zKSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg4LDAuNzgpKQ0KYGBgDQoNCk5vcm1hbCBwbGFuZXMgaGF2ZSBsb3dlciBhdmVyYWdlIHZhbHVlcyBvZiBDYXJyaWVyIERlbGF5cyBvZiBsZXNzIHRoYW4gODAgbWludXRlcy4NCg0KSGVuY2UsIGl0IGlzIGV2aWRlbnQgdGhhdCBvbGRlciBwbGFuZXMgd291bGQgc3VmZmVyIG1vcmUgZGVsYXlzIGJ5IGhhdmluZyBhIGxhcmdlciB2YWx1ZSBvZiBDYXJyaWVyIERlbGF5IG1pbnV0ZXMgd2hpbGUgZmx5aW5nIHJvdXRlcyBsb25nZXIgdGhhbiAxNTAwIG1pbGVzLg0KDQoNCk92ZXJhbGwsIHRoZSBncm91cGluZyBieSBJc3N1ZSBZZWFyIHdhcyBjbGVhciBpbiBzaG93aW5nIHRoYXQgb2xkZXIgcGxhbmVzIGRvIHN1ZmZlciBmcm9tIG1vcmUgZGVsYXksIGJ1dCBvbmx5IGJ5IHVwIHRvIDggbWludXRlcyBvbiBhdmVyYWdlLCB3aGVyZSBMYXRlQWlyY3JhZnREZWxheSwgZm9sbG93ZWQgYnkgQ2Fycmllci9OQVNEZWxheSBhcmUgdGhlIHByaW1hcnkgY2F1c2VzLiA8cD4NCiAgICBXaGVuIHV0aWxpemVkIGZvciBsb25nLWhhdWwgZmxpZ2h0cywgb2xkZXIgcGxhbmVzIGFyZSBhbHNvIG1vcmUgbGlrZWx5IHRvIGhhdmUgZGVsYXlzIGR1ZSB0byBjYXJyaWVyIGRlbGF5cyB0aGF0IG1pZ2h0IGhhdmUgcmVzdWx0ZWQgZnJvbSBhaXJjcmFmdCBtYWludGVuYW5jZSBvciBpbnNwZWN0aW9uLiA8cD4NCiAgICAgICAgVGhlIGRpZmZlcmVuY2UgaW4gZGVsYXkgaG93ZXZlciBpcyBxdWl0ZSBuZWdsaWdpYmxlIGFuZCBpdCBtaWdodCBiZSBkdWUgdG8gYWlybGluZXMg4oCccGFkZGluZ+KAnSBhbmQgc2NoZWR1bGluZyBleHRyYSB0aW1lIGZvciBmbGlnaHRzIHRvIHByZXZlbnQgZmxpZ2h0cyBmcm9tIGJlaW5nIGNsYXNzaWZpZWQgYXMgZGVsYXllZCAoS3JhbWVyLCAyMDE5KS4NCg0KYGBge3IgcmVtb3ZlIGRmcTJ9DQojIFRvIHNhdmUgdXAgc3BhY2UNCnJtKGRmX3EyKQ0Kcm0oZGZfcTJhKQ0Kcm0oY291bnRzKQ0Kcm0obm9ybWFsX3BsYW5lcykNCnJtKG9sZF9wbGFuZXMpDQpgYGANCg0KPGJyPg0KDQojIFEzLiBIb3cgZG9lcyB0aGUgbnVtYmVyIG9mIHBlb3BsZSBmbHlpbmcgYmV0d2VlbiBkaWZmZXJlbnQgbG9jYXRpb25zIGNoYW5nZSBvdmVyIHRpbWUNCg0KDQpXZSB3aWxsIGZpbmQgdGhlIG1vc3QgcG9wdWxhciByb3V0ZXMgdG8gZ2F1Z2UgdGhlIG51bWJlciBvZiBwZW9wbGUgZmx5aW5nIGJldHdlZW4gdGhlc2UgZGlmZmVyZW50IGxvY2F0aW9ucy4NCg0KKiBEaXN0cmlidXRpb24gYnkgRmxpZ2h0IFJvdXRlcw0KKiBEaXN0cmlidXRpb24gYnkgU3RhdGUNCg0KYGBge3IgY29weWRhdGEzfQ0KIyBDcmVhdGUgbmV3IGRhdGFmcmFtZQ0KZGZfcTMgPC0gZmxpZ2h0X25vdGNhbmNlbGxlZA0KYGBgDQoNCg0KIyMgRGlzdHJpYnV0aW9uIGJ5IEZsaWdodCBSb3V0ZXMNCg0KDQpUbyBiZWdpbiwgdGhlIE9yaWdpbiBhbmQgRGVzdGluYXRpb24gYXJlIGNvbWJpbmVkIGludG8gYSBuZXcg4oCYRmxpZ2h0Um91dGXigJkgY29sdW1uIChlLmcuLCBPR0cgdG8gSE5MKS4NCg0KYGBge3IgT3JpZ2luVG9EZXN0fQ0KIyBDcmVhdGUgbmV3IGNvbHVtbiBhcyBpdHMgZmxpZ2h0IHJvdXRlDQpkZl9xMyRGbGlnaHRSb3V0ZSA8LSBwYXN0ZShkZl9xMyRPcmlnaW4sICJ0byIsIGRmX3EzJERlc3QpDQpgYGANCg0KYGBge3IgdmFsdWVjb3VudHMgcm91dGVzfQ0KIyBNb3N0IHBvcHVsYXIgcm91dGVzDQpkZl9xMyAlPiUgDQogIGdyb3VwX2J5KGRmX3EzJEZsaWdodFJvdXRlKSAlPiUNCiAgZHBseXI6OnN1bW1hcml6ZShDb3VudD1uKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoQ291bnQpKQ0KYGBgDQoNCg0KVGhlIHRvcCBmaXZlIG1vc3QgcG9wdWxhciByb3V0ZXMgYXJlOg0KDQoNCjEuIE9HRyB0byBITkwNCjIuIEhOTCB0byBPR0cNCjMuIExBWCB0byBMQVMNCjQuIFNBTiB0byBMQVgNCjUuIExBWCB0byBTQU4NCg0KDQpPdXQgb2YgdGhlIDUxOTYgZGlzdGluY3Qgcm91dGVzLCB0aGUgVG9wIDUgcm91dGVzIHdpdGggdGhlIGhpZ2hlc3QgY291bnQgYXJlIGlkZW50aWZpZWQgdG8gZXhhbWluZSBpZiB0aGUgbnVtYmVyIG9mIGZsaWdodHMgaGFzIGNoYW5nZWQgb3ZlciB0aGUgY291cnNlIG9mIHRoZSB5ZWFyLiANCg0KYGBge3IgdG9wNXJvdXRlc30NCiMgRXh0cmFjdCB0aGUgdG9wIDUgZmxpZ2h0IHJvdXRlcw0KZGZfcTMgPC0gZGZfcTNbZGZfcTMkRmxpZ2h0Um91dGUgJWluJSBjKCdPR0cgdG8gSE5MJywgJ0hOTCB0byBPR0cnLCAnTEFYIHRvIExBUycsICdMQVggdG8gU0FOJywnU0FOIHRvIExBWCcpLCBdDQpgYGANCg0KYGBge3IgYmFycGxvdCBmbGlnaHRyb3V0ZX0NCiMgQmFyUGxvdA0KZ2dwbG90KGRmX3EzLCBhZXMoeD0gTW9udGgpKSArIGdlb21fYmFyKGFlcyhmaWxsPWFzLmZhY3RvcihGbGlnaHRSb3V0ZSkpKSArIGdndGl0bGUoIkJhcnBsb3Qgb2YgRmxpZ2h0Um91dGUgY291bnRzIikNCmBgYA0KDQoNCmBgYHtyIGZsaWdodHJvdXRlIGxpbmV9DQojIEZsaWdodFJvdXRlIExpbmUgUGxvdA0KZ2dwbG90KGRmX3EzLCBhZXMoeD1Nb250aCwgZ3JvdXAgPSBmYWN0b3IoRmxpZ2h0Um91dGUpICwgY29sb3VyPWZhY3RvcihGbGlnaHRSb3V0ZSkpKSArIA0KICBnZW9tX2xpbmUoc3RhdCA9ICdjb3VudCcsc2l6ZT0xKSArIGdndGl0bGUoIkNvdW50IG9mIGZsaWdodHMgcGVyIEZsaWdodCBSb3V0ZSBieSBNb250aCIpICsNCiAgeGxhYigiTW9udGgiKSArIHlsYWIoIlRvdGFsIEZsaWdodCBDb3VudHMiKQ0KYGBgDQoNCkZvciB0aGUgZmlyc3QgZml2ZSBtb250aHMsIHRoZSBmbGlnaHQgcm91dGVzIFtPR0cgdG8gSE5MXSBhbmQgW0hOTCB0byBPR0ddIGhhZCByb3VnaGx5IDUwIGZld2VyIGZsaWdodHMgdGhhbiB0aGUgb3RoZXIgdGhyZWUgcm91dGVzLiA8cD4NCiAgICBIb3dldmVyLCBmcm9tIEp1bmUgdG8gQXVndXN0LCBib3RoIHJvdXRlcyBiZWdpbiB0byBpbmNyZWFzZSBpbiB0aGVpciBudW1iZXIgb2YgZmxpZ2h0cywgcmVhY2hpbmcgb3ZlciAxMDAgbW9yZSBmbGlnaHRzIHRoYW4gdGhlIG90aGVyIHJvdXRlcy4gPHA+DQogICAgICAgIEJldHdlZW4gU2VwdGVtYmVyIGFuZCBEZWNlbWJlciwgdGhlc2Ugcm91dGVzIGNvbnRpbnVlIHRvIGhhdmUgbW9yZSBmbGlnaHRzIHRoYW4gdGhlIG90aGVycy4NCg0KDQo8cD4NCg0KIyMgRGlzdHJpYnV0aW9uIGJ5IFN0YXRlDQoNClRvIG1ha2UgdGhlIG1vc3Qgb2YgdGhlIGRhdGEsIHRoZSBTdGF0ZXMgd2VyZSBxdWVyaWVkIHRvIGp1c3RpZnkgdGhlIG51bWJlciBvZiBwZW9wbGUgZmx5aW5nIGludGVyc3RhdGUgYW5kIGludHJhc3RhdGUuDQoNCmBgYHtyIHJlcGxhY2VkYXRhM30NCiMgQ3JlYXRlIG5ldyBkYXRhZnJhbWUNCmRmX3EzIDwtIGZsaWdodF9ub3RjYW5jZWxsZWQNCmBgYA0KDQpgYGB7ciBleHRyYWN0IHN0YXRlfQ0KIyBRdWVyeSBzdGF0ZSBmcm9tIGFpcnBvcnRfZGYgaW50byBkYXRhZnJhbWUNCmRmX3EzJHN0YXRlIDwtIGFpcnBvcnRfZGYkc3RhdGVbbWF0Y2goZGZfcTMkT3JpZ2luLCBhaXJwb3J0X2RmJGlhdGEpXQ0KYGBgDQoNCmBgYHtyIHZhbHVlY291bnRzIHN0YXRlfQ0KIyBNb3N0IHBvcHVsYXIgc3RhdGVzDQpkZl9xMyAlPiUgDQogIGdyb3VwX2J5KGRmX3EzJHN0YXRlKSAlPiUNCiAgZHBseXI6OnN1bW1hcml6ZShDb3VudD1uKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoQ291bnQpKQ0KYGBgDQoNClRvcCAzIHBvcHVsYXIgc3RhdGVzIGFyZToNCg0KMS4gQ2FsaWZvcm5pYQ0KMi4gVGV4YXMNCjMuIElsbGlub2lzDQoNCmBgYHtyIHRvcDNzdGF0ZXN9DQojIFNlbGVjdCB0aGUgdG9wIDMgc3RhdGVzDQpkZl9xMyA8LSBkZl9xM1tkZl9xMyRzdGF0ZSAlaW4lIGMoJ0NBJywgJ1RYJywgJ0lMJyksIF0NCmBgYA0KDQpgYGB7ciBzdGF0ZSBsaW5lY291bnR9DQojIENoZWNrIHRvcCAzIHN0YXRlcyB0cmF2ZWwgZmxpZ2h0IGNvdW50cw0KZ2dwbG90KGRmX3EzLCBhZXMoeD1Nb250aCwgZ3JvdXAgPSBmYWN0b3Ioc3RhdGUpICwgY29sb3VyPWZhY3RvcihzdGF0ZSkpKSArIA0KICBnZW9tX2xpbmUoc3RhdCA9ICdjb3VudCcsc2l6ZT0xKSArIGdndGl0bGUoIkNvdW50IG9mIGZsaWdodHMgcGVyIEZsaWdodCBSb3V0ZSBieSBNb250aCIpICsNCiAgeGxhYigiTW9udGgiKSArIHlsYWIoIlRvdGFsIEZsaWdodCBDb3VudHMiKQ0KYGBgDQoNCmBgYHtyIE9yaWdpblRvRGVzdDF9DQojIENyZWF0ZSBuZXcgY29sdW1uIG9mIHN0YXRlIGZsaWdodCByb3V0ZXMNCmRmX3EzJEZsaWdodFJvdXRlIDwtIHBhc3RlKGRmX3EzJHN0YXRlLCAidG8iLCBkZl9xMyRzdGF0ZVstMTowXSkNCmBgYA0KDQpgYGB7ciBzdGF0ZSBsaW5lfQ0KIyBGbGlnaHQgUm91dGVzIG9mIFRvcCAzIHN0YXRlcw0KZ2dwbG90KGRmX3EzLCBhZXMoeD1Nb250aCwgZ3JvdXAgPSBmYWN0b3IoRmxpZ2h0Um91dGUpICwgY29sb3VyPWZhY3RvcihGbGlnaHRSb3V0ZSkpKSArIA0KICBnZW9tX2xpbmUoc3RhdCA9ICdjb3VudCcsc2l6ZT0xKSArIGdndGl0bGUoIkNvdW50IG9mIGZsaWdodHMgcGVyIEZsaWdodCBSb3V0ZSBieSBNb250aCIpICsNCiAgeGxhYigiTW9udGgiKSArIHlsYWIoIlRvdGFsIEZsaWdodCBDb3VudHMiKQ0KYGBgDQoNCkZvciBib3RoIGludHJhc3RhdGUgYW5kIGludGVyc3RhdGUsIEZlYnJ1YXJ5IGFuZCBTZXB0ZW1iZXIgaGF2ZSB0aGUgZmV3ZXN0IGZsaWdodHMgd2l0aCBhcm91bmQgMTAwIGxlc3MgdGhhbiBvdGhlciBtb250aHMuIEFzIGV4cGVjdGVkLCB0aGUgbnVtYmVyIG9mIGZsaWdodHMgaW5jcmVhc2VzIGluIHRoZSBtaWRkbGUgb2YgdGhlIHllYXIsIGJldHdlZW4gSnVuZSBhbmQgQXVndXN0Lg0KPHA+DQoNCldpdGggdHdvIGRpc3RpbmN0IG1ldGhvZG9sb2dpZXMsIGl0IGlzIGV2aWRlbnQgdGhhdCBGZWJydWFyeSBoYXMgdGhlIGZld2VzdCBmbGlnaHRzLCBmb2xsb3dlZCBieSBTZXB0ZW1iZXIuIFRoZSBtb3JlIHBvcHVsYXIgdHJhdmVsbGluZyBtb250aHMgYXJlIGR1cmluZyBKdW5lIHRvIEF1Z3VzdCwgd2hpY2ggaXMgbGlrZWx5IGR1ZSB0byB0aGUgc3VtbWVyIGhvbGlkYXlzIGluIHRoZSBVU0Egd2hpY2ggbGFzdCAxMSB3ZWVrcyBmcm9tIEp1bmUgdG8gQXVndXN0IChTY2hvb2wgSG9saWRheXMgVVNBLCAyMDIyKS4NCg0KYGBge3IgcmVtb3ZlZGZxM30NCiMgVG8gc2F2ZSB1cCBzcGFjZQ0Kcm0oZGZfcTMpDQpgYGANCjxicj4NCg0KIyBRNC4gQ2FuIHlvdSBkZXRlY3QgY2FzY2FkaW5nIGZhaWx1cmVzIGFzIGRlbGF5cyBpbiBvbmUgYWlycG9ydCBjcmVhdGUgZGVsYXlzIGluIG90aGVycz8NCg0KDQpDYXNjYWRpbmcgZmFpbHVyZXMgb2NjdXIgd2hlbiBhIGZsaWdodCBkZWxheSBmb3Igb25lIHBsYW5lIGluIGFuIGFpcnBvcnQgY2F1c2VzIGEgZmxpZ2h0IGRlbGF5IGluIGFub3RoZXIuIA0KDQpUaGlzIGlzIGV4cGxhaW5lZCBieSB0aGUgZXhpc3RpbmcgdmFyaWFibGUgJ0xhdGVBaXJjcmFmdERlbGF5JyB3aGljaCBkZXNjcmliZXMgaG93IGEgcGFydGljdWxhciBmbGlnaHQgZGVsYXllZCBpbiBpdHMgT3JpZ2luIGFycml2ZXMgbGF0ZSBpbiBpdHMgRGVzdGluYXRpb24sIHRoZW4gYWZmZWN0aW5nIHRoZSBuZXh0IGZsaWdodCdzIGRlcGFydHVyZSBzaW5jZSB0aGUgc2FtZSBwbGFuZSB3YXMgdXNlZC4gVGhlIHJpcHBsZSBpbXBhY3Qgb2YgYSBwcmV2aW91cyBkZWxheSBhdCBkb3duc3RyZWFtIGFpcnBvcnRzIGhlbmNlIGNhdXNlcyBjYXNjYWRpbmcgZmFpbHVyZXMuDQoNClNpbmNlIFRhaWwgTnVtYmVycyBhcmUgaWRlbnRpZmljYXRpb24gbnVtYmVycyBvbiBhaXJjcmFmdCwgaXQgd2lsbCBiZSBlYXNpZXIgdG8gZm9jdXMgb24gZGF0YSB3aXRoIOKAmExhdGVBaXJjcmFmdERlbGF54oCZLCB0aGVuIGZvY3VzIG9uIGEgc2VsZWN0ZWQgYWlyY3JhZnQgYW5kIG9ic2VydmUgaXRzIGZsaWdodCBzY2hlZHVsZS4NCg0KV2Ugd2lsbCBhcHByb2FjaCB0aGUgcXVlc3Rpb24gYXMgZm9sbG93Og0KDQoqIE1ldGhvZG9sb2d5DQoqIEluaXRpYWwgVGVzdA0KKiBTZWNvbmRhcnkgVGVzdGluZw0KDQpgYGB7ciBjb3B5ZGF0YTR9DQojIENyZWF0ZSBuZXcgZGF0YWZyYW1lDQpkZl9xNCA8LSBtZXJnZWRfZGYNCmBgYA0KDQpgYGB7ciB2YWx1ZWNvdW50cyB0YWlsbnVtfQ0KIyBGaW5kIERhdGUgd2l0aCBoaWdoZXN0IHZhbHVlIGNvdW50cw0KZGZfcTQgJT4lIA0KICBncm91cF9ieShkZl9xNCRUYWlsTnVtKSAlPiUNCiAgZHBseXI6OnN1bW1hcml6ZShDb3VudD1uKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoQ291bnQpKQ0KYGBgDQoNCicwJyBhbmQgJzAwMDAwMCcgYXJlIGxpa2VseSBwcml2YXRlIGNvbmZpZGVudGlhbCBUYWlsTnVtcywgc28gd2UgaWdub3JlIHRob3NlIGRhdGEgYW5kIGZvY3VzIG9uIHRoZSB0b3AgMiBoaWdoZXN0IGNvdW50cyBvZiBUYWlsTnVtOg0KDQoqIE40NzhIQQ0KKiBOMzA4U1cNCjxicj4NCg0KIyMgTWV0aG9kb2xvZ3kNCg0KV2Ugd2lsbCBhcHByb2FjaCB0aGUgZGF0YXNldCBpbiB0aGUgZm9sbG93aW5nIHdheSB0byBlZmZlY3RpdmVseSBpbGx1c3RyYXRlIGNhc2NhZGluZyBmYWlsdXJlcywgdGhhdCBpcywgZGVsYXlzIGluIG9uZSBhaXJwb3J0IHdpbGwgY2F1c2UgZGVsYXlzIGluIGFub3RoZXI6DQoNCjEuCUZpbmQgSGlnaGVzdCDigJhUYWlsTnVt4oCZIGNvdW50IChIaWdoZXIgY2hhbmNlIG9mIGNvbnRpbnVvdXMgZmxpZ2h0cyBpbiBhIGRheSkNCjIuCUV4dHJhY3QgZGF0YSBmaXR0aW5nIGNvbmRpdGlvbiBvZiBIaWdoZXN0IOKAmFRhaWxOdW3igJkgJiAnTGF0ZUFpcmNyYWZ0RGVsYXknID4gMTUgbWludXRlcw0KMy4JRmluZCBIaWdoZXN0IOKAmERhdGXigJkgY291bnQgZnJvbSBleHRyYWN0ZWQgZGF0YSAoSGlnaGVyIGNoYW5jZSBvZiBjb250aW51b3VzIGZsaWdodHMgaW4gYSBkYXkpDQo0LglFeHRyYWN0IG92ZXJhbGwgZGF0YSBmaXR0aW5nIGNvbmRpdGlvbnMgb2YgSGlnaGVzdCDigJhUYWlsTnVt4oCZICYgSGlnaGVzdCDigJhEYXRl4oCZIGNvdW50DQo1LglTb3J0IGRhdGEgYnkg4oCYRGVwVGltZeKAmSAoVG8gc2VlIHRoZSBmbGlnaHQgc2NoZWR1bGUgb2Ygc2FtZSBhaXJjcmFmdCBieSBEZXBhcnR1cmUgVGltaW5nKQ0KDQpUaGlzIGFwcHJvYWNoIHdpbGwgYWxsb3cgdXMgdG8gYXNzZXNzIGlmIGEgZGVsYXllZCBmbGlnaHQgaW4gb25lIGFpcnBvcnQgbWF5IGNhdXNlIGEgZGVsYXkgaW4gYW5vdGhlci4gDQpUbyBlbnN1cmUgdGhhdCB0aGUgZGF0YSBleHRyYWN0aW9uIGFuZCBhbmFseXNpcyBhcHByb2FjaGVzIGFyZSB2YWxpZCwgdHdvIHNlcGFyYXRlIHRlc3RzIHdpbGwgYmUgY29uZHVjdGVkLg0KDQo8cD4NCg0KIyMjIEluaXRpYWwgVGVzdA0KDQpUaGUgaW5pdGlhbCB0ZXN0IHVzZWQgTjMwOFNXIHRvIGlkZW50aWZ5IHRoZSBoaWdoZXN0IGRhdGUgY291bnRzLg0KYGBge3IgcXVlcnkgbGF0ZWFpcmNyYWZ0c30NCiMgRmlyc3QgZXh0cmFjdGlvbiBvZiBkYXRhIHdpdGggdG9wIG1vc3QgVGFpbE51bSBjb3VudHMNCmRmX3E0XzEgPC0gbWVyZ2VkX2RmW21lcmdlZF9kZiRUYWlsTnVtICVpbiUgYygnTjMwOFNXJykgJiBtZXJnZWRfZGYkTGF0ZUFpcmNyYWZ0RGVsYXkgPiAxNSwgXQ0KYGBgDQoNCmBgYHtyIHZhbHVlY291bnQgZGF0ZXMxfQ0KZGZfcTRfMSAlPiUgDQogIGdyb3VwX2J5KGRmX3E0XzEkZGF0ZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpemUoQ291bnQ9bigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKENvdW50KSkNCmBgYA0KPGJyPg0KV2Ugd2lsbCBmb2N1cyBvbiB0aGUgZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IGNvdW50IHRvIHNlZSBpZiB0aGVyZSBpcyBhbnkgcmVsYXRpb24gdGhhdCBsZWFkcyB0byBjYXNjYWRpbmcgZmFpbHVyZS4NCg0KDQpgYGB7ciBxdWVyeSB0YWlsbnVtICYgZGF0ZX0NCiMgRXh0cmFjdGluZyBkYXRhc2V0IHRoYXQgbWF0Y2hlcyBvdXIgZmluZGluZ3MNCmRmX3E0XzEgPC0gbWVyZ2VkX2RmW21lcmdlZF9kZiRUYWlsTnVtICVpbiUgYygnTjMwOFNXJykgJiBtZXJnZWRfZGYkZGF0ZSA9PSAoJzIwMDYtMDMtMzEnKSwgXQ0KZGZfcTRfMVtvcmRlcihEZXBUaW1lKV1bLWMoMSksXQ0KYGBgDQoNCkl0IGlzIGNsZWFyIHRoYXQgb25lIGZsaWdodCdzIGRlbGF5IGluIGFuIGFpcnBvcnQgY2FuIGNhdXNlIGNhc2NhZGluZyBmYWlsdXJlcyBpbiBhbm90aGVyLg0KDQpUYWtpbmcgRmxpZ2h0IDQ1OCBhcyBhbiBleGFtcGxlOiBoYXMgdG8gZGVwYXJ0IGF0IDE4MzUgKDY6MzVwbSkgYnV0IGRlbGF5ZWQgZm9yIDUyIG1pbnMgdGlsbCAxOTI3ICg3OjI3cG0pLCBmbHlpbmcgZnJvbSBEQUwgdG8gU0FULiBTdXBwb3NlZCB0byByZWFjaCAxOTM1ICg3OjM1cG0pIGJ1dCBkZWxheWVkIDUwIG1pbnMgYW5kIHJlYWNoZWQgMjAyNSAoODoyNXBtKS4NCg0KVGhpcyA3NyBtaW5zIGRlbGF5IHRoZXJlYWZ0ZXIgY2F1c2VkIHRoZSBuZXh0IGZsaWdodCBmcm9tIFNBVCB0byBEQUwgKEZsaWdodE51bSAxNzgpIHRvIGhhdmUgZGVwYXJ0dXJlIGRlbGF5IGZvciA0NSBtaW51dGVzICg0MyBtaW5zIGZvciBMYXRlQWlyY3JhZnREZWxheSkgYWxzbyBzaW5jZSBpdHMgc2NoZWR1bGVkIGRlcGFydHVyZSB0aW1lIGlzIGF0IDIwMDUgKDg6MDVwbSksIGJ1dCBpdCBvbmx5IHRvb2sgb2ZmIGF0IDIwNTAgKDg6NTBwbSkuIEl0IGFsc28gYXJyaXZlZCA0MyBtaW51dGVzIGxhdGVyIHRoYW4gZXhwZWN0ZWQgdGltZSBvZiAyMTAwIGF0IDIxNDMuDQoNCldlIHdpbGwgZnVydGhlciBjaGVjayBvbiB0aGUgZnVsbCBkYXRhIChzYW1lIGRhdGUsIHNhbWUgVGFpbE51bSkgdG8gc2VlIHRoZSBmbGlnaHRzIHRoYXQgd2VyZSBub3QgaW5jbHVkZWQgaW4gdGhlIHNhbXBsZSB0aGF0IHdlIGV4dHJhY3RlZCwgc28gYXMgdG8gbWFrZSBhIG1vcmUgY29uY2x1c2l2ZSBzdGF0ZW1lbnQuDQoNCmBgYHtyIHF1ZXJ5MSB0YWlsbnVtICYgZGF0ZX0NCiMgTWF0Y2hpbmcgd2l0aCBmdWxsIGRhdGEgdG8gY29uZmlybSBhc3N1bXB0aW9uDQpkZl9xNF8xIDwtIG1lcmdlZGRmW21lcmdlZGRmJFRhaWxOdW0gJWluJSBjKCdOMzA4U1cnKSAmIG1lcmdlZGRmJGRhdGUgPT0gKCcyMDA2LTAzLTMxJyksIF0NCmRmX3E0XzFbb3JkZXIoRGVwVGltZSldWy1jKDE6OCwgMTAsMTIsMTQpLF0gJT4lIHNlbGVjdCgxMCw1OjgsMjksMTU6MTgsMzAsOSwxMSkgI1NlbGVjdCBpbXBvcnRhbnQgY29sdW1ucw0KYGBgDQoNCkNvbnNpZGVyaW5nIEZsaWdodCA3NTU6IHRoZXJlIHdhcyBhIDUyIG1pbnV0ZXMgQXJyRGVsYXkgaW4gdGhlIERlc3RpbmF0aW9uIEFpcnBvcnQgKExJVCkuIER1ZSB0byB0aGUgZGVsYXksIEZsaWdodCA0NTgsIHNjaGVkdWxlZCB0byBkZXBhcnQgYXQgMTcwMCAoNXBtKSwgd2FzIGRlbGF5ZWQgZm9yIDYwIG1pbnMgdGlsbCAxODAwICg2cG0pLCBmbHlpbmcgZnJvbSBMSVQgdG8gREFMLiANCg0KU3VwcG9zZWQgdG8gcmVhY2ggMTgwNSAoNjowNXBtKSBidXQgcmVhY2hlZCAxOTAwICg3cG0pLiBXaXRoIHRoZSA1NS1taW4gZGVsYXksIHRoZSBuZXh0IGZsaWdodCBmcm9tIERBTCB0byBTQVQgKEZsaWdodE51bSA0NTgpIHdhcyBzdWJzZXF1ZW50bHkgZGVsYXllZCBmb3IgNTIgbWludXRlcyAoNTAtbWluIGZvciBMYXRlQWlyY3JhZnREZWxheSkgc2luY2UgaXRzIHNjaGVkdWxlZCBkZXBhcnR1cmUgdGltZSB3YXMgYXQgMTgzNSAoNjozNXBtKSwgYnV0IGl0IG9ubHkgdG9vayBvZmYgYXQgMTkyNyAoNzoyN3BtKS4gSXQgdGhlbiBsYW5kZWQgNTAgbWludXRlcyBsYXRlciwgYXQgMjE0MywgdGhhbiBpdHMgZXhwZWN0ZWQgdGltZSBvZiAyMTAwLiANCg0KU2luY2UgdGhlIERlcFRpbWUgb3ZlcmxhcHBlZCB3aXRoIGl0cyBwcmV2aW91cyBmbGlnaHTigJlzIENSU0FyclRpbWUsIHRoZSBkZWxheSBwZXJzaXN0ZWQgYW5kIGVzY2FsYXRlZCB0aWxsIG5lYXIgbWlkbmlnaHQgb2YgdGhlIGRheSwgcmVzdWx0aW5nIGluIGNhc2NhZGluZyBmYWlsdXJlLg0KDQpXaXRoIHRoaXMsIHdlIGNhbiBvYnNlcnZlIHRoZSBjYXNjYWRpbmcgZmFpbHVyZSBmb3IgQ2FycmllciBXTiBvZiBzYW1lIFRhaWxOdW0sIHdoaWNoIHBlcnNpc3RlZCB0aHJvdWdob3V0IHRoZSBuaWdodCBhdCBkaWZmZXJlbnQgYWlycG9ydHMgKExJVCwgREFMLCBTQVQsIFRVTCBldGMuKQ0KPGJyPg0KDQpTaW5jZSB0aGUgQ1JTRGVwVGltZSBvdmVybGFwcGVkIHdpdGggaXRzIHByZXZpb3VzIGZsaWdodOKAmXMgQXJyVGltZSwgdGhlIGRlbGF5IHBlcnNpc3RlZCBhbmQgZXNjYWxhdGVkIHRpbGwgbmVhciBtaWRuaWdodCBvZiB0aGUgZGF5LCByZXN1bHRpbmcgaW4gY2FzY2FkaW5nIGZhaWx1cmVzLg0KDQoNCjxwPg0KDQojIyMgU2Vjb25kYXJ5IFRlc3RpbmcNCg0KVGhlIHNlY29uZGFyeSB0ZXN0IHVzZWQgTjQ3OEhBIHRvIGlkZW50aWZ5IHRoZSBoaWdoZXN0IGRhdGUgY291bnRzLg0KDQpgYGB7ciBxdWVyeTIgbGF0ZWFpcmNyYWZ0c30NCiMgRmlyc3QgZXh0cmFjdGlvbiBvZiBkYXRhIHdpdGggdG9wIG1vc3QgVGFpbE51bSBjb3VudHMNCmRmX3E0XzIgPC0gbWVyZ2VkX2RmW21lcmdlZF9kZiRUYWlsTnVtICVpbiUgYygnTjQ3OEhBJykgJiBtZXJnZWRfZGYkTGF0ZUFpcmNyYWZ0RGVsYXkgPiAxNSwgXQ0KYGBgDQoNCmBgYHtyIHZhbHVlY291bnQgZGF0ZXMyfQ0KZGZfcTRfMiAlPiUgDQogIGdyb3VwX2J5KGRmX3E0XzIkZGF0ZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpemUoQ291bnQ9bigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKENvdW50KSkNCmBgYA0KPGJyPg0KV2Ugd2lsbCBmb2N1cyBvbiB0aGUgZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IGNvdW50IHRvIHNlZSBpZiB0aGVyZSBpcyBhbnkgcmVsYXRpb24gdGhhdCBsZWFkcyB0byBjYXNjYWRpbmcgZmFpbHVyZS4NCg0KDQpgYGB7ciBxdWVyeTIgdGFpbG51bSAmIGRhdGV9DQojIEV4dHJhY3RpbmcgZGF0YXNldCB0aGF0IG1hdGNoZXMgb3VyIGZpbmRpbmdzDQpkZl9xNF8yIDwtIG1lcmdlZF9kZlttZXJnZWRfZGYkVGFpbE51bSAlaW4lIGMoJ040NzhIQScpICYgbWVyZ2VkX2RmJGRhdGUgPT0gKCcyMDA2LTAzLTMxJyksIF0NCmRmX3E0XzJbb3JkZXIoRGVwVGltZSldDQpgYGANCg0KVGFraW5nIEZsaWdodCAxNzkgYXMgYW4gZXhhbXBsZTogaGFzIHRvIGRlcGFydCBhdCAxMzI4ICgxOjI4cG0pIGJ1dCBkZWxheWVkIGZvciA4MiBtaW5zIHRpbGwgMTQ1MCAoMjo1MHBtKSwgZmx5aW5nIGZyb20gS09BIHRvIE9HRy4gU3VwcG9zZWQgdG8gcmVhY2ggMTM1NyAoMTo1N3BtKSBidXQgZGVsYXllZCA4NyBtaW5zIGFuZCByZWFjaGVkIDE1MjQgKDM6MjRwbSkuDQoNCldlIHdpbGwgZnVydGhlciBjaGVjayBvbiB0aGUgZnVsbCBkYXRhIChzYW1lIGRhdGUsIHNhbWUgVGFpbE51bSkgdG8gc2VlIHRoZSBmbGlnaHRzIHRoYXQgd2VyZSBub3QgaW5jbHVkZWQgaW4gdGhlIHNhbXBsZSB0aGF0IHdlIGV4dHJhY3RlZCwgc28gYXMgdG8gbWFrZSBhIG1vcmUgY29uY2x1c2l2ZSBzdGF0ZW1lbnQuDQoNCmBgYHtyIHF1ZXJ5MyB0YWlsbnVtICYgZGF0ZX0NCiMgTWF0Y2hpbmcgd2l0aCBmdWxsIGRhdGEgdG8gY29uZmlybSBhc3N1bXB0aW9uDQpkZl9xNF8yIDwtIG1lcmdlZGRmW21lcmdlZGRmJFRhaWxOdW0gJWluJSBjKCdONDc4SEEnKSAmIG1lcmdlZGRmJGRhdGUgPT0gKCcyMDA2LTAzLTMxJyksIF0NCmRmX3E0XzJbb3JkZXIoRGVwVGltZSldWy1jKDE6NCksXSAgJT4lIHNlbGVjdCgxMCw1OjgsMjksMTU6MTgsMzAsOSwxMSkgI1NlbGVjdCBpbXBvcnRhbnQgY29sdW1ucw0KDQpgYGANCg0KVGhpcyA4NyBtaW5zIGFycml2YWwgZGVsYXkgb2YgRmxpZ2h0TnVtIDE3OSB0aGVyZWFmdGVyIGNhdXNlZCB0aGUgbmV4dCBmbGlnaHQgZnJvbSBPR0cgdG8gSE5MIChGbGlnaHROdW0gMTc5KSB0byBoYXZlIGRlcGFydHVyZSBkZWxheSBmb3IgNzkgbWludXRlcyAoODkgbWlucyBmb3IgTGF0ZUFpcmNyYWZ0RGVsYXkpIGFsc28gc2luY2UgaXRzIHNjaGVkdWxlZCBkZXBhcnR1cmUgdGltZSBpcyBhdCAxNDI1ICgyOjI1cG0pLCBidXQgaXQgb25seSB0b29rIG9mZiBhdCAxNTQ0ICgzOjQ0cG0pLiBJdCBhbHNvIGFycml2ZWQgODkgbWludXRlcyBsYXRlciB0aGFuIGV4cGVjdGVkIHRpbWUgb2YgMTQ1OSBhdCAxNjI4Lg0KV2UgY2FuIGFsc28gc2VlIGZyb20gdGhlIGRheSB0aGF0IG9uZSBmbGlnaHQgZGVsYXkgaW4gYW4gYWlycG9ydCB3aWxsIGNhdXNlIGNhc2NhZGluZyBmYWlsdXJlcywgY29udHJpYnV0aW5nIHRvIEFyckRlbGF5IGFuZCBMYXRlQWlyY3JhZnREZWxheSBmb3IgZmxpZ2h0cywgaGVuY2UgY29uZmlybWluZyBvdXIgb2JzZXJ2YXRpb24uDQoNCg0KPHA+DQoNCkl0IGlzIGFsc28gd29ydGggbm90aW5nIHRoZSBpbnN0YW5jZXMgd2hlcmUgc3Vic2VxdWVudCBmbGlnaHRzIG1pZ2h0IG5vdCB1c2UgdGhlIHNhbWUgVGFpbE51bSwgYnV0IHRoZXkgYXJlIGFzc3VtZWQgZHVlIHRvIGEgbGFjayBvZiBzY2hlZHVsZSBpbmZvcm1hdGlvbi4gQSB0b3RhbCBvZiB0d28gdGVzdHMgd2VyZSBjb25kdWN0ZWQgaW4gUiwgdG8gZGVtb25zdHJhdGUgdGhlIHNhbWUgZWZmZWN0IG9mIGNhc2NhZGluZyBmYWlsdXJlcyB3aGVyZSBkZWxheXMgYXQgb25lIGFpcnBvcnQgY2F1c2VzIGRlbGF5cyBpbiBhbm90aGVyLiBUaGUgcHJldmlvdXMgZmxpZ2h0J3MgbGF0ZSBhcnJpdmFsLCB3aGljaCB1c2VkIHRoZSBzYW1lIHBsYW5lIHRoYXQgd291bGQgYmUgZGVwYXJ0aW5nLCBjYXVzZWQgdGhlIHN1YnNlcXVlbnQgZmxpZ2h04oCZcyBkZWxheS4gQXMgYSByZXN1bHQsIHRoZSBjdXJyZW50IGZsaWdodCB3aWxsIGRlcGFydCBsYXRlLCBzZXR0aW5nIG9mZiBhIGNoYWluIHJlYWN0aW9uIGNhdXNpbmcgcGFzc2VuZ2VycyBhdCBvdGhlciBhaXJwb3J0cyB0byBib2FyZCB0aGUgcGxhbmUgbXVjaCBsYXRlciBhcyB3ZWxsLg0KDQpgYGB7ciByZW1vdmUgZGZxNH0NCiMgVG8gc2F2ZSBzcGFjZQ0Kcm0oZGZfcTQpDQpybShkZl9xNF8xKQ0Kcm0oZGZfcTRfMikNCmBgYA0KPGJyPg0KDQojIFE1LiBVc2UgdGhlIGF2YWlsYWJsZSB2YXJpYWJsZXMgdG8gY29uc3RydWN0IGEgbW9kZWwgdGhhdCBwcmVkaWN0cyBkZWxheXMuDQoNCg0KV2l0aCB0aGUgZmxpZ2h0IGRhdGEgbGFiZWxsZWQsIFN1cGVydmlzZWQgTGVhcm5pbmcgYWxnb3JpdGhtcyBzdWNoIGFzIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIGFuZCBSYW5kb20gRm9yZXN0IGFyZSB1c2VkIHRvIGNvbnN0cnVjdCBSZWdyZXNzaW9uIGFuZCBDbGFzc2lmaWNhdGlvbiBwcmVkaWN0aW9uIG1vZGVscyBpbiBSLiA8cD4NCiAgICBUaGlzIHdvcmtzIGJ5IGFsbG93aW5nIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBsYWJlbCBvZiBuZXcgZGF0YSBwb2ludHMgYmFzZWQgb24gcGFzdCBkYXRhLiANCg0KPHA+DQoNClRvIHByZWRpY3QgZGVsYXlzLCB0aGVzZSBmZXcgc3VwZXJ2aXNlZCBsZWFybmluZyBtb2RlbHMgd2l0aCBzZWxlY3RlZCB2YXJpYWJsZXMgYXJlIHVzZWQ6DQoNCiogTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24NCiogUmFuZG9tIEZvcmVzdA0KDQpgYGB7ciBjb3B5ZGF0YTV9DQojIENyZWF0ZSBuZXcgZGF0YWZyYW1lDQpkZl9xNSA8LSBmbGlnaHRfbm90Y2FuY2VsbGVkDQpgYGANCg0KYGBge3Igc2VsZWN0IHZhcn0NCiMgU2VsZWN0IHZhcmlhYmxlIGNvbHVtbnMNCmRmX3E1IDwtIGRmX3E1ICU+JSBzZWxlY3QoTW9udGgsIFVuaXF1ZUNhcnJpZXIsIERlcFRpbWUsIENSU0RlcFRpbWUsIEFyclRpbWUsIENSU0FyclRpbWUsIEFyckRlbGF5LCBEZXBEZWxheSxEaXN0YW5jZSxUYXhpSW4sVGF4aU91dCwgQWN0dWFsRWxhcHNlZFRpbWUsQWlyVGltZSwgRGVsYXlTdGF0dXMpDQpgYGANCg0KVGltZS1yZWxhdGVkIGFuZCBGYWN0b3IgY29sdW1ucyBhcmUgc2VsZWN0ZWQgYXMgcHJlZGljdG9yIHZhcmFpYmxlcyBmb3IgYW5hbHlzaXMuDQoNCmBgYHtyIGNoYW5nZWZhY3RvcnN9DQojIENoYW5nZSBzZWxlY3RlZCB2YXIgYXMgZmFjdG9ycw0KZGZfcTUkVW5pcXVlQ2FycmllcjwtIGFzLmZhY3RvcihkZl9xNSRVbmlxdWVDYXJyaWVyKQ0KZGZfcTUkTW9udGggPC0gYXMuZmFjdG9yKGRmX3E1JE1vbnRoKQ0KYGBgDQoNCg0KYGBge3IgdHJhaW50ZXN0c3BsaXR9DQojIEhvbGQtb3V0IFZhbGlkYXRpb24gbWV0aG9kDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoNDIpDQoNCkZfc2FtcGxlID0gY3JlYXRlRGF0YVBhcnRpdGlvbih5PWRmX3E1JEFyckRlbGF5LCBwID0gMC43LCBsaXN0ID0gRikNCnRyYWluID0gZGZfcTVbRl9zYW1wbGUsXQ0KdGVzdCA9IGRmX3E1Wy1GX3NhbXBsZSxdDQpgYGANCg0KRmlyc3QsIOKAmENyZWF0ZURhdGFQYXJ0aXRpb27igJkgZmVhdHVyZSB3aWxsIGNyZWF0ZSBhIHRyYWluLXRlc3Qgc3BsaXQgdG8gcHJldmVudCBvdmVyZml0dGluZywgd2l0aCBUcmFpbnNldCBhY2NvdW50aW5nIGZvciA3MCUsIGFuZCBUZXN0c2V0IGZvciAzMCUuIFRoZSBtb2RlbCB3aWxsIGJlIHRyYWluZWQgdXNpbmcgdGhlIFRyYWluc2V0IGRhdGEsIGFuZCBpdHMgcGVyZm9ybWFuY2Ugd2lsbCBiZSBldmFsdWF0ZWQgYnkgcHJlZGljdGluZyB3aXRoIHRoZSB1bnNlZW4gVGVzdHNldC4gRm9yIHJlcHJvZHVjdGlvbiwgcmFuZG9tIHN0YXRlIGlzIHNldCB0byBhIHJhbmRvbSBzZWVkIDQyLg0KDQpUaGUgcmVzcG9uc2UgdmFyaWFibGUgWSwgZm9yIHRoZSBSZWdyZXNzaW9uIG1vZGVsIHdpbGwgY29tcHJpc2Ugb2YgQXJyRGVsYXkgKG1pbnV0ZXMpLg0KDQo8cD4NCg0KIyMgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24NCg0KTUxSIGlzIGEgcmVncmVzc2lvbiBtb2RlbCB3aGljaCBlbmFibGVzIHVzIHRvIHVuZGVyc3RhbmQgYW5kIGVzdGltYXRlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBtdWx0aXBsZSB2YXJpYWJsZXMuDQoNCmBgYHtyIG1sciBtb2RlbH0NCiMgTG9hZCBtb2RlbA0KbWxyX21vZGVsIDwtIGxtKEFyckRlbGF5IH4uIC1EZWxheVN0YXR1cywgZGF0YSA9IHRyYWluKQ0Kc3VtbWFyeShtbHJfbW9kZWwpDQpgYGANCg0KVGhlIG1vZGVsIHN1bW1hcnkgaXMgdGhlbiB1c2VkIHRvIGlkZW50aWZ5IHRoZSBzaWduaWZpY2FudCB2YXJpYWJsZXMgYWZmZWN0aW5nIEFyckRlbGF5LCBleGNsdWRpbmcgRGVsYXlTdGF0dXMgc2luY2UgdGhleSBhcmUgcmVsYXRlZC4gDQoNCjxwPg0KDQpUaGUgUjIgc2NvcmUgaXMgMC45MzEsIHdoZXJlIHByZWRpY3RvciB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsIGV4cGxhaW5lZCA5My4xJSBvZiB0aGUgdmFyaWF0aW9uIGluIFkgKEFyckRlbGF5KS4gUk1TRSBpcyBhbHNvIGNvbnNpZGVyYWJseSBmaXR0aW5nIGF0IDkuOTguIA0KDQoNCmBgYHtyfQ0KIyBQcmVkaWN0aW5nIGRlbGF5cw0KZGVsYXlQcmVkIDwtIHByZWRpY3QobWxyX21vZGVsLCB0ZXN0KSAgDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBBY3R1YWxuUHJlZCBkYXRhZnJhbWUNCkFjdHVhbG5QcmVkIDwtIGRhdGEuZnJhbWUoY2JpbmQoQWN0dWFsPXRlc3QkQXJyRGVsYXksIFByZWRpY3RlZD1yb3VuZChkZWxheVByZWQpKSkNCkFjdHVhbG5QcmVkDQojIDk2LjQlIGNvcnJlbGF0aW9uIGFjY3VyYWN5IG9mIHNpbWlsYXIgZGlyZWN0aW9uYWwgbW92ZW1lbnQNCmNvcnJlbGF0aW9uX2FjY3VyYWN5IDwtIGNvcihBY3R1YWxuUHJlZCkNCmNvcnJlbGF0aW9uX2FjY3VyYWN5DQpgYGANCg0KVGhlIENvcnJlbGF0aW9uIEFjY3VyYWN5IG9mIDk2LjQlIHNob3dzIGhvdyB0aGUgYWN0dWFsIGFuZCBwcmVkaWN0ZWQgdmFsdWVzIGhhdmUgc2ltaWxhciBkaXJlY3Rpb25hbCBtb3ZlbWVudHMuDQoNCmBgYHtyIHJtc2V9DQpzcXJ0KG1lYW4oKHRlc3QkQXJyRGVsYXkgLSBkZWxheVByZWQpXjIpKQ0KYGBgDQoNClRoZSBSMiBzY29yZSBpcyAwLjkzNjkgYW5kIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKSBpcyA5LjY3NjU2OS4NClRoZSBSMiB2YWx1ZSBzaG93cyB0aGF0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGluIHRoZSBtb2RlbCBhcmUgYWJsZSB0byBleHBsYWluIDkzLjY5JSBvZiB0aGUgdmFyaWF0aW9uIGluIEFyckRlbGF5Lg0KDQojIyBSYW5kb20gRm9yZXN0DQoNClVzZWQgZm9yIENsYXNzaWZpY2F0aW9uIGFuZCBSZWdyZXNzaW9uLCBSYW5kb20gRm9yZXN0IGlzIGEgU3VwZXJ2aXNlZCBMZWFybmluZyBhbGdvcml0aG0gdGhhdCBjb25zdHJ1Y3RzIG1hbnkgZGVjaXNpb24gdHJlZXMuIDxwPg0KUiB1c2VzIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHdoZXJlIHRoZSBvdXRwdXQgaXMgY2hvc2VuIGJ5IGEgbWFqb3JpdHkgdm90ZSBhbW9uZyBkZWNpc2lvbiB0cmVlcy4NCg0KYGBge3J9DQpzZXQuc2VlZCg0MikNCnJlcXVpcmUoY2FUb29scykNCnNhbXBsZSA9IHNhbXBsZS5zcGxpdChkZl9xNSREZWxheVN0YXR1cywgU3BsaXRSYXRpbyA9IC43MCkNCnRyYWluID0gc3Vic2V0KGRmX3E1LCBzZWxlY3QgPSAtYyhBcnJEZWxheSksIHNhbXBsZSA9PSBUUlVFKQ0KdGVzdCAgPSBzdWJzZXQoZGZfcTUsIHNlbGVjdCA9IC1jKEFyckRlbGF5KSwgc2FtcGxlID09IEZBTFNFKQ0KdHJhaW4kRGVsYXlTdGF0dXMgPC0gYXMuY2hhcmFjdGVyKHRyYWluJERlbGF5U3RhdHVzKQ0KdHJhaW4kRGVsYXlTdGF0dXMgPC0gYXMuZmFjdG9yKHRyYWluJERlbGF5U3RhdHVzKQ0KYGBgDQoNCkhlbmNlLCBhIG5ldyB0cmFpbi10ZXN0IHNldCB3aXRoIHJlc3BvbnNlIHZhcmlhYmxlIFkgYXMgRGVsYXlTdGF0dXMgb2Ygc2ltaWxhciBwYXJhbWV0ZXJzICgzMCUgVGVzdCkgaXMgY3JlYXRlZCwgd2l0aCB0aGUgQXJyRGVsYXkgY29sdW1uIHJlbW92ZWQgdG8gcHJldmVudCBtdWx0aWNvbGxpbmVhcml0eS4gDQoNCmBgYHtyfQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQpyZiA8LSByYW5kb21Gb3Jlc3QoRGVsYXlTdGF0dXMgfiAuLCBkYXRhPXRyYWluLCBudHJlZT0xMCwgcmFuZG9tX3N0YXRlID00MikNCmBgYA0KDQpgYGB7cn0NCnJmDQpgYGANCg0KVGhlIE91dC1vZi1iYWcgZXJyb3IgZXN0aW1hdGUgaXMgYXQgNi45JSwgd2hlcmUgQWNjdXJhY3kgPSAxIC1PT0IgZXJyb3IsIHNvIHRoZSBtb2RlbCBoYXMgYW4gYWNjdXJhY3kgb2YgOTMuMSUuIA0KDQpgYGB7cn0NCmltcG9ydGFuY2UocmYpICAgICAgICANCnZhckltcFBsb3QocmYpICAgDQpgYGANCg0KYGBge3J9DQpkZWxheVByZWQgPC0gcHJlZGljdChyZiwgdGVzdCkgIA0KYGBgDQoNCmBgYHtyfQ0KdGVzdCREZWxheVN0YXR1cyA8LSBhcy5mYWN0b3IodGVzdCREZWxheVN0YXR1cykNCg0KIyBDcmVhdGUgQWN0dWFsblByZWQgZGF0YWZyYW1lDQpBY3R1YWxuUHJlZCA8LSBkYXRhLmZyYW1lKGNiaW5kKEFjdHVhbD10ZXN0JERlbGF5U3RhdHVzLCBQcmVkaWN0ZWQ9ZGVsYXlQcmVkKSkNCkFjdHVhbG5QcmVkDQpgYGANCg0KDQoNCiMjIyBSRiBNb2RlbCB3aXRob3V0IERlcERlbGF5DQoNClNpbmNlIERlcERlbGF5IGFuZCBBcnJEZWxheSBoYXZlIGEgcG9zaXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCB3aGljaCBtaWdodCBsZWFkIHRvIG11bHRpY29sbGluZWFyaXR5LCB3ZSB3aWxsIGludmVzdGlnYXRlIHRvIHNlZSBpZiB0aGVyZSBhcmUgY2hhbmdlcyB0byB0aGUgZmFjdG9ycyB0aGF0IG1pZ2h0IGFmZmVjdCBEZWxheVN0YXR1cy4NCg0KYGBge3J9DQpyZiA8LSByYW5kb21Gb3Jlc3QoRGVsYXlTdGF0dXMgfiAuLSBEZXBEZWxheSwgZGF0YT10cmFpbiwgbnRyZWU9MTAsIHJhbmRvbV9zdGF0ZSA9NDIpDQpgYGANCg0KYGBge3J9DQpyZg0KYGBgDQoNCg0KYGBge3J9DQppbXBvcnRhbmNlKHJmKSAgICAgICAgDQp2YXJJbXBQbG90KHJmKSAgIA0KYGBgDQpNb2RlbHMgd2l0aCBhbmQgd2l0aG91dCBEZXBEZWxheSBzaG93IHRoZSBjaGFuZ2VzIGluIGF0dHJpYnV0aW5nIGZhY3RvcnMgY2F1c2luZyBEZWxheVN0YXR1cywgd2hlcmUgdGhlIHRvcCA2IGZhY3RvcnMgZm9yIGJvdGggbW9kZWxzIGFyZSBzaW1pbGFyLg0KDQpgYGB7cn0NCmRlbGF5UHJlZCA8LSBwcmVkaWN0KHJmLCB0ZXN0KSAgDQpgYGANCg0KYGBge3J9DQp0ZXN0JERlbGF5U3RhdHVzIDwtIGFzLmZhY3Rvcih0ZXN0JERlbGF5U3RhdHVzKQ0KDQojIENyZWF0ZSBBY3R1YWxuUHJlZCBkYXRhZnJhbWUNCkFjdHVhbG5QcmVkIDwtIGRhdGEuZnJhbWUoY2JpbmQoQWN0dWFsPXRlc3QkRGVsYXlTdGF0dXMsIFByZWRpY3RlZD1kZWxheVByZWQpKQ0KQWN0dWFsblByZWQNCmBgYA0KDQpJbiB0b3RhbCwgb25lIHJlZ3Jlc3Npb24gYW5kIG9uZSBjbGFzc2lmaWNhdGlvbiBtZXRob2Qgd2VyZSB0ZXN0ZWQgb3V0IGluIFIuIE92ZXJhbGwsIHRoZSBiZXN0IG1vZGVsIHRvIHByZWRpY3QgdGhlIGNvbnRpbnVvdXMgQXJyRGVsYXkgd291bGQgYmUgTUxSIHdpdGggOTUuNyUgYWNjdXJhY3kuIFRvIHByZWRpY3QgdGhlIGJpbmFyeSBvdXRjb21lIERlbGF5U3RhdHVzLCBSYW5kb20gRm9yZXN0IG1vZGVsIGlzIGFyb3VuZCA5My4xJSBhY2N1cmF0ZS4NCg==