Study Area

The study area is marked in red, and is know as “The Elbow.” It is about 145 km west-northwest of Tampa Bay, Florida. The area is a popular offshore fishing area that contains contains hard bottom ridges and is hypothesized to be a paleoshoreline shaped by wave action approximately 12,000 years ago.

Figure S1.1

Match Bathymetry and Raster Grids

Read in Data

Bathymetry

bathy_og<- raster("Data/raw/Multibeam/ElbowDec2015Cube_2m.tiff") #Original bathymetry
class      : RasterLayer 
dimensions : 13948, 4908, 68456784  (nrow, ncol, ncell)
resolution : 2, 2  (x, y)
extent     : 184571, 194387, 3064261, 3092157  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=17 +datum=WGS84 +units=m +no_defs 
source     : C:/Users/socce/Documents/Grad_School/Research/R_Projects/Elbow_Paper/Data/raw/Multibeam/ElbowDec2015Cube_2m.tiff 
names      : ElbowDec2015Cube_2m 
values     : -77.40717, -44.16052  (min, max)

Backscatter

backscatter_og<- raster("Data/raw/Multibeam/EL1_1mTimeSeriesBS_TrimmedtoMosaic_AVG800.tiff")
class      : RasterLayer 
dimensions : 26788, 8382, 224537016  (nrow, ncol, ncell)
resolution : 1, 1  (x, y)
extent     : 185107.5, 193489.5, 3064871, 3091659  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=17 +datum=WGS84 +units=m +no_defs 
source     : C:/Users/socce/Documents/Grad_School/Research/R_Projects/Elbow_Paper/Data/raw/Multibeam/EL1_1mTimeSeriesBS_TrimmedtoMosaic_AVG800.tiff 
names      : EL1_1mTimeSeriesBS_TrimmedtoMosaic_AVG800 
values     : -13.84911, -5.748498  (min, max)
EL_shp<- readOGR("Data/derived/shapefiles/EL_Shp.shp")
[1] "SpatialPolygonsDataFrame"
attr(,"package")
[1] "sp"
[1] "Extent: 777033 783941 3065379 3090118 (xmin, xmax, ymin, ymax)"
[1] "crs: +proj=utm +zone=16 +datum=WGS84 +units=m +no_defs"

Resample to macthing 10m grid

bathy<- projectRaster(from = bathy_og, res = 10, crs = make_crs("UTM 16N")) #Reproject/resample
bathy<- focal(bathy, w=matrix(data = 1, nrow = 3, ncol = 3), fun=mean, na.rm=TRUE, NAonly=TRUE) #Fill holes   
names(bathy)<- "bathy"
backscatter<- projectRaster(from= backscatter_og, to =bathy) #Reproject/resample
backscatter<- focal(backscatter, w=matrix(data = 1, nrow = 3, ncol = 3), fun=mean, na.rm=TRUE, NAonly=TRUE) #Fill holes   
names(backscatter)<- "backscatter"

Trim data to mask

bathy<- mask(x = bathy, EL_shp)
backscatter<- mask(x = backscatter, EL_shp)

Finalized Surfaces

Figure S1.2

Calculate Derivative Features

Determine scales of analysis

The Fibonacci sequence is a good way to systematically cover multiple-scales (Wilson, 2007).

my_window_sizes<- 1+ (2*fib_seq(10)[-c(1,2)])
print(my_window_sizes)
[1]  3  5  7 11 17 27 43 69

We will evaluate predictors at 8 different window sizes from 3x3 to 69x69

Bathymetric Derivative Features

Calculate bathymetric derivatives at various scales of analysis. This includes the slope, aspect (split into Northness and Eastness), Topographic Position Index (Whether an area is a local high or low), mean bathymetry, and standard deviation of bathymetry (a measure of rugosity).

slope<- terrain(bathy, opt= "slope", unit = "degrees")
names(slope)<- "slope_native"
aspect<- terrain(bathy, opt = c("aspect"), unit = "radians")
eastness<- sin(aspect)
names(eastness)<- "eastness_native"
northness<- cos(aspect)
names(northness)<- "northness_native"

for (i in my_window_sizes) {
  print(i)
  w<- matrix(data = 1, nrow = i, ncol = i)
  
  curr_slope<- focal(x = slope$slope_native, w, fun=mean, na.rm=TRUE, pad=TRUE)
  names(curr_slope)<- paste0("slope_",as.character(i),"x", as.character(i))
  slope<- addLayer(slope, curr_slope)
  
  curr_eastness<- focal(x = eastness$eastness_native, w, fun=mean, na.rm=TRUE, pad=TRUE)
  names(curr_eastness)<- paste0("eastness_",as.character(i),"x", as.character(i))
  eastness<- addLayer(eastness, curr_eastness)
  
  curr_northness<- focal(x = northness$northness_native, w, fun=mean, na.rm=TRUE, pad=TRUE)
  names(curr_northness)<- paste0("northness_",as.character(i),"x", as.character(i))
  northness<- addLayer(northness, curr_northness)
  
  curr_tpi<- TPI(bathy, d=c(i,i), na.rm=TRUE, pad = TRUE)
  names(curr_tpi)<-  paste0("tpi_",as.character(i),"x", as.character(i))
  
  curr_BathyMean<- focal(x = bathy, w, fun=mean, na.rm=TRUE, pad=TRUE)
  names(curr_BathyMean)<-  paste0("BathyMean_",as.character(i),"x", as.character(i))
  
  curr_BathySD<- focal(x = bathy, w, fun=sd, na.rm=TRUE, pad=TRUE)
  names(curr_BathySD)<-  paste0("BathySD_",as.character(i),"x", as.character(i))

  if(i==3){
    tpi<- curr_tpi
    BathyMean<- curr_BathyMean
    BathySD<- curr_BathySD 
    } else{
      tpi<- addLayer(tpi, curr_tpi)
      BathyMean<- addLayer(BathyMean, curr_BathyMean)
      BathySD<- addLayer(BathySD, curr_BathySD)
    }
}
bathy_deriv<- stack(BathyMean, BathySD, tpi, slope, eastness, northness)
bathy_deriv<- mask(x = bathy_deriv, EL_shp) #Trim back down b/c used pad and na.rm

Backscatter Derivative Features

Calculate backscatter derivatives at various scales of analysis. This includes the mean and standard deviation of bathymetry as well as texture metric derived from a Gray Level Co-occurence Matrix (GLCM).

The most common texture metrics from remote sensing were derived using the formulas from (Hall-Beyer 2017). Rotationally Invariant/Directionally Isotropic Harilick Texture Metrics were derived by calculating a symetrical GLCM with 32 gray levels. The texture metrics can be broken down into three groups: The contrast group (contrast, dissimilarity, and homogeneity), the orderliness group (ASM and Entropy), and the descriptive statistics group (mean, variance, and correlation;Hall-Beyer, 2017). At smaller window sizes, the correlation texture can sometimes be undefined (0/0) leading to holes in the resulting raster surfaces, so correlation at 3x3, 5x5, and 7x7 were removed from the analysis to prevent this.

backscatter_quant<- quantize_raster(backscatter, n_levels = 32,method = "equal prob")
for (i in my_window_sizes) {
  print(i)
  w<- matrix(data = 1, nrow = i, ncol = i)
  
  curr_bs_mean<- focal(backscatter, w, fun=mean, na.rm=TRUE, pad=TRUE)
  names(curr_bs_mean)<- "BackscatterMean"
  curr_bs_sd<- focal(backscatter, w, fun=sd, na.rm=TRUE, pad=TRUE) 
  names(curr_bs_sd)<- "BackscatterSD"
  
  curr_BSderiv<- stack(curr_bs_mean, 
                       curr_bs_sd,
                       glcm_textures(backscatter_quant, w= c(i,i), n_levels=32,  shift = list(c(1, 0), c(1, 1), c(0, 1), c(-1, 1)), metrics = c("glcm_contrast", "glcm_dissimilarity", "glcm_homogeneity", "glcm_ASM",
    "glcm_entropy", "glcm_mean", "glcm_variance", "glcm_correlation"), quantization= "none", na_opt = "center", pad = TRUE))
  
  names(curr_BSderiv)<- paste0(names(curr_BSderiv), "_", as.character(i), "x", as.character(i))
  
  if(i==3){
    backscatter_deriv<- curr_BSderiv} else{
      backscatter_deriv<- stack(backscatter_deriv, curr_BSderiv)
      rm(curr_BSderiv)
    }
}
backscatter_deriv<- mask(backscatter_deriv, EL_shp)
backscatter_deriv<- dropLayer(backscatter_deriv, which(names(backscatter_deriv)=="glcm_correlation_3x3"))
backscatter_deriv<- dropLayer(backscatter_deriv, which(names(backscatter_deriv)=="glcm_correlation_5x5"))
backscatter_deriv<- dropLayer(backscatter_deriv, which(names(backscatter_deriv)=="glcm_correlation_7x7")) #remove these b/c have NA's that create holes due to zero in denominator

Georeference Ground-Truth Data

Transects

Solid lines represent transects for training data, and dashed line represents the transect where observations were withheld in order to test the accuracy of substrate predictions

Figure S1.3

Read in Video Habitat Observations

Read in Timestamps for Camera Images

cam<- read_csv("Data/derived/CBASS/camera.csv")

Read in Position and Ancillary Data Streams

Hypack<- read_hypack(list.files("Data/raw/Hypack/", pattern="\\.RAW$", full.names = TRUE))
cable_out<- read_tsv("Data/raw/payout/payout_Feb&Oct2016.tsv", col_types = list(.default=col_double(), timestamp=col_datetime()))
Lat_Long<- get_HypackPos(Hypack)
names(Lat_Long)[2:3]<- c("ShipLon", "ShipLat")
Ship_Speed<- get_HypackSpeed(Hypack)

T1_oneHz<- make_1hz(alt_path = "Data/raw/CBASS/sensors/T1_D4/altimeter_readings.tsv", compass_path = "Data/raw/CBASS/sensors/T1_D4/compass_readings.tsv", ctd_path = "Data/raw/CBASS/sensors/T1_D4/ctd_redo_sal.tsv", Lat_Long = Lat_Long, Ship_Speed = Ship_Speed, cable_out = cable_out)

T3_oneHz<- make_1hz(alt_path = "Data/raw/CBASS/sensors/T3_D1/altimeter_readings.tsv", compass_path = "Data/raw/CBASS/sensors/T3_D1/compass_readings.tsv", ctd_path = "Data/raw/CBASS/sensors/T3_D1/ctd_redo_sal.tsv", Lat_Long = Lat_Long, Ship_Speed = Ship_Speed, cable_out = cable_out)

T5_oneHz<- make_1hz(alt_path = "Data/raw/CBASS/sensors/T5_D14/altimeter_readings.tsv", compass_path = "Data/raw/CBASS/sensors/T5_D14/compass_readings.tsv", ctd_path = "Data/raw/CBASS/sensors/T5_D14/ctd_redo_sal.tsv", Lat_Long = Lat_Long, Ship_Speed = Ship_Speed, cable_out = cable_out)

T6_oneHz<- make_1hz(alt_path = "Data/raw/CBASS/sensors/T6_D13/altimeter_readings.tsv", compass_path = "Data/raw/CBASS/sensors/T6_D13/compass_readings.tsv", ctd_path = "Data/raw/CBASS/sensors/T6_D13/ctd_redo_sal.tsv", Lat_Long = Lat_Long, Ship_Speed = Ship_Speed, cable_out = cable_out)

one_hz<- bind_rows(mutate(T1_oneHz, Transect="T1"),
                mutate(T3_oneHz, Transect="T3"),
                mutate(T5_oneHz, Transect="T5"),
                mutate(T6_oneHz, Transect="T6"))
one_hz

Estimate C-BASS Position

CBASS Position is estimated using Pythagorean theorem using the cable out (the hypotenuse) and CBASS depth (the height of the triangle). The cable out is zeroed when the the CBASS hits the water so to get true cable out from the trawl block, and the true height of this triangle, the height of the trawl block above the water (~6.1 m) is added to the recorded cable out and CBASS Depth. The layback behind the block is then calculated using pythagorean theorum. The y offset (fore/aft distance; ~24.4 m) between the trawl block and the GPS antenna is then added to this layback to get the layback of the CBASS system behind the logged GPS ship position. CBASS is assumed to follow the ship track but with a time delay. The layback distance is converted to a time delay by dividing the layback distance by the average ship speed over the past minute. To get the CBASS position for a given time, the the ship position from “x” number of seconds ago (the time delay) is grabbed from the table and assigned as the current CBASS position. For more information see the documentation and source code for mytools::calc_layback (arguments used GPS_Source=“Nothstar”, zeroed=“water”, cat_fact=1).

one_hz<- one_hz %>% mutate(Layback_m= calc_layback(payout = payout, depth = depth, GPS_Source = "Northstar", zeroed = "water", cat_fact = 1))
one_hz<- one_hz %>% mutate(Layback_sec= round(Layback_m/Ship_Speed_mps_1minAvg))
one_hz<- one_hz %>% mutate(TimeToMatch=timestamp-dseconds(Layback_sec))
temp_one_hz<- one_hz %>% select(timestamp, Transect, ShipLon, ShipLat)
names(temp_one_hz)<- c("TimeToMatch", "Transect", "CBASSLon", "CBASSLat")
one_hz<- one_hz %>% left_join(temp_one_hz, by=c("TimeToMatch","Transect")) %>% select(-TimeToMatch)
rm(temp_one_hz)
one_hz
write_csv(one_hz, "Data/derived/CBASS/onehz.csv")

Georeference Video Habitat Observations

Calculate Frame number. Videos are 1 minute long at 12 frames per second.

hab<- hab %>% mutate(Frame_num= (Vid * 12 * 60) + Sec*12)

Get timestamp for each frame to nearest second

hab<- hab %>% left_join(select(cam, timestamp, u_second, Frame_num, Transect), by= c("Transect", "Frame_num"))
hab<- hab %>% mutate(timestamp=round_date(timestamp + dmicroseconds(u_second), unit = "second")) %>% select(-u_second)

Link to position by timestamp

hab<- hab %>% left_join(select(one_hz, timestamp, Transect, CBASSLat, CBASSLon), by=c("timestamp", "Transect"))
hab
write_csv(hab, path = "Data/derived/CBASS/hab.csv")

Prepare training and validation set

Only include habitats that were the same as their previous and subsequent observations to remove transitional/mixed areas to create set used for training and validation.

TV_set<- hab %>% group_by(Transect) %>% const_hab("Substrate") %>% ungroup()
print(unique(TV_set$Substrate))
[1] "Sand"   "Rock"   "No_Vis"
TV_set<- TV_set %>% filter(Substrate!="No_Vis") #Remove bad visibility
TV_set$Substrate<- factor(TV_set$Substrate, levels = c("Rock", "Sand"))
TV_set<- TV_set %>% mutate(Set=ifelse(test = Transect=="T1", yes = "Validation", no = "Training")) #Reserve T1 for validation of results. Use other transects for training data

Create Spatial Object

TV_set<- TV_set %>% filter(!is.na(CBASSLat)) #Remove where there is no positioning data
TV_set<- spTransform(SpatialPointsDataFrame(coords = cbind(TV_set$CBASSLon, TV_set$CBASSLat),
                                data = as.data.frame(select(TV_set, -c(CBASSLon, CBASSLat))), 
                                proj4string = make_crs("WGS84")), CRSobj = make_crs("UTM 16N"))
TV_set<- TV_set[EL_shp,] #Remove points outside beyond bounds of the study area

The ground-truth dataset consisted of 3680 observations, where each observation was the substrate classification determined from the video at 15 second intervals. This consisted of 473 observations of rock, 3195 observations of sand, and 12 observations where substrate was not discernible. After censoring substrate determinations that differed from their previous and subsequent observations, those where substrate was not visible, and observations beyond the bounds of the multibeam survey or did not have associated positioning data, there were 238 observations of rock and 2533 observations of sand. These data were then split into training and validation sets. The training data set consisted of 210observations of rock and 1947 observations of sand. The validation transect consisted of 28 observations of rock and 586 observations of sand.

Figure S1.4

hab_onehz_joined<- hab %>% left_join(one_hz)

Transects total to 109 km in length

Predictive Habitat Mapping

predictor_vars<- stack(bathy, bathy_deriv, backscatter, backscatter_deriv)
n_predictors<-nlayers(predictor_vars)

Supervised Classification (Random Forest)

Variable Selection

There are 130 predictor variables in total. To decide on which variables to retain in the final model, we first use the Boruta feature selection algorithm, which through an iterative procedure determines whether the importance of a predictor is significantly greater (p_bonferroni < 0.05) than zero. Any predictors that do not have an importance significantly greater than zero are removed from the model. We then reduce co-linearity among predictors and remove redundant variables by eliminating any predictor if it is highly correlated (|r| > 0.8) with any variable that had a higher variable importance score.

data_df<- raster::extract(x = predictor_vars, y = filter(TV_set, Set=="Training"), method="simple", factors=TRUE, sp=TRUE)@data
data_df$Substrate<- factor(data_df$Substrate, levels=c("Rock", "Sand"))
sample_fraction<- data_df %>% count(Substrate) %>% mutate(prop=n/sum(n)) %>% pull(prop) %>% min() #Downsample to minority class
data_df<- data_df %>% select(-c(Transect, Vid, Sec, timestamp, Relief, Frame_num, Set)) %>% na.omit() 

set.seed(4)
Boruta_obj<- Boruta(Substrate ~ . , data=data_df, pValue = .05, mcAdj = TRUE, maxRuns = 5000, getImp= getImpRfRaw, sample.fraction=c(sample_fraction,sample_fraction))
Computing permutation importance.. Progress: 48%. Estimated remaining time: 24 minutes, 7 seconds.
Computing permutation importance.. Progress: 98%. Estimated remaining time: 1 minute, 6 seconds.
Computing permutation importance.. Progress: 84%. Estimated remaining time: 11 minutes, 40 seconds.
Computing permutation importance.. Progress: 90%. Estimated remaining time: 6 minutes, 33 seconds.
Computing permutation importance.. Progress: 25%. Estimated remaining time: 27 minutes, 29 seconds.
Computing permutation importance.. Progress: 89%. Estimated remaining time: 7 minutes, 13 seconds.
Computing permutation importance.. Progress: 73%. Estimated remaining time: 17 minutes, 40 seconds.
Computing permutation importance.. Progress: 95%. Estimated remaining time: 3 minutes, 25 seconds.
Growing trees.. Progress: 43%. Estimated remaining time: 1 hour, 20 minutes, 12 seconds.
Computing permutation importance.. Progress: 28%. Estimated remaining time: 2 hours, 31 minutes, 39 seconds.
Computing permutation importance.. Progress: 96%. Estimated remaining time: 3 minutes, 50 seconds.
Computing permutation importance.. Progress: 62%. Estimated remaining time: 17 minutes, 6 seconds.
Computing permutation importance.. Progress: 97%. Estimated remaining time: 21 seconds.
Computing permutation importance.. Progress: 25%. Estimated remaining time: 1 hour, 34 minutes, 43 seconds.
Computing permutation importance.. Progress: 82%. Estimated remaining time: 12 minutes, 59 seconds.
Computing permutation importance.. Progress: 100%. Estimated remaining time: 7 seconds.
Computing permutation importance.. Progress: 99%. Estimated remaining time: 23 seconds.
Boruta_obj<- TentativeRoughFix(x = Boruta_obj)
Predictor_Decisions<-data.frame(Decision = Boruta_obj[["finalDecision"]])
Predictor_Decisions<- Predictor_Decisions %>% mutate(Variable= rownames(Predictor_Decisions)) %>% select(Variable, Decision)
Boruta_Imp<- as.data.frame(Boruta_obj[["ImpHistory"]]) %>% summarize(across(everything(), .fns=mean)) %>% pivot_longer(cols = everything(), names_to = "Variable", values_to= "Importance")
sup_retained_predictors_df<- Predictor_Decisions %>% filter(Decision=="Confirmed") %>% left_join(Boruta_Imp, by="Variable") %>% arrange(desc(Importance))

sup_retained_predictors<- predictor_vars[[which(names(predictor_vars) %in% sup_retained_predictors_df$Variable)]]
corr_vals<- layerStats(sup_retained_predictors, 'pearson', na.rm=TRUE)
corr_vals<- as.data.frame(corr_vals$`pearson correlation coefficient`) %>% mutate(V1=row.names(corr_vals$`pearson correlation coefficient`))
corr_vals<- corr_vals %>% pivot_longer(cols= -V1, names_to = "V2", values_to="correlation")
corr_vals<- corr_vals %>% filter(V1!=V2)
sup_retained_predictors_df<- sup_retained_predictors_df %>% mutate(Keep=TRUE)
for (i in nrow(sup_retained_predictors_df):2) {
  curr_var<- sup_retained_predictors_df$Variable[i]
  more_important_predictors<- sup_retained_predictors_df[1:(i-1), "Variable"]
  max_corr<- corr_vals %>% filter((V1==curr_var) & (V2 %in% more_important_predictors)) %>% pull(correlation) %>% abs() %>% max()
  if(max_corr > 0.8){sup_retained_predictors_df$Keep[i]<- FALSE}
  }
sup_retained_predictors_df<- sup_retained_predictors_df %>% filter(Keep==TRUE)
sup_retained_predictors<- sup_retained_predictors[[which(names(sup_retained_predictors) %in% sup_retained_predictors_df$Variable)]]

Plot of Retained Variables

Figure S1.5

Fit and run model

#Fit and predict using random forest
n_retained_pred<- nlayers(sup_retained_predictors)
beginCluster(detectCores(logical = FALSE)-1)
set.seed(5)
sup_model<- superClass(img =sup_retained_predictors, trainData = filter(TV_set, Set=="Training"), responseCol = "Substrate", model = "ranger", mode = "classification", valData = filter(TV_set, Set=="Validation"), predict = FALSE, predType = "prob", kfold=5, minDist = 0, metric= "Kappa", num.trees=3000, tuneGrid = data.frame(mtry=2:(n_retained_pred-1), min.node.size=1, splitrule="gini"), keep.inbag = TRUE, sample.fraction= c(sample_fraction,sample_fraction), scale.permutation.importance = FALSE, importance= "permutation", local.importance = TRUE, verbose = FALSE)
endCluster()

sup_hab<- RStoolbox:::predict.superClass(object = sup_model, img = sup_retained_predictors, predType="raw")
names(sup_hab)<- "Substrate"
sup_hab<- as.factor(sup_hab)
levels(sup_hab)[[1]]$Substrate<- c("Rock", "Sand")

#Uncertainty
sup_hab_prob<- RStoolbox:::predict.superClass(sup_model, sup_retained_predictors, predType = "prob")
names(sup_hab_prob)<- c("Rock", "Sand")
sup_hab_entropy<- mytools::calc_entropy(sup_hab_prob)
names(sup_hab_entropy)<- "Shannon_Entropy"

Model Diagnostics

Model diagnostics showing that the overall Out-Of-Bag error rate plateaus, meaning that a sufficient number of decision trees have been used in the model

Figure S1.6

Aggregating predictions.. Progress: 46%. Estimated remaining time: 1 hour, 8 minutes, 49 seconds.

Resulting Maps

The left panel shows the predicted substrate across the study area based on our supervised classification model. In addition to the predictions, the random forest model allows for an entropy map which plots the uncertainty associated with the classification for each pixel by quantifying the level of disagreement among the all the different decision trees in our random forest model.

Figure S1.7

Variable Importance

Figure S1.8

Acuraccy Assessment

Accuracy assessment was performed by comparing the predictions to the observed ground-truth habitat observations on the transect that was reserved for validation and not used to fit the model.

          Reference
Prediction Rock Sand
      Rock   23    9
      Sand    5  575

Overall Accuracy = 97.7%

Kappa = 0.75

A Kappa score of greater than 0.6 indicates substantial agreement between predictions and observations.

Unsupervied Classification

Principal Components Analysis

First, we conduct a Principal Component Analysis on Z-score normalized variables (spca=TRUE) to remove co-linearity.

PCA_layers<- rasterPCA(predictor_vars, spca = TRUE)

As each sequential principal component explains a smaller percentage of the variation in the data, next we must determine how many principal components to retain. We will retain only a subset that are determined to be important. There are several methods used to identify the appropriate number of principal components to retain (Jackson, 1993). We compared the variation explained by each Principal Component and the variation expected to be explained by simulated data in which the variance was distributed randomly among the principal components as modeled by a broken-stick distribution (Frontier, 1976). The first 9 explained more than could be expected by random chance so the first 9 principal components were retained. These first 9 principal components explain 86% of the variation in the original data.

Figure S1.9

Plot of the retained principal components Figure S1.10

Run clustering on the retained principal components

beginCluster(detectCores(logical = FALSE)-1)
set.seed(5)
unsup<- unsuperClass(img =PCA_layers$map[[1:n_pcs]], nClasses = 4, nstarts=100, nIter = 1000, clusterMap = FALSE, algorithm = "MacQueen")
endCluster()
names(unsup$map)<- "Acoustic_Cluster"
unsup$map<- as.factor(unsup$map)
levels(unsup$map)[[1]]$Cluster<- c("1", "2", "3", "4")

Interpret Acoustic clusters

Using the ground-truth data points, the substrate observations within each cluster were counted up and each cluster was interpreted as the substrate that occured most often within it. The firt panel shows the cluster number, and the right panel shows the classified map based on majority vote of the ground-truth habitat observations in each cluster (see table below).

acoustic_class<- raster::extract(x = unsup$map, y = filter(TV_set, Set=="Training"), method="simple", factors=FALSE, sp=TRUE)@data
acoustic_class<- acoustic_class %>% select(Substrate, Acoustic_Cluster) %>% na.omit()
votes<- acoustic_class %>% group_by(Acoustic_Cluster) %>% count(Substrate) %>% ungroup()
votes_table<- votes %>% pivot_wider(names_from = Substrate, values_from=n, values_fill=0)
votes_table<- votes_table %>% mutate(Assigned_Class = ifelse(test = Sand>Rock, yes = "Sand", no = "Rock"))
votes_table

Figure S1.11

Acuraccy Assessment

Accuracy assessment was performed by comparing the predictions to the observed ground-truth habitat observations on the transect that was reserved for validation and not used to fit the model.

unsup_val<- validateMap(map = unsup_hab, valData = filter(TV_set, Set=="Validation"), responseCol = "Substrate", mode="classification", classMapping=data.frame(class=c("Rock", "Sand"), classID= c(1,2)))
          Reference
Prediction Rock Sand
      Rock   10    0
      Sand   18  585

Overall Accuracy = 97.1%

Kappa = 0.51

A Kappa score of greater than 0.4 indicates moderate agreement between predictions and observations.

Comparison of Supervised and Unsupervised

Difference Maps

For areas of disagreement, the legend is formatted as supervised/unsupervised.

Figure S1.12

For areas of disagreement, the legend is formatted as supervised/unsupervised.

diff_map_plot<- tm_shape(droplevels(st_as_stars(diff_map, ignore_file=TRUE), exclude=NA), raster.downsample = FALSE)+
  tm_raster(palette = c("palegreen", "red","#FFEBBE"), title="", drop.levels = TRUE)+
   tm_graticules(lines=FALSE)
diff_map_plot

Khist and Kloc

In addition to comparing the agreement between ground-truth observations and predictions, Kappa can be used to compare the agreement in predictions between the two maps by looking at the agreement in corresponding classifications for each pixel. Additionally Kappa can be broken down into two components: Klocation and Khisto, where Kappa is defined as the product of Klocation and Khisto (Pontius, 2000; Hagen, 2002; Sousa et al 2002). Klocation is a measure of the similarity in the spatial distribution of the substrate, and Khisto is measure of the similarity in the frequency of class predictions (Pontius, 2000; Hagen, 2002; Sousa et al 2002). The two maps have a Kloc value of 0.807 indicating almost perfect agreement in the spatial distribution of classes, and a Khisto of 0.989 indicating almost perfect agreement in the frequency of class predictions. Overall this corresponds to a Kappa value of 0.798 indicating a substantial level of agreement between the two maps.

Monte-Carlo Permutation Test

A one-tailed Monte-Carlo permutation test with 1000 iterations on the difference in Kappa values was performed (McKenzie et al, 1996). This results of this test showed that the supervised classification map provided significantly higher agreement with the ground-truth observations as compared to the unsupervised classification map (p <= 0.001).

Overlaying vertical relief on substrate

Because higher relief features relatively rare, and are fairly small relative to the error associated with positioning of the CBASS, we did not directly try to predict this in the supervised and unsupervised models. However, we can measure vertical relief as a difference between the minimum and maximum values. Total depth was calculated from the CBASS sensors as CBASS depth + CBASS altitude (where altitude measurements have been adjusted for the pitch of the system). Vertical relief for each 15 second bin was calculated as the maximum change in depth over that 15 second observation window. A box plot of the assigned visual relief class, and the measured vertical relief from the sensors is shown below. In blue, orange, and red are the relief cutoffs for low, moderate, and high relief respectively as proposed by Smith et al (2011) in their reef fish survey of the Florida Keys. This appears to match up fairly well with our observations, so we will use these cutoffs: low relief <= 1m ; 1m < moderate relief <= 2m; high relief > 2m.

Figure S1.13

one_hz<- one_hz %>% mutate(Total_Depth= depth+ altitude*cos(radians(pitch))) #Calculate Total depth

one_hz<- one_hz %>% mutate(vertical_relief_15s = NA_real_)
for (i in 8:(nrow(one_hz)-7)) {
  curr_idx<- (i-7):(i+7)
  if(length(unique(one_hz$Transect[curr_idx]))!=1){
    next() #Prevent averaging across different transects
    }
  dvalues<- one_hz$Total_Depth[curr_idx]
  dmin<- min(dvalues, na.rm=TRUE)
  dmax= max(dvalues, na.rm=TRUE)
  vrelief<- dmax-dmin
  if(is.infinite(vrelief)){vrelief<-NA}
  one_hz$vertical_relief_15s[i]<- vrelief
}

hab_relief<- hab %>% left_join(one_hz, by= c("timestamp","Transect")) %>% select(timestamp, Transect, Relief, vertical_relief_15s) %>% filter(Relief!="None" & Relief != "No_Vis")
hab_relief$Relief<- factor(hab_relief$Relief, levels=c("Low_Relief", "Moderate_Relief", "High_Relief"))

visual_relief_plot<- ggplot(data = hab_relief, mapping = aes(x= Relief, y=vertical_relief_15s))+
  annotate("rect", xmin = -Inf, xmax = Inf, ymin = 0, ymax = 1, fill= "dodgerblue", alpha=.2)+
  annotate("rect", xmin = -Inf, xmax = Inf, ymin = 1, ymax = 2, fill= "orange", alpha=.2)+
  annotate("rect", xmin = -Inf, xmax = Inf, ymin = 2, ymax = Inf, fill= "red", alpha=.2)+
  geom_boxplot()+
  scale_y_continuous(breaks = seq(0, 8, by= 1), expand = c(0,0))+
  ylab("Vertical Relief (m) over 15 s")+
  xlab("Relief Class")
visual_relief_plot

These thresholds are then used to delineate higher relief areas directly from the bathymetry by calculating the difference between a central pixel and the minimum depth of surrounding pixels using a sliding 3x3 window.

Figure S1.14

w<- matrix(data = 1, nrow = 3,ncol = 3) #3x3 window
w[5]<- NA
min_bathy<- focal(x = bathy, w=w, fun=min, na.rm=TRUE) #Calculate minimum depth around a central pixel
min_bathy[is.infinite(min_bathy)]<- NA #Replace Inf with NA
relief<- bathy-min_bathy #Calculate vertical relief

relief_qrules<- matrix(c(-Inf,1,2, 1, 2, Inf, 1,2,3), nrow = 3,ncol = 3)
relief_classified<- raster::reclassify(relief, relief_qrules, include.lowest=FALSE, right=TRUE)
relief_classified<- as.factor(relief_classified)
levels(relief_classified)[[1]]$Relief_Class<- c("Low","Mod", "High")
Relief_Map_Plot<- tm_shape(relief, raster.downsample = FALSE)+
  tm_raster(palette=pal1, midpoint = NA, style="cont", title = "Relief (m)")+
   tm_graticules(lines=FALSE)
Relief_Classified_Plot<- tm_shape(st_as_stars(relief_classified, ignore_file=TRUE), raster.downsample = FALSE)+
  tm_raster(palette=c("gray", "yellow", "red"), title = "Relief Class")+
   tm_graticules(lines=FALSE)
tmap_arrange(Relief_Map_Plot, Relief_Classified_Plot)

Figure S1.15

sub_relief<- (sup_hab*10)+relief_classified
sub_relief[sub_relief==22]<-21
sub_relief[sub_relief==23]<-21 #Collapse sand to one category
sub_relief<- as.factor(sub_relief)
names(sub_relief)<- "Habitat"
levels(sub_relief)[[1]]$habitat<-  c("LR Rock", "MR Rock", "HR Rock", "Sand")
sub_relief_plot<- tm_shape(droplevels(st_as_stars(sub_relief, ignore_file=TRUE), exclude=NA), raster.downsample = FALSE)+
  tm_raster(palette = c("dodgerblue", "orange", "red", "#FFEBBE"))+
   tm_graticules(lines=FALSE)
sub_relief_plot

LS0tDQp0aXRsZTogIkVsYm93IE1hcHBpbmcgQW5hbHlzZXMiDQphdXRob3I6ICJBbGV4YW5kZXIgSWxpY2giDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgZmlnX3dpZHRoOiA2DQogICAgZmlnX2hlaWdodDogOQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gbm9ybWFsaXplUGF0aCgiLi4iKSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gRkFMU0UsIGRwaSA9IDMwMCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDkpICNEZWZhdWx0IGNodW5rIG9wdGlvbnMNCmluc3RhbGxlZF9wYWNrYWdlczwtIGluc3RhbGxlZC5wYWNrYWdlcygpDQpuZWVkZWRfcGFja2FnZXM8LSBjKCJjYXJldCIsICJSQ29sb3JCcmV3ZXIiLCAicGFyYWxsZWwiLCAic25vdyIsICJyYW5nZXIiLCAiQm9ydXRhIiwgImx1YnJpZGF0ZSIsICJyZ2RhbCIsICJzcCIsICJyYXN0ZXIiLCAic3RhcnMiLCAidG1hcCIsICJSU3Rvb2xib3giLA0KICAgICAgICAgICAgICAgICAgICAidGlkeXZlcnNlIiwgInJlYWR4bCIsICJzcGRwbHlyIiwgImNvbG9yUmFtcHMiLCAiZGV2dG9vbHMiKQ0KDQpwYWNrYWdlc190b19pbnN0YWxsPC0gbmVlZGVkX3BhY2thZ2VzWyEobmVlZGVkX3BhY2thZ2VzICVpbiUgaW5zdGFsbGVkX3BhY2thZ2VzKV0NCmlmKGxlbmd0aChwYWNrYWdlc190b19pbnN0YWxsKT4wKXsNCiAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlc190b19pbnN0YWxsKQ0KICB9DQppZighKCJteXRvb2xzIiAlaW4lIGluc3RhbGxlZF9wYWNrYWdlcykpew0KICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImFpbGljaC9teXRvb2xzQHYwLjMxIikgDQogIH0gDQppZighKCJHTENNVGV4dHVyZXMiICVpbiUgaW5zdGFsbGVkX3BhY2thZ2VzKSl7DQogIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWlsaWNoL0dMQ01UZXh0dXJlc0AwLjEiKSANCiAgfSAjSW5zdGFsbA0KDQpsaWJyYXJ5KGNhcmV0KSAjTW9kZWxsaW5nDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgI0NvbG9yIFBhbGV0dGVzDQpsaWJyYXJ5KHBhcmFsbGVsKSAjUGFyYWxsZWwgUHJvY2Vzc2luZw0KbGlicmFyeShzbm93KSAjUGFyYWxsZWwgUHJvY2Vzc2luZw0KbGlicmFyeShyYW5nZXIpICNGb3IgUmFuZG9tIEZvcmVzdCBNb2RlbA0KbGlicmFyeShCb3J1dGEpICNWYXJpYWJsZSBTZWxlY3Rpb24NCmxpYnJhcnkobHVicmlkYXRlKSAjVGltZSBEYXRhDQpsaWJyYXJ5KHJnZGFsKSAjU3BhdGlhbCBEYXRhDQpsaWJyYXJ5KHNwKSAjU3BhdGlhbCBkYXRhDQpsaWJyYXJ5KHJhc3RlcikgI1NwYXRpYWwgRGF0YQ0KbGlicmFyeShzdGFycykgI1NwYXRpYWwgZGF0YQ0KbGlicmFyeSh0bWFwKSAjU3BhdGlhbCBQbG90dGluZw0KbGlicmFyeShSU3Rvb2xib3gpICNTcGF0aWFsIE1vZGVsbGluZw0KbGlicmFyeSh0aWR5dmVyc2UpICNEYXRhIFdyYW5nbGluZw0KbGlicmFyeShyZWFkeGwpICNyZWFkIGV4Y2VsIHNoZWV0cw0KbGlicmFyeShzcGRwbHlyKSAjRGF0YSBXcmFuZ2xpbmcgZm9yIHNwYXRpYWwgZGF0YQ0KbGlicmFyeShHTENNVGV4dHVyZXMpICNHTENNIFRleHR1cmUgTWV0cmljcw0KbGlicmFyeShteXRvb2xzKSAjTXkgZnVuY3Rpb25zDQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNClN5cy5zZXRlbnYoVFo9J1VUQycpICNTZXQgdGltZSB0byBkaXNwbGF5IGluIFVUQw0Kb3B0aW9ucyhkaWdpdHMuc2Vjcz0gNikgI1RpbWUgZGlnaXRzIGRpc3BsYXllZCBhbmQgd3JpdHRlbiB0byA2IHBsYWNlcw0KcmFzdGVyT3B0aW9ucyhhZGRoZWFkZXIgPSAiRU5WSSIpICNQcmVzZXJ2ZXMgbmFtZXMgd2hlbiB3cml0aW5nIHJhc3RlciBzdGFja3MgdG8gZmlsZQ0KYGBgDQoNCmBgYHtyIHdhcm5pbmc9VFJVRSwgaW5jbHVkZT1GQUxTRX0NCiNteXRvb2xzIGFuZCBHTENNVGV4dHVyZXMgYXJlIHBhY2thZ2VzIEkgY3JlYXRlZC4gbXl0b29scyBpcyBhc3NvcnRlZCBmdW5jdGlvbnMgSSBoYXZlIG1hZGUgb3ZlciB0aW1lIGFuZCBHTENNVGV4dHVyZXMgY2FsY3VsYXRlcyB0ZXh0dXJlIG1ldHJpY3MNCmlmKHBhY2thZ2VWZXJzaW9uKCJteXRvb2xzIikhPSIwLjMiKXsNCiAgd2FybmluZygibXl0b29scyBpcyBhIGRpZmZlcmVudCB2ZXJzaW9uIHRoYW4gdGhlIG9uZSBJIHVzZWQgdG8gcnVuIHRoaXMgc2NyaXB0IGFuZCBtYXkgcHJvZHVjZSBkaWZmZXJlbnQgcmVzdWx0cyIpDQp9DQppZihwYWNrYWdlVmVyc2lvbigiR0xDTVRleHR1cmVzIikhPSIwLjAuMS4wIil7DQogIHdhcm5pbmcoIkdMQ01UZXh0dXJlcyBpcyBhIGRpZmZlcmVudCB2ZXJzaW9uIHRoYW4gdGhlIG9uZSBJIHVzZWQgdG8gcnVuIHRoaXMgc2NyaXB0IGFuZCBtYXkgcHJvZHVjZSBkaWZmZXJlbnQgcmVzdWx0cyIpDQp9DQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiNOb3RlIHRvIGxvYWQgdGhlIGVudmlyb25tZW50IGFzIGl0IHdvdWxkIGJlIGF0IHRoZSBlbmQgb2YgdGhlIHNjcmlwdCB3aXRob3V0IHJ1bm5pbmcgaXQsIHJ1biB0aGlzIGxpbmUgb2YgY29kZQ0KbG9hZCgiRGF0YS9SX2Vudmlyb25tZW50L0VsYm93X1JfTWFwcGluZ19FbnZpcm9ubWVudC5SRGF0YSIpDQpgYGANCg0KYGBge3IgRGVmaW5lIEZ1bmN0aW9ucywgaW5jbHVkZT1GQUxTRX0NCiNGdW5jdGlvbiBmb3IgcGFyc2luZyByYXcgZGF0YSBpbnRvIDFIeiB0YWJsZQ0KbWFrZV8xaHo8LSBmdW5jdGlvbihhbHRfcGF0aCwgY29tcGFzc19wYXRoLCBjdGRfcGF0aCwgTGF0X0xvbmcsIFNoaXBfU3BlZWQsIGNhYmxlX291dCl7DQogIExhdF9Mb25nPC0gTGF0X0xvbmcgJT4lIG11dGF0ZSh0aW1lc3RhbXA9IHJvdW5kX2RhdGUodGltZXN0YW1wLCB1bml0ID0gInNlY29uZCIpKSAlPiUNCiAgICBncm91cF9ieSh0aW1lc3RhbXApICU+JQ0KICAgIHN1bW1hcml6ZShTaGlwTG9uPW1lYW4oU2hpcExvbiwgbmEucm09VFJVRSksIFNoaXBMYXQ9bWVhbihTaGlwTGF0LCBuYS5ybT1UUlVFKSwgLmdyb3Vwcz0iZHJvcCIpDQoNCiAgY2FibGVfb3V0PC0gY2FibGVfb3V0ICU+JSBtdXRhdGUodGltZXN0YW1wPSByb3VuZF9kYXRlKHRpbWVzdGFtcCArIGRtaWNyb3NlY29uZHModV9zZWNvbmQpLCB1bml0ID0gInNlY29uZCIpKSAlPiUNCiAgICBncm91cF9ieSh0aW1lc3RhbXApICU+JQ0KICAgIHN1bW1hcml6ZShwYXlvdXQ9bWVhbihwYXlvdXQsIG5hLnJtPVRSVUUpLCAuZ3JvdXBzPSJkcm9wIikNCg0KICBTaGlwX1NwZWVkPC0gU2hpcF9TcGVlZCAlPiUgbXV0YXRlKFNoaXBfU3BlZWRfbXBzPVNwZWVkX2twaCooMTAwMC8oNjAqNjApKSkgI0NvbnZlcnQgdG8gbWV0ZXJzIHBlciBzZWNvbmQNCiAgU2hpcF9TcGVlZDwtIFNoaXBfU3BlZWQgJT4lIG11dGF0ZSh0aW1lc3RhbXA9IHJvdW5kX2RhdGUodGltZXN0YW1wLCB1bml0ID0gInNlY29uZCIpKSAlPiUNCiAgICBncm91cF9ieSh0aW1lc3RhbXApICU+JQ0KICAgIHN1bW1hcml6ZShTaGlwX1NwZWVkX21wcz1tZWFuKFNoaXBfU3BlZWRfbXBzLCBuYS5ybT1UUlVFKSwgLmdyb3Vwcz0iZHJvcCIpDQoNCiAgYWx0PC0gcmVhZF90c3YoYWx0X3BhdGgsIGNvbF90eXBlcyA9IGxpc3QoLmRlZmF1bHQ9Y29sX2RvdWJsZSgpLCB0aW1lc3RhbXA9Y29sX2RhdGV0aW1lKCkpKQ0KICBjb21wYXNzPC0gcmVhZF90c3YoY29tcGFzc19wYXRoLCBjb2xfdHlwZXMgPSBsaXN0KC5kZWZhdWx0PWNvbF9kb3VibGUoKSwgdGltZXN0YW1wPWNvbF9kYXRldGltZSgpKSkNCiAgY3RkPC0gcmVhZF90c3YoY3RkX3BhdGgsIGNvbF90eXBlcyA9IGxpc3QoLmRlZmF1bHQ9Y29sX2RvdWJsZSgpLCB0aW1lc3RhbXA9Y29sX2RhdGV0aW1lKCkpKQ0KDQogIGFsdCRhbHRpdHVkZVthbHQkYWx0aXR1ZGU+PTUwXTwtIE5BICM1MCBpcyBtYXggdmFsdWUgb2YgYWx0aW1ldGVyIDYwIGFueSB2YWx1ZSA1MCBvciBncmVhdGVyIGlzIHJlY29yZGVkIGFzIDUwDQogIGFsdDwtIGFsdCAlPiUgbXV0YXRlKHRpbWVzdGFtcD1yb3VuZF9kYXRlKHRpbWVzdGFtcCtkbWljcm9zZWNvbmRzKHVfc2Vjb25kKSwgdW5pdCA9ICJzZWNvbmQiKSkgJT4lDQogICAgZ3JvdXBfYnkodGltZXN0YW1wKSAlPiUNCiAgICBzdW1tYXJpemUoYWx0aXR1ZGU9bWVkaWFuKGFsdGl0dWRlLCBuYS5ybT1UUlVFKSwgLmdyb3Vwcz0iZHJvcCIpDQoNCiAgY29tcGFzczwtIGNvbXBhc3MgJT4lIG11dGF0ZSh0aW1lc3RhbXA9cm91bmRfZGF0ZSh0aW1lc3RhbXArZG1pY3Jvc2Vjb25kcyh1X3NlY29uZCksIHVuaXQgPSAic2Vjb25kIikpICU+JQ0KICAgIGdyb3VwX2J5KHRpbWVzdGFtcCkgJT4lDQogICAgc3VtbWFyaXplKGhlYWRpbmc9IG1lZGlhbihoZWFkaW5nLCBuYS5ybSA9IFRSVUUpLCBwaXRjaD1tZWRpYW4ocGl0Y2gsIG5hLnJtPVRSVUUpLCByb2xsPW1lZGlhbihyb2xsLCBuYS5ybT1UUlVFKSwgLmdyb3Vwcz0iZHJvcCIpDQoNCiAgY3RkPC0gY3RkICU+JSBtdXRhdGUodGltZXN0YW1wPXJvdW5kX2RhdGUodGltZXN0YW1wK2RtaWNyb3NlY29uZHModV9zZWNvbmQpLCB1bml0ID0gInNlY29uZCIpKSAlPiUNCiAgICBncm91cF9ieSh0aW1lc3RhbXApICU+JQ0KICAgIHN1bW1hcml6ZSh0ZW1wZXJhdHVyZT0gbWVhbih0ZW1wZXJhdHVyZSwgbmEucm09VFJVRSksIGRlcHRoPW1lYW4oZGVwdGgsIG5hLnJtPVRSVUUpLCBzYWxpbml0eT0gbWVhbihzYWxpbml0eSwgbmEucm09VFJVRSksIC5ncm91cHM9ImRyb3AiKQ0KDQogIG9uZV9oejwtIHRpYmJsZSh0aW1lc3RhbXA9c2VxLlBPU0lYdChmcm9tID1taW4oYyhhbHQkdGltZXN0YW1wLCBjb21wYXNzJHRpbWVzdGFtcCwgY3RkJHRpbWVzdGFtcCkpLWRzZWNvbmRzKDE4MCksIHRvID0gbWF4KGMoYWx0JHRpbWVzdGFtcCwgY29tcGFzcyR0aW1lc3RhbXAsIGN0ZCR0aW1lc3RhbXApKSwgYnkgPSAic2VjIikpDQoNCiAgb25lX2h6PC0gb25lX2h6ICU+JQ0KICAgIGxlZnRfam9pbihMYXRfTG9uZywgYnk9InRpbWVzdGFtcCIpICU+JQ0KICAgIGxlZnRfam9pbihTaGlwX1NwZWVkLCBieT0idGltZXN0YW1wIikgJT4lDQogICAgbGVmdF9qb2luKGFsdCwgYnk9InRpbWVzdGFtcCIpICU+JQ0KICAgIGxlZnRfam9pbihjb21wYXNzLCBieT0idGltZXN0YW1wIikgJT4lDQogICAgbGVmdF9qb2luKGN0ZCwgYnk9InRpbWVzdGFtcCIpICU+JQ0KICAgIGxlZnRfam9pbihjYWJsZV9vdXQsIGJ5PSJ0aW1lc3RhbXAiKQ0KDQogIG9uZV9oeiRTaGlwTG9uPC0gaW50ZXJwKG9uZV9oeiRTaGlwTG9uLCBuYS5ybSA9IFRSVUUpDQogIG9uZV9oeiRTaGlwTGF0PC0gaW50ZXJwKG9uZV9oeiRTaGlwTGF0LCBuYS5ybSA9IFRSVUUpDQogIG9uZV9oeiRhbHRpdHVkZTwtIGludGVycChvbmVfaHokYWx0aXR1ZGUsIG5hLnJtID0gVFJVRSkNCiAgb25lX2h6JGFsdGl0dWRlWzE4MF08LSBOQV9yZWFsXw0KICBvbmVfaHokcGl0Y2g8LSBpbnRlcnAob25lX2h6JHBpdGNoLCBuYS5ybSA9IFRSVUUpDQogIG9uZV9oeiRwaXRjaFsxODBdPC0gTkFfcmVhbF8NCiAgb25lX2h6JGRlcHRoPC0gaW50ZXJwKG9uZV9oeiRkZXB0aCwgbmEucm0gPSBUUlVFKQ0KICBvbmVfaHokZGVwdGhbMTgwXTwtIE5BX3JlYWxfDQogIG9uZV9oeiRwYXlvdXQ8LSBpbnRlcnAob25lX2h6JHBheW91dCwgbmEucm0gPSBUUlVFKQ0KDQogIG9uZV9oejwtIG9uZV9oeiAlPiUgbXV0YXRlKFNoaXBfU3BlZWRfbXBzXzFtaW5Bdmc9TkFfcmVhbF8pDQogIGZvciAoaSBpbiA2MDpucm93KG9uZV9oeikpIHsNCiAgICBvbmVfaHokU2hpcF9TcGVlZF9tcHNfMW1pbkF2Z1tpXTwtIG1lYW4ob25lX2h6JFNoaXBfU3BlZWRfbXBzWyhpLTU5KTppXSwgbmEucm09VFJVRSkNCiAgfSAjMSBtaW51dGUgcm9sbGluZyBhdmVyYWdlDQogIHJldHVybihvbmVfaHopDQp9DQoNCmZpYl9zZXE8LSBmdW5jdGlvbihuKXsNCiAgZmliPC0gcmVwKE5BX3JlYWxfLCBuKQ0KICBmaWJbMToyXTwtIGMoMCwxKQ0KICBmb3IgKGkgaW4gMzpsZW5ndGgoZmliKSkgew0KICBmaWJbaV08LSBmaWJbaS0xXStmaWJbaS0yXQ0KICB9DQogIHJldHVybihmaWIpfQ0KDQpleHRyYWN0X3Jhbmdlcl9vb2I8LSBmdW5jdGlvbihvYmosIHRyYWluaW5nRGF0YSwgcmVzcG9uc2VfY29sKXsNCiAgaWYoY2xhc3Mob2JqKVsxXT09InN1cGVyQ2xhc3MiKXsNCiAgICBvYmo8LSBvYmokbW9kZWx9DQogIA0KICBpZihjbGFzcyhvYmopWzFdPT0idHJhaW4iKXsNCiAgICB0cmFpbmluZ0RhdGE8LSBvYmokdHJhaW5pbmdEYXRhDQogICAgcmVzcG9uc2VfY29sPC0gIi5vdXRjb21lIg0KICAgIGZpbmFsX21vZDwtIG9iaiRmaW5hbE1vZGVsDQogIH0gZWxzZXsNCiAgICBmaW5hbF9tb2Q8LSBvYmp9DQogIG5fdHJlZXM8LSBmaW5hbF9tb2QkbnVtLnRyZWVzDQogIG5fY2xhc3NlczwtIGxlbmd0aCh1bmlxdWUodHJhaW5pbmdEYXRhW1tyZXNwb25zZV9jb2xdXSkpDQogIA0KICBUcmVlX09PQjwtIGRhdGEuZnJhbWUoVHJlZT0gcmVwKDE6bl90cmVlcywgZWFjaD0gbl9jbGFzc2VzKzEpLCBPT0IgPSBOQV9yZWFsXykNCiAgajwtMQ0KICBmb3IgKGkgaW4gMTpuX3RyZWVzKSB7DQogICAgaWR4PC0gZmluYWxfbW9kJGluYmFnLmNvdW50c1tbaV1dPT0wDQogICAgT09CX2RhdGE8LSB0cmFpbmluZ0RhdGFbaWR4LF0NCiAgICBPT0I8LSBzdGF0czo6cHJlZGljdChmaW5hbF9tb2QsIE9PQl9kYXRhKQ0KICAgIE9PQl9wcmVkaWN0aW9uPC0gYXMuZGF0YS5mcmFtZShPT0IkcHJlZGljdGlvbnMpDQogICAgDQogICAgaWYobmNvbChPT0JfcHJlZGljdGlvbik+MSl7DQogICAgICBPT0JfcHJlZGljdGlvbjwtIE9PQl9wcmVkaWN0aW9uICU+JSBtdXRhdGUoSUQ9MTpucm93KGFsbF9vZihPT0JfcHJlZGljdGlvbikpKQ0KICAgICAgT09CX3ByZWRpY3Rpb248LSBPT0JfcHJlZGljdGlvbiAlPiUgcGl2b3RfbG9uZ2VyKC1JRCkNCiAgICAgIE9PQl9wcmVkaWN0aW9uPC0gT09CX3ByZWRpY3Rpb24gJT4lIGdyb3VwX2J5KElEKSAlPiUgZmlsdGVyKHZhbHVlPT1tYXgodmFsdWUpKSAlPiUgc2FtcGxlX24oMSkgJT4lIHVuZ3JvdXAoKSAlPiUgYXJyYW5nZShJRCkNCiAgICAgIE9PQl9wcmVkaWN0aW9uPC0gT09CX3ByZWRpY3Rpb24gJT4lIHNlbGVjdChuYW1lKQ0KICAgICAgbmFtZXMoT09CX3ByZWRpY3Rpb24pPC0gIlYxIg0KICAgICAgT09CX3ByZWRpY3Rpb248LSBPT0JfcHJlZGljdGlvbiRWMQ0KICAgICAgfSAjUmVmb3JtYXQgaWYgd2FzIGluIHByb2JhaWxpdHkgaW5zdGVhZCBvZiBjbGFzc2lmaWNhdGlvbiBtb2RlDQoNCiAgICBPYnNlcnZlZF92c19QcmVkaWN0ZWQ8LSBkYXRhLmZyYW1lKE9ic2VydmVkPSBPT0JfZGF0YVtbcmVzcG9uc2VfY29sXV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcmVkaWN0ZWQ9IE9PQl9wcmVkaWN0aW9uKSANCiAgICBUcmVlX09PQiRPT0Jbal08LSBzdW0oT2JzZXJ2ZWRfdnNfUHJlZGljdGVkJE9ic2VydmVkIT1PYnNlcnZlZF92c19QcmVkaWN0ZWQkUHJlZGljdGVkKS9ucm93KE9ic2VydmVkX3ZzX1ByZWRpY3RlZCkNCiAgICBUcmVlX09PQiRUeXBlW2pdPC0gIk92ZXJhbGwiDQogICAgYnlfY2xhc3NPT0I8LSBPYnNlcnZlZF92c19QcmVkaWN0ZWQgJT4lIGdyb3VwX2J5KE9ic2VydmVkLCAuZHJvcD1GQUxTRSkgJT4lIHN1bW1hcml6ZShPT0I9IHN1bShPYnNlcnZlZCE9UHJlZGljdGVkKS9uKCksIC5ncm91cHM9ImRyb3AiKQ0KICAgIFRyZWVfT09CJE9PQlsoaisxKTooaituX2NsYXNzZXMpXTwtIGJ5X2NsYXNzT09CJE9PQg0KICAgIFRyZWVfT09CJFR5cGVbKGorMSk6KGorbl9jbGFzc2VzKV08LSBhcy5jaGFyYWN0ZXIoYnlfY2xhc3NPT0IkT2JzZXJ2ZWQpDQogICAgajwtIGorbl9jbGFzc2VzKzENCiAgfQ0KICBUcmVlX09PQjwtIFRyZWVfT09CICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gVHlwZSwgdmFsdWVzX2Zyb209T09CKQ0KICBNZWFuX09PQjwtIGRhdGEuZnJhbWUobnRyZWVzPSAxOm5fdHJlZXMpDQogIGZvciAoayBpbiAyOm5jb2woVHJlZV9PT0IpKSB7DQogICAgTkFfaWR4PC0gaXMubmEoVHJlZV9PT0JbLGtdKQ0KICAgIE1lYW5fT09CX3RlbXA8LSBUcmVlX09PQlshTkFfaWR4LGMoMSxrKV0NCiAgICBNZWFuX09PQl90ZW1wWywyXTwtIGN1bW1lYW4oYXMuZGF0YS5mcmFtZShNZWFuX09PQl90ZW1wKVssMl0pDQogICAgTWVhbl9PT0I8LSBNZWFuX09PQiAlPiUgbGVmdF9qb2luKE1lYW5fT09CX3RlbXAsIGJ5PWMoIm50cmVlcyI9IlRyZWUiKSkNCiAgfQ0KICByZXR1cm4oTWVhbl9PT0IpDQp9DQpgYGANCg0KIyBTdHVkeSBBcmVhDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KRkxfc2hwPC0gcmVhZE9HUigiRGF0YS9kZXJpdmVkL3NoYXBlZmlsZXMvRmxvcmlkYV9TaG9yZWxpbmVfXzFfdG9fMiUyQzAwMCUyQzAwMF9TY2FsZV8uc2hwIikgI1NvdXJjZTogaHR0cHM6Ly9nZW9kYXRhLm15ZndjLmNvbS9kYXRhc2V0cy9mbG9yaWRhLXNob3JlbGluZS0xLXRvLTIwMDAwMDAtc2NhbGU/Z2VvbWV0cnk9LTEwMi4zNjYlMkMyNC4zNTYlMkMtNjUuMjk4JTJDMzEuMTU2DQpFTF9zaHA8LSByZWFkT0dSKCJEYXRhL2Rlcml2ZWQvc2hhcGVmaWxlcy9FTF9TaHAuc2hwIikNCmBgYA0KVGhlIHN0dWR5IGFyZWEgaXMgbWFya2VkIGluIHJlZCwgYW5kIGlzIGtub3cgYXMgIlRoZSBFbGJvdy4iIEl0IGlzIGFib3V0IDE0NSBrbSB3ZXN0LW5vcnRod2VzdCBvZiBUYW1wYSBCYXksIEZsb3JpZGEuIFRoZSBhcmVhIGlzIGEgcG9wdWxhciBvZmZzaG9yZSBmaXNoaW5nIGFyZWEgdGhhdCBjb250YWlucyBjb250YWlucyBoYXJkIGJvdHRvbSByaWRnZXMgYW5kIGlzIGh5cG90aGVzaXplZCB0byBiZSBhIHBhbGVvc2hvcmVsaW5lIHNoYXBlZCBieSB3YXZlIGFjdGlvbiBhcHByb3hpbWF0ZWx5IDEyLDAwMCB5ZWFycyBhZ28uDQoNCkZpZ3VyZSBTMS4xDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTZ9DQp0bV9zaGFwZShGTF9zaHApKw0KICB0bV9maWxsKCkrDQogIHRtX3NoYXBlKEVMX3NocCkrDQogIHRtX2ZpbGwoY29sPSJyZWQiKSsNCiAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkrDQogIHRtX2NyZWRpdHMoImh0dHBzOi8vZ2VvZGF0YS5teWZ3Yy5jb20vZGF0YXNldHMiLCBwb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIikpDQpgYGANCg0KIyBNYXRjaCBCYXRoeW1ldHJ5IGFuZCBSYXN0ZXIgR3JpZHMNCg0KIyMgUmVhZCBpbiBEYXRhDQoNCkJhdGh5bWV0cnkNCmBgYHtyIH0NCmJhdGh5X29nPC0gcmFzdGVyKCJEYXRhL3Jhdy9NdWx0aWJlYW0vRWxib3dEZWMyMDE1Q3ViZV8ybS50aWZmIikgI09yaWdpbmFsIGJhdGh5bWV0cnkNCmBgYA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KYmF0aHlfb2cNCmBgYA0KQmFja3NjYXR0ZXINCmBgYHtyIH0NCmJhY2tzY2F0dGVyX29nPC0gcmFzdGVyKCJEYXRhL3Jhdy9NdWx0aWJlYW0vRUwxXzFtVGltZVNlcmllc0JTX1RyaW1tZWR0b01vc2FpY19BVkc4MDAudGlmZiIpDQpgYGANCg0KYGBge3IgZWNobz1GQUxTRX0NCmJhY2tzY2F0dGVyX29nDQpgYGANCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KRUxfc2hwPC0gcmVhZE9HUigiRGF0YS9kZXJpdmVkL3NoYXBlZmlsZXMvRUxfU2hwLnNocCIpDQpgYGANCmBgYHtyIGVjaG89RkFMU0V9DQpwcmludChzdW1tYXJ5KEVMX3NocCkkY2xhc3MpDQpwcmludChwYXN0ZSgiRXh0ZW50OiIsIHBhc3RlKGFzLmNoYXJhY3Rlcihyb3VuZChhcy52ZWN0b3IodChzdW1tYXJ5KEVMX3NocCkkYmJveCkpKSksIGNvbGxhcHNlPSIgIiksICIoeG1pbiwgeG1heCwgeW1pbiwgeW1heCkiKSkNCnByaW50KHBhc3RlKCJjcnM6Iiwgc3VtbWFyeShFTF9zaHApJHByb2o0c3RyaW5nKSkNCmBgYA0KDQojIyBSZXNhbXBsZSB0byBtYWN0aGluZyAxMG0gZ3JpZA0KYGBge3IgfQ0KYmF0aHk8LSBwcm9qZWN0UmFzdGVyKGZyb20gPSBiYXRoeV9vZywgcmVzID0gMTAsIGNycyA9IG1ha2VfY3JzKCJVVE0gMTZOIikpICNSZXByb2plY3QvcmVzYW1wbGUNCmJhdGh5PC0gZm9jYWwoYmF0aHksIHc9bWF0cml4KGRhdGEgPSAxLCBucm93ID0gMywgbmNvbCA9IDMpLCBmdW49bWVhbiwgbmEucm09VFJVRSwgTkFvbmx5PVRSVUUpICNGaWxsIGhvbGVzICAgDQpuYW1lcyhiYXRoeSk8LSAiYmF0aHkiDQpiYWNrc2NhdHRlcjwtIHByb2plY3RSYXN0ZXIoZnJvbT0gYmFja3NjYXR0ZXJfb2csIHRvID1iYXRoeSkgI1JlcHJvamVjdC9yZXNhbXBsZQ0KYmFja3NjYXR0ZXI8LSBmb2NhbChiYWNrc2NhdHRlciwgdz1tYXRyaXgoZGF0YSA9IDEsIG5yb3cgPSAzLCBuY29sID0gMyksIGZ1bj1tZWFuLCBuYS5ybT1UUlVFLCBOQW9ubHk9VFJVRSkgI0ZpbGwgaG9sZXMgICANCm5hbWVzKGJhY2tzY2F0dGVyKTwtICJiYWNrc2NhdHRlciINCmBgYA0KDQpUcmltIGRhdGEgdG8gbWFzaw0KYGBge3IgfQ0KYmF0aHk8LSBtYXNrKHggPSBiYXRoeSwgRUxfc2hwKQ0KYmFja3NjYXR0ZXI8LSBtYXNrKHggPSBiYWNrc2NhdHRlciwgRUxfc2hwKQ0KYGBgDQoNCiMjIEZpbmFsaXplZCBTdXJmYWNlcw0KDQpGaWd1cmUgUzEuMg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcud2lkdGg9MTAsIGluY2x1ZGU9VFJVRX0NCnBhbDE8LSBjb2xvclJhbXBzOjptYXRsYWIubGlrZSgxMDApDQoNCmJhdGh5X3Bsb3Q8LSB0bV9zaGFwZShiYXRoeSwgcmFzdGVyLmRvd25zYW1wbGUgPSBGQUxTRSkgKw0KICB0bV9yYXN0ZXIocGFsZXR0ZSA9IHBhbDEsIHN0eWxlPSAiY29udCIsIHRpdGxlID0gIkJhdGh5bWV0cnkgKG0pIikrDQogIHRtX2NvbXBhc3ModHlwZSA9ICI4c3RhciIsIHBvc2l0aW9uID0gYygicmlnaHQiLCAidG9wIikpKw0KICB0bV9zY2FsZV9iYXIoYnJlYWtzPSBjKDAsIDIuNSwgNSksIHRleHQuc2l6ZT0xKSsNCiAgdG1fY3JlZGl0cyh0ZXh0ID0gImEpIiwgcG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpLCBzaXplID0gMSkrDQogIHRtX2dyYXRpY3VsZXMobGluZXM9RkFMU0UpDQoNCmJhY2tzY2F0dGVyX3Bsb3Q8LSB0bV9zaGFwZShiYWNrc2NhdHRlciwgcmFzdGVyLmRvd25zYW1wbGUgPSBGQUxTRSkrDQogIHRtX3Jhc3RlcihwYWxldHRlID0gZ3JleS5jb2xvcnMoMTAwKSwgc3R5bGU9ICJjb250IiwgdGl0bGUgPSAiQmFja3NjYXR0ZXIgSW50ZW5zaXR5IChkQikiKSsNCiAgdG1fY29tcGFzcyh0eXBlID0gIjhzdGFyIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJ0b3AiKSkrDQogIHRtX3NjYWxlX2JhcihicmVha3M9IGMoMCwgMi41LCA1KSwgdGV4dC5zaXplPTEpKw0KICB0bV9jcmVkaXRzKHRleHQgPSAiYikiLCBwb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIiksIHNpemUgPSAxKSsNCiAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkNCg0KdG1hcF9hcnJhbmdlKGJhdGh5X3Bsb3QsIGJhY2tzY2F0dGVyX3Bsb3QpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKHggPSBiYXRoeSwgZmlsZW5hbWUgPSAiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9iYXRoeS50aWYiLCBvdmVyd3JpdGU9VFJVRSkNCndyaXRlUmFzdGVyKHggPSBiYXRoeSwgZmlsZW5hbWUgPSAiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9iYXRoeS5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCmJhdGh5PC0gcmFzdGVyKCJEYXRhL2Rlcml2ZWQvTXVsdGliZWFtL2JhdGh5LmdyZCIpDQp3cml0ZVJhc3Rlcih4ID0gYmFja3NjYXR0ZXIsIGZpbGVuYW1lID0gIkRhdGEvZGVyaXZlZC9NdWx0aWJlYW0vYmFja3NjYXR0ZXIudGlmIiwgb3ZlcndyaXRlPVRSVUUpDQp3cml0ZVJhc3Rlcih4ID0gYmFja3NjYXR0ZXIsIGZpbGVuYW1lID0gIkRhdGEvZGVyaXZlZC9NdWx0aWJlYW0vYmFja3NjYXR0ZXIuZ3JkIiwgb3ZlcndyaXRlPVRSVUUpDQpiYWNrc2NhdHRlcjwtIHJhc3RlcigiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9iYWNrc2NhdHRlci5ncmQiKQ0KYGBgDQoNCiMgQ2FsY3VsYXRlIERlcml2YXRpdmUgRmVhdHVyZXMNCg0KIyMgRGV0ZXJtaW5lIHNjYWxlcyBvZiBhbmFseXNpcw0KVGhlIEZpYm9uYWNjaSBzZXF1ZW5jZSBpcyBhIGdvb2Qgd2F5IHRvIHN5c3RlbWF0aWNhbGx5IGNvdmVyIG11bHRpcGxlLXNjYWxlcyAoV2lsc29uLCAyMDA3KS4NCmBgYHtyfQ0KbXlfd2luZG93X3NpemVzPC0gMSsgKDIqZmliX3NlcSgxMClbLWMoMSwyKV0pDQpwcmludChteV93aW5kb3dfc2l6ZXMpDQpgYGANCldlIHdpbGwgZXZhbHVhdGUgcHJlZGljdG9ycyBhdCBgciBsZW5ndGgobXlfd2luZG93X3NpemVzKWAgZGlmZmVyZW50IHdpbmRvdyBzaXplcyBmcm9tIGByIG1pbihteV93aW5kb3dfc2l6ZXMpYHhgciBtaW4obXlfd2luZG93X3NpemVzKWAgdG8gYHIgbWF4KG15X3dpbmRvd19zaXplcylgeGByIG1heChteV93aW5kb3dfc2l6ZXMpYA0KDQojIyBCYXRoeW1ldHJpYyBEZXJpdmF0aXZlIEZlYXR1cmVzDQpDYWxjdWxhdGUgYmF0aHltZXRyaWMgZGVyaXZhdGl2ZXMgYXQgdmFyaW91cyBzY2FsZXMgb2YgYW5hbHlzaXMuIFRoaXMgaW5jbHVkZXMgdGhlIHNsb3BlLCBhc3BlY3QgKHNwbGl0IGludG8gTm9ydGhuZXNzIGFuZCBFYXN0bmVzcyksIFRvcG9ncmFwaGljIFBvc2l0aW9uIEluZGV4IChXaGV0aGVyIGFuIGFyZWEgaXMgYSBsb2NhbCBoaWdoIG9yIGxvdyksIG1lYW4gYmF0aHltZXRyeSwgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBiYXRoeW1ldHJ5IChhIG1lYXN1cmUgb2YgcnVnb3NpdHkpLg0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJ30NCnNsb3BlPC0gdGVycmFpbihiYXRoeSwgb3B0PSAic2xvcGUiLCB1bml0ID0gImRlZ3JlZXMiKQ0KbmFtZXMoc2xvcGUpPC0gInNsb3BlX25hdGl2ZSINCmFzcGVjdDwtIHRlcnJhaW4oYmF0aHksIG9wdCA9IGMoImFzcGVjdCIpLCB1bml0ID0gInJhZGlhbnMiKQ0KZWFzdG5lc3M8LSBzaW4oYXNwZWN0KQ0KbmFtZXMoZWFzdG5lc3MpPC0gImVhc3RuZXNzX25hdGl2ZSINCm5vcnRobmVzczwtIGNvcyhhc3BlY3QpDQpuYW1lcyhub3J0aG5lc3MpPC0gIm5vcnRobmVzc19uYXRpdmUiDQoNCmZvciAoaSBpbiBteV93aW5kb3dfc2l6ZXMpIHsNCiAgcHJpbnQoaSkNCiAgdzwtIG1hdHJpeChkYXRhID0gMSwgbnJvdyA9IGksIG5jb2wgPSBpKQ0KICANCiAgY3Vycl9zbG9wZTwtIGZvY2FsKHggPSBzbG9wZSRzbG9wZV9uYXRpdmUsIHcsIGZ1bj1tZWFuLCBuYS5ybT1UUlVFLCBwYWQ9VFJVRSkNCiAgbmFtZXMoY3Vycl9zbG9wZSk8LSBwYXN0ZTAoInNsb3BlXyIsYXMuY2hhcmFjdGVyKGkpLCJ4IiwgYXMuY2hhcmFjdGVyKGkpKQ0KICBzbG9wZTwtIGFkZExheWVyKHNsb3BlLCBjdXJyX3Nsb3BlKQ0KICANCiAgY3Vycl9lYXN0bmVzczwtIGZvY2FsKHggPSBlYXN0bmVzcyRlYXN0bmVzc19uYXRpdmUsIHcsIGZ1bj1tZWFuLCBuYS5ybT1UUlVFLCBwYWQ9VFJVRSkNCiAgbmFtZXMoY3Vycl9lYXN0bmVzcyk8LSBwYXN0ZTAoImVhc3RuZXNzXyIsYXMuY2hhcmFjdGVyKGkpLCJ4IiwgYXMuY2hhcmFjdGVyKGkpKQ0KICBlYXN0bmVzczwtIGFkZExheWVyKGVhc3RuZXNzLCBjdXJyX2Vhc3RuZXNzKQ0KICANCiAgY3Vycl9ub3J0aG5lc3M8LSBmb2NhbCh4ID0gbm9ydGhuZXNzJG5vcnRobmVzc19uYXRpdmUsIHcsIGZ1bj1tZWFuLCBuYS5ybT1UUlVFLCBwYWQ9VFJVRSkNCiAgbmFtZXMoY3Vycl9ub3J0aG5lc3MpPC0gcGFzdGUwKCJub3J0aG5lc3NfIixhcy5jaGFyYWN0ZXIoaSksIngiLCBhcy5jaGFyYWN0ZXIoaSkpDQogIG5vcnRobmVzczwtIGFkZExheWVyKG5vcnRobmVzcywgY3Vycl9ub3J0aG5lc3MpDQogIA0KICBjdXJyX3RwaTwtIFRQSShiYXRoeSwgZD1jKGksaSksIG5hLnJtPVRSVUUsIHBhZCA9IFRSVUUpDQogIG5hbWVzKGN1cnJfdHBpKTwtICBwYXN0ZTAoInRwaV8iLGFzLmNoYXJhY3RlcihpKSwieCIsIGFzLmNoYXJhY3RlcihpKSkNCiAgDQogIGN1cnJfQmF0aHlNZWFuPC0gZm9jYWwoeCA9IGJhdGh5LCB3LCBmdW49bWVhbiwgbmEucm09VFJVRSwgcGFkPVRSVUUpDQogIG5hbWVzKGN1cnJfQmF0aHlNZWFuKTwtICBwYXN0ZTAoIkJhdGh5TWVhbl8iLGFzLmNoYXJhY3RlcihpKSwieCIsIGFzLmNoYXJhY3RlcihpKSkNCiAgDQogIGN1cnJfQmF0aHlTRDwtIGZvY2FsKHggPSBiYXRoeSwgdywgZnVuPXNkLCBuYS5ybT1UUlVFLCBwYWQ9VFJVRSkNCiAgbmFtZXMoY3Vycl9CYXRoeVNEKTwtICBwYXN0ZTAoIkJhdGh5U0RfIixhcy5jaGFyYWN0ZXIoaSksIngiLCBhcy5jaGFyYWN0ZXIoaSkpDQoNCiAgaWYoaT09Myl7DQogICAgdHBpPC0gY3Vycl90cGkNCiAgICBCYXRoeU1lYW48LSBjdXJyX0JhdGh5TWVhbg0KICAgIEJhdGh5U0Q8LSBjdXJyX0JhdGh5U0QgDQogICAgfSBlbHNlew0KICAgICAgdHBpPC0gYWRkTGF5ZXIodHBpLCBjdXJyX3RwaSkNCiAgICAgIEJhdGh5TWVhbjwtIGFkZExheWVyKEJhdGh5TWVhbiwgY3Vycl9CYXRoeU1lYW4pDQogICAgICBCYXRoeVNEPC0gYWRkTGF5ZXIoQmF0aHlTRCwgY3Vycl9CYXRoeVNEKQ0KICAgIH0NCn0NCg0KYmF0aHlfZGVyaXY8LSBzdGFjayhCYXRoeU1lYW4sIEJhdGh5U0QsIHRwaSwgc2xvcGUsIGVhc3RuZXNzLCBub3J0aG5lc3MpDQpiYXRoeV9kZXJpdjwtIG1hc2soeCA9IGJhdGh5X2Rlcml2LCBFTF9zaHApICNUcmltIGJhY2sgZG93biBiL2MgdXNlZCBwYWQgYW5kIG5hLnJtDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKGJhdGh5X2Rlcml2LCAiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9iYXRoeV9kZXJpdi5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCnJtKHNsb3BlLCBhc3BlY3QsIGVhc3RuZXNzLCBub3J0aG5lc3MsIGN1cnJfc2xvcGUsIGN1cnJfZWFzdG5lc3MsIGN1cnJfbm9ydGhuZXNzLCBjdXJyX3RwaSwgY3Vycl9CYXRoeU1lYW4sIGN1cnJfQmF0aHlTRCwgdHBpLCBCYXRoeU1lYW4sIEJhdGh5U0QpDQpiYXRoeV9kZXJpdjwtIGJyaWNrKCJEYXRhL2Rlcml2ZWQvTXVsdGliZWFtL2JhdGh5X2Rlcml2LmdyZCIpDQpgYGANCg0KIyMgQmFja3NjYXR0ZXIgRGVyaXZhdGl2ZSBGZWF0dXJlcw0KQ2FsY3VsYXRlIGJhY2tzY2F0dGVyIGRlcml2YXRpdmVzIGF0IHZhcmlvdXMgc2NhbGVzIG9mIGFuYWx5c2lzLiBUaGlzIGluY2x1ZGVzIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYmF0aHltZXRyeSBhcyB3ZWxsIGFzIHRleHR1cmUgbWV0cmljIGRlcml2ZWQgZnJvbSBhIEdyYXkgTGV2ZWwgQ28tb2NjdXJlbmNlIE1hdHJpeCAoR0xDTSkuIA0KDQpUaGUgbW9zdCBjb21tb24gdGV4dHVyZSBtZXRyaWNzIGZyb20gcmVtb3RlIHNlbnNpbmcgd2VyZSBkZXJpdmVkIHVzaW5nIHRoZSBmb3JtdWxhcyBmcm9tIChIYWxsLUJleWVyIDIwMTcpLiBSb3RhdGlvbmFsbHkgSW52YXJpYW50L0RpcmVjdGlvbmFsbHkgSXNvdHJvcGljIEhhcmlsaWNrIFRleHR1cmUgTWV0cmljcyB3ZXJlIGRlcml2ZWQgYnkgY2FsY3VsYXRpbmcgYSBzeW1ldHJpY2FsIEdMQ00gd2l0aCAzMiBncmF5IGxldmVscy4NClRoZSB0ZXh0dXJlIG1ldHJpY3MgY2FuIGJlIGJyb2tlbiBkb3duIGludG8gdGhyZWUgZ3JvdXBzOiBUaGUgY29udHJhc3QgZ3JvdXAgKGNvbnRyYXN0LCBkaXNzaW1pbGFyaXR5LCBhbmQgaG9tb2dlbmVpdHkpLCB0aGUgb3JkZXJsaW5lc3MgZ3JvdXAgKEFTTSBhbmQgRW50cm9weSksIGFuZCB0aGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBncm91cCAobWVhbiwgdmFyaWFuY2UsIGFuZCBjb3JyZWxhdGlvbjtIYWxsLUJleWVyLCAyMDE3KS4gQXQgc21hbGxlciB3aW5kb3cgc2l6ZXMsIHRoZSBjb3JyZWxhdGlvbiB0ZXh0dXJlIGNhbiBzb21ldGltZXMgYmUgdW5kZWZpbmVkICgwLzApIGxlYWRpbmcgdG8gaG9sZXMgaW4gdGhlIHJlc3VsdGluZyByYXN0ZXIgc3VyZmFjZXMsIHNvIGNvcnJlbGF0aW9uIGF0IDN4MywgNXg1LCBhbmQgN3g3IHdlcmUgcmVtb3ZlZCBmcm9tIHRoZSBhbmFseXNpcyB0byBwcmV2ZW50IHRoaXMuDQoNCmBgYHtyIGNhY2hjZT1UUlVFLCByZXN1bHRzPSdoaWRlJ30NCmJhY2tzY2F0dGVyX3F1YW50PC0gcXVhbnRpemVfcmFzdGVyKGJhY2tzY2F0dGVyLCBuX2xldmVscyA9IDMyLG1ldGhvZCA9ICJlcXVhbCBwcm9iIikNCmZvciAoaSBpbiBteV93aW5kb3dfc2l6ZXMpIHsNCiAgcHJpbnQoaSkNCiAgdzwtIG1hdHJpeChkYXRhID0gMSwgbnJvdyA9IGksIG5jb2wgPSBpKQ0KICANCiAgY3Vycl9ic19tZWFuPC0gZm9jYWwoYmFja3NjYXR0ZXIsIHcsIGZ1bj1tZWFuLCBuYS5ybT1UUlVFLCBwYWQ9VFJVRSkNCiAgbmFtZXMoY3Vycl9ic19tZWFuKTwtICJCYWNrc2NhdHRlck1lYW4iDQogIGN1cnJfYnNfc2Q8LSBmb2NhbChiYWNrc2NhdHRlciwgdywgZnVuPXNkLCBuYS5ybT1UUlVFLCBwYWQ9VFJVRSkgDQogIG5hbWVzKGN1cnJfYnNfc2QpPC0gIkJhY2tzY2F0dGVyU0QiDQogIA0KICBjdXJyX0JTZGVyaXY8LSBzdGFjayhjdXJyX2JzX21lYW4sIA0KICAgICAgICAgICAgICAgICAgICAgICBjdXJyX2JzX3NkLA0KICAgICAgICAgICAgICAgICAgICAgICBnbGNtX3RleHR1cmVzKGJhY2tzY2F0dGVyX3F1YW50LCB3PSBjKGksaSksIG5fbGV2ZWxzPTMyLCAgc2hpZnQgPSBsaXN0KGMoMSwgMCksIGMoMSwgMSksIGMoMCwgMSksIGMoLTEsIDEpKSwgbWV0cmljcyA9IGMoImdsY21fY29udHJhc3QiLCAiZ2xjbV9kaXNzaW1pbGFyaXR5IiwgImdsY21faG9tb2dlbmVpdHkiLCAiZ2xjbV9BU00iLA0KICAgICJnbGNtX2VudHJvcHkiLCAiZ2xjbV9tZWFuIiwgImdsY21fdmFyaWFuY2UiLCAiZ2xjbV9jb3JyZWxhdGlvbiIpLCBxdWFudGl6YXRpb249ICJub25lIiwgbmFfb3B0ID0gImNlbnRlciIsIHBhZCA9IFRSVUUpKQ0KICANCiAgbmFtZXMoY3Vycl9CU2Rlcml2KTwtIHBhc3RlMChuYW1lcyhjdXJyX0JTZGVyaXYpLCAiXyIsIGFzLmNoYXJhY3RlcihpKSwgIngiLCBhcy5jaGFyYWN0ZXIoaSkpDQogIA0KICBpZihpPT0zKXsNCiAgICBiYWNrc2NhdHRlcl9kZXJpdjwtIGN1cnJfQlNkZXJpdn0gZWxzZXsNCiAgICAgIGJhY2tzY2F0dGVyX2Rlcml2PC0gc3RhY2soYmFja3NjYXR0ZXJfZGVyaXYsIGN1cnJfQlNkZXJpdikNCiAgICAgIHJtKGN1cnJfQlNkZXJpdikNCiAgICB9DQp9DQpiYWNrc2NhdHRlcl9kZXJpdjwtIG1hc2soYmFja3NjYXR0ZXJfZGVyaXYsIEVMX3NocCkNCmJhY2tzY2F0dGVyX2Rlcml2PC0gZHJvcExheWVyKGJhY2tzY2F0dGVyX2Rlcml2LCB3aGljaChuYW1lcyhiYWNrc2NhdHRlcl9kZXJpdik9PSJnbGNtX2NvcnJlbGF0aW9uXzN4MyIpKQ0KYmFja3NjYXR0ZXJfZGVyaXY8LSBkcm9wTGF5ZXIoYmFja3NjYXR0ZXJfZGVyaXYsIHdoaWNoKG5hbWVzKGJhY2tzY2F0dGVyX2Rlcml2KT09ImdsY21fY29ycmVsYXRpb25fNXg1IikpDQpiYWNrc2NhdHRlcl9kZXJpdjwtIGRyb3BMYXllcihiYWNrc2NhdHRlcl9kZXJpdiwgd2hpY2gobmFtZXMoYmFja3NjYXR0ZXJfZGVyaXYpPT0iZ2xjbV9jb3JyZWxhdGlvbl83eDciKSkgI3JlbW92ZSB0aGVzZSBiL2MgaGF2ZSBOQSdzIHRoYXQgY3JlYXRlIGhvbGVzIGR1ZSB0byB6ZXJvIGluIGRlbm9taW5hdG9yDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKGJhY2tzY2F0dGVyX2Rlcml2LCAiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9iYWNrc2NhdHRlcl9kZXJpdi5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCnJtKGJhY2tzY2F0dGVyX3F1YW50LCBjdXJyX2JzX21lYW4sIGN1cnJfYnNfc2QpDQpiYWNrc2NhdHRlcl9kZXJpdjwtIGJyaWNrKCJEYXRhL2Rlcml2ZWQvTXVsdGliZWFtL2JhY2tzY2F0dGVyX2Rlcml2LmdyZCIpDQpgYGANCg0KIyBHZW9yZWZlcmVuY2UgR3JvdW5kLVRydXRoIERhdGENCg0KIyMgVHJhbnNlY3RzDQoNCmBgYHtyIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KVDE8LSBzcFRyYW5zZm9ybShyZWFkT0dSKCJEYXRhL2Rlcml2ZWQvc2hhcGVmaWxlcy9FTFQxX1NoaXB0cmFjay5zaHAiKSwgQ1JTb2JqID0gbWFrZV9jcnMoIlVUTSAxNk4iKSkNClQzPC0gc3BUcmFuc2Zvcm0ocmVhZE9HUigiRGF0YS9kZXJpdmVkL3NoYXBlZmlsZXMvRUxUM19TaGlwdHJhY2suc2hwIiksIENSU29iaiA9IG1ha2VfY3JzKCJVVE0gMTZOIikpDQpUNTwtIHNwVHJhbnNmb3JtKHJlYWRPR1IoIkRhdGEvZGVyaXZlZC9zaGFwZWZpbGVzL0VMVDVfU2hpcHRyYWNrLnNocCIpLCBDUlNvYmogPSBtYWtlX2NycygiVVRNIDE2TiIpKQ0KVDY8LSBzcFRyYW5zZm9ybShyZWFkT0dSKCJEYXRhL2Rlcml2ZWQvc2hhcGVmaWxlcy9FTFQ2X1NoaXB0cmFjay5zaHAiKSwgQ1JTb2JqID0gbWFrZV9jcnMoIlVUTSAxNk4iKSkNCmBgYA0KDQpTb2xpZCBsaW5lcyByZXByZXNlbnQgdHJhbnNlY3RzIGZvciB0cmFpbmluZyBkYXRhLCBhbmQgZGFzaGVkIGxpbmUgcmVwcmVzZW50cyB0aGUgdHJhbnNlY3Qgd2hlcmUgb2JzZXJ2YXRpb25zIHdlcmUgd2l0aGhlbGQgaW4gb3JkZXIgdG8gdGVzdCB0aGUgYWNjdXJhY3kgb2Ygc3Vic3RyYXRlIHByZWRpY3Rpb25zDQoNCkZpZ3VyZSBTMS4zDQoNCmBgYHtyIGVjaG89RkFMU0V9DQp0cmFuc2VjdF9wbG90PC0gdG1fc2hhcGUoYmF0aHksIHJhc3Rlci5kb3duc2FtcGxlID0gRkFMU0UpKw0KICB0bV9yYXN0ZXIocGFsZXR0ZSA9IHBhbDEsIHN0eWxlPSAiY29udCIsIHRpdGxlID0gIiIsIGxlZ2VuZC5zaG93ID0gRkFMU0UpKw0KICB0bV9zaGFwZShyYmluZChUMyxUNSxUNikpKw0KICB0bV9saW5lcygpKw0KICB0bV9zaGFwZShUMSkrDQogIHRtX2xpbmVzKGx0eT0iZGFzaGVkIikrDQogIHRtX2dyYXRpY3VsZXMobGluZXM9RkFMU0UpDQp0cmFuc2VjdF9wbG90DQpgYGANCg0KIyMgUmVhZCBpbiBWaWRlbyBIYWJpdGF0IE9ic2VydmF0aW9ucw0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KVDFfaGFiPC0gcmVhZF94bHN4KCJEYXRhL3Jhdy9DQkFTUy9oYWJpdGF0L1QxX0Q0X3F1aWNraGFiMi54bHN4IikNClQxX2hhYjwtIFQxX2hhYlt3aGljaChUMV9oYWIkQ29tbWVudHM9PSJCb3R0b20iKTpucm93KFQxX2hhYiksXQ0KDQpUM19oYWI8LSByZWFkX3hsc3goIkRhdGEvcmF3L0NCQVNTL2hhYml0YXQvVDNfRDFfcXVpY2toYWIyLnhsc3giKQ0KVDNfaGFiPC0gVDNfaGFiW3doaWNoKFQzX2hhYiRDb21tZW50cz09IkJvdHRvbSIpOm5yb3coVDNfaGFiKSxdDQoNClQ1X2hhYjwtIHJlYWRfeGxzeCgiRGF0YS9yYXcvQ0JBU1MvaGFiaXRhdC9UNV9EMTRfcXVpY2toYWIyLnhsc3giKQ0KVDVfaGFiPC0gVDVfaGFiW3doaWNoKFQ1X2hhYiRDb21tZW50cz09IkJvdHRvbSIpOm5yb3coVDVfaGFiKSxdDQoNClQ2X2hhYjwtIHJlYWRfeGxzeCgiRGF0YS9yYXcvQ0JBU1MvaGFiaXRhdC9UNl9EMTNfcXVpY2toYWIyLnhsc3giKQ0KVDZfaGFiPC0gVDZfaGFiW3doaWNoKFQ2X2hhYiRDb21tZW50cz09IkJvdHRvbSIpOm5yb3coVDZfaGFiKSxdDQoNCmhhYjwtIGJpbmRfcm93cyhUMV9oYWIsIFQzX2hhYiwgVDVfaGFiLCBUNl9oYWIpDQpybShUMV9oYWIsVDNfaGFiLFQ1X2hhYiwgVDZfaGFiKQ0KaGFiPC0gaGFiICU+JSBtdXRhdGUoU3Vic3RyYXRlPSBpZmVsc2UodGVzdCA9IGlzLm5hKEdlbyksIHllcyA9ICJTYW5kIiwgbm8gPSAiUm9jayIpKQ0KaGFiJFN1YnN0cmF0ZVtoYWIkR2VvPT0iTm9fVmlzIl08LSAiTm9fVmlzIg0KaGFiPC0gaGFiICU+JSBtdXRhdGUoUmVsaWVmPSBpZmVsc2UodGVzdCA9IGlzLm5hKEdlbyksIHllcyA9ICJOb25lIiwgbm8gPSBHZW8pKQ0KaGFiPC0gaGFiICU+JSBzZWxlY3QoVHJhbnNlY3QsIFZpZCwgU2VjLCBTdWJzdHJhdGUsIFJlbGllZikNCmBgYA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KaGFiDQpgYGANCg0KIyMgUmVhZCBpbiBUaW1lc3RhbXBzIGZvciBDYW1lcmEgSW1hZ2VzDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpmaWxlX2xpc3Q8LSBsaXN0LmZpbGVzKCJEYXRhL3Jhdy9DQkFTUy9jYW1lcmEiLCBmdWxsLm5hbWVzID0gVFJVRSkNCmZvciAoaSBpbiAxOmxlbmd0aChmaWxlX2xpc3QpKSB7DQogIGN1cnJfY2FtPC0gcmVhZF90c3YoZmlsZV9saXN0W2ldKQ0KICByZWM8LSBjdXJyX2NhbSAlPiUgDQogICAgbXV0YXRlKHJlY29yZGluZz1zdHJfZXh0cmFjdChzdHJpbmcgPSBmaWxlX3BhdGgsIHBhdHRlcm4gPSAiXFxkezh9LVxcZHs2fSIpKSAlPiUNCiAgICBncm91cF9ieShyZWNvcmRpbmcpICU+JQ0KICAgIHN1bW1hcmlzZShDT1VOVD1uKCkpICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICAgIGNob3Nlbl9yZWNvcmRpbmc8LSByZWMkcmVjb3JkaW5nW3doaWNoLm1heChyZWMkQ09VTlQpXQ0KICAgIGN1cnJfY2FtPC0gY3Vycl9jYW0gJT4lIA0KICAgICAgZmlsdGVyKHN0cl9kZXRlY3Qoc3RyaW5nID0gZmlsZV9wYXRoLCBwYXR0ZXJuID0gY2hvc2VuX3JlY29yZGluZykpICU+JQ0KICAgICAgZmlsdGVyKHN0cl9kZXRlY3Qoc3RyaW5nID0gZmlsZV9wYXRoLCBwYXR0ZXJuID0gIlxcLmpwZyQiKSkNCiAgICBjdXJyX2NhbTwtIGN1cnJfY2FtICU+JSBtdXRhdGUoRnJhbWVfSUQ9IGFzLm51bWVyaWMoc3RyX2V4dHJhY3Qoc3RyX2V4dHJhY3Qoc3RyaW5nID0gZmlsZV9wYXRoLCBwYXR0ZXJuID0gIkdpZ0VHcmFiRXgtXFxkezd9XFwuanBnJCIpLCBwYXR0ZXJuID0gIlxcZHs3fSIpKSkNCiAgICBjdXJyX2NhbTwtIGN1cnJfY2FtICU+JSBtdXRhdGUoVHJhbnNlY3Q9c3RyX2V4dHJhY3Qoc3RyaW5nID0gZmlsZV9saXN0W2ldLCBwYXR0ZXJuID0gIlRcXGQiKSkNCiAgICBwcmludChjaGVja19qdW1ibGVkKGN1cnJfY2FtKT09RkFMU0UpDQogICAgaWYoaT09MSl7Y2FtPC0gY3Vycl9jYW19IGVsc2V7DQogICAgICBjYW08LSBjYW0gJT4lIGJpbmRfcm93cyhjdXJyX2NhbSl9DQp9ICNDbGVhbiBkYXRhIGJ5IHJlbW92aW5nIHNob3J0IHJlY29yZGluZ3Mgbm90IGluY2x1ZGVkIGluIG1vc2FpY2VkIHZpZGVvIGFuZCBmaWx0ZXJpbmcgZm9yIG9ubHkgaGVuIGltYWdlcyB3ZXJlIHdyaXR0ZW4NCmNhbTwtIGNhbSAlPiUgZ3JvdXBfYnkoVHJhbnNlY3QpICU+JSBtdXRhdGUoRnJhbWVfbnVtPSBGcmFtZV9JRC1taW4oRnJhbWVfSUQpKSAlPiUgdW5ncm91cCgpICNTb21ldGltZXMgZnJhbWUgZmlsZW5hbWVzIHdlcmUgbm90IHJlc2V0IGJhY2sgdG8gemVybywgc28gc3VidHJhY3QgbWluaW11bSBmcmFtZSBvZiBhIHRyYW5zZWN0IHNvIHRoYXQgeW91IGNhbiBhc3NvY2lhdGUgdmlkZW8gZnJhbWUgIyB3aXRoIGFuIGltYWdlIGZyYW1lICMNCmNhbTwtIGNhbSAlPiUgc2VsZWN0KHRpbWVzdGFtcCwgdV9zZWNvbmQsZmlsZV9wYXRoLCBUcmFuc2VjdCwgRnJhbWVfSUQsIEZyYW1lX251bSkNCndyaXRlX2NzdihjYW0sIHBhdGggPSAiRGF0YS9kZXJpdmVkL0NCQVNTL2NhbWVyYS5jc3YiKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCmNhbTwtIHJlYWRfY3N2KCJEYXRhL2Rlcml2ZWQvQ0JBU1MvY2FtZXJhLmNzdiIpDQpgYGANCmBgYHtyIGVjaG89RkFMU0V9DQpjYW0NCmBgYA0KDQojIyBSZWFkIGluIFBvc2l0aW9uIGFuZCBBbmNpbGxhcnkgRGF0YSBTdHJlYW1zDQoNCmBgYHtyIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRX0NCkh5cGFjazwtIHJlYWRfaHlwYWNrKGxpc3QuZmlsZXMoIkRhdGEvcmF3L0h5cGFjay8iLCBwYXR0ZXJuPSJcXC5SQVckIiwgZnVsbC5uYW1lcyA9IFRSVUUpKQ0KY2FibGVfb3V0PC0gcmVhZF90c3YoIkRhdGEvcmF3L3BheW91dC9wYXlvdXRfRmViJk9jdDIwMTYudHN2IiwgY29sX3R5cGVzID0gbGlzdCguZGVmYXVsdD1jb2xfZG91YmxlKCksIHRpbWVzdGFtcD1jb2xfZGF0ZXRpbWUoKSkpDQpMYXRfTG9uZzwtIGdldF9IeXBhY2tQb3MoSHlwYWNrKQ0KbmFtZXMoTGF0X0xvbmcpWzI6M108LSBjKCJTaGlwTG9uIiwgIlNoaXBMYXQiKQ0KU2hpcF9TcGVlZDwtIGdldF9IeXBhY2tTcGVlZChIeXBhY2spDQoNClQxX29uZUh6PC0gbWFrZV8xaHooYWx0X3BhdGggPSAiRGF0YS9yYXcvQ0JBU1Mvc2Vuc29ycy9UMV9ENC9hbHRpbWV0ZXJfcmVhZGluZ3MudHN2IiwgY29tcGFzc19wYXRoID0gIkRhdGEvcmF3L0NCQVNTL3NlbnNvcnMvVDFfRDQvY29tcGFzc19yZWFkaW5ncy50c3YiLCBjdGRfcGF0aCA9ICJEYXRhL3Jhdy9DQkFTUy9zZW5zb3JzL1QxX0Q0L2N0ZF9yZWRvX3NhbC50c3YiLCBMYXRfTG9uZyA9IExhdF9Mb25nLCBTaGlwX1NwZWVkID0gU2hpcF9TcGVlZCwgY2FibGVfb3V0ID0gY2FibGVfb3V0KQ0KDQpUM19vbmVIejwtIG1ha2VfMWh6KGFsdF9wYXRoID0gIkRhdGEvcmF3L0NCQVNTL3NlbnNvcnMvVDNfRDEvYWx0aW1ldGVyX3JlYWRpbmdzLnRzdiIsIGNvbXBhc3NfcGF0aCA9ICJEYXRhL3Jhdy9DQkFTUy9zZW5zb3JzL1QzX0QxL2NvbXBhc3NfcmVhZGluZ3MudHN2IiwgY3RkX3BhdGggPSAiRGF0YS9yYXcvQ0JBU1Mvc2Vuc29ycy9UM19EMS9jdGRfcmVkb19zYWwudHN2IiwgTGF0X0xvbmcgPSBMYXRfTG9uZywgU2hpcF9TcGVlZCA9IFNoaXBfU3BlZWQsIGNhYmxlX291dCA9IGNhYmxlX291dCkNCg0KVDVfb25lSHo8LSBtYWtlXzFoeihhbHRfcGF0aCA9ICJEYXRhL3Jhdy9DQkFTUy9zZW5zb3JzL1Q1X0QxNC9hbHRpbWV0ZXJfcmVhZGluZ3MudHN2IiwgY29tcGFzc19wYXRoID0gIkRhdGEvcmF3L0NCQVNTL3NlbnNvcnMvVDVfRDE0L2NvbXBhc3NfcmVhZGluZ3MudHN2IiwgY3RkX3BhdGggPSAiRGF0YS9yYXcvQ0JBU1Mvc2Vuc29ycy9UNV9EMTQvY3RkX3JlZG9fc2FsLnRzdiIsIExhdF9Mb25nID0gTGF0X0xvbmcsIFNoaXBfU3BlZWQgPSBTaGlwX1NwZWVkLCBjYWJsZV9vdXQgPSBjYWJsZV9vdXQpDQoNClQ2X29uZUh6PC0gbWFrZV8xaHooYWx0X3BhdGggPSAiRGF0YS9yYXcvQ0JBU1Mvc2Vuc29ycy9UNl9EMTMvYWx0aW1ldGVyX3JlYWRpbmdzLnRzdiIsIGNvbXBhc3NfcGF0aCA9ICJEYXRhL3Jhdy9DQkFTUy9zZW5zb3JzL1Q2X0QxMy9jb21wYXNzX3JlYWRpbmdzLnRzdiIsIGN0ZF9wYXRoID0gIkRhdGEvcmF3L0NCQVNTL3NlbnNvcnMvVDZfRDEzL2N0ZF9yZWRvX3NhbC50c3YiLCBMYXRfTG9uZyA9IExhdF9Mb25nLCBTaGlwX1NwZWVkID0gU2hpcF9TcGVlZCwgY2FibGVfb3V0ID0gY2FibGVfb3V0KQ0KDQpvbmVfaHo8LSBiaW5kX3Jvd3MobXV0YXRlKFQxX29uZUh6LCBUcmFuc2VjdD0iVDEiKSwNCiAgICAgICAgICAgICAgICBtdXRhdGUoVDNfb25lSHosIFRyYW5zZWN0PSJUMyIpLA0KICAgICAgICAgICAgICAgIG11dGF0ZShUNV9vbmVIeiwgVHJhbnNlY3Q9IlQ1IiksDQogICAgICAgICAgICAgICAgbXV0YXRlKFQ2X29uZUh6LCBUcmFuc2VjdD0iVDYiKSkNCm9uZV9oeg0KYGBgDQoNCiMjIEVzdGltYXRlIEMtQkFTUyBQb3NpdGlvbg0KDQpDQkFTUyBQb3NpdGlvbiBpcyBlc3RpbWF0ZWQgdXNpbmcgUHl0aGFnb3JlYW4gdGhlb3JlbSB1c2luZyB0aGUgY2FibGUgb3V0ICh0aGUgaHlwb3RlbnVzZSkgYW5kIENCQVNTIGRlcHRoICh0aGUgaGVpZ2h0IG9mIHRoZSB0cmlhbmdsZSkuIFRoZSBjYWJsZSBvdXQgaXMgemVyb2VkIHdoZW4gdGhlIHRoZSBDQkFTUyBoaXRzIHRoZSB3YXRlciBzbyB0byBnZXQgdHJ1ZSBjYWJsZSBvdXQgZnJvbSB0aGUgdHJhd2wgYmxvY2ssIGFuZCB0aGUgdHJ1ZSBoZWlnaHQgb2YgdGhpcyB0cmlhbmdsZSwgdGhlIGhlaWdodCBvZiB0aGUgdHJhd2wgYmxvY2sgYWJvdmUgdGhlIHdhdGVyIChcfjYuMSBtKSBpcyBhZGRlZCB0byB0aGUgcmVjb3JkZWQgY2FibGUgb3V0IGFuZCBDQkFTUyBEZXB0aC4gVGhlIGxheWJhY2sgYmVoaW5kIHRoZSBibG9jayBpcyB0aGVuIGNhbGN1bGF0ZWQgdXNpbmcgcHl0aGFnb3JlYW4gdGhlb3J1bS4gVGhlIHkgb2Zmc2V0IChmb3JlL2FmdCBkaXN0YW5jZTsgXH4yNC40IG0pIGJldHdlZW4gdGhlIHRyYXdsIGJsb2NrIGFuZCB0aGUgR1BTIGFudGVubmEgaXMgdGhlbiBhZGRlZCB0byB0aGlzIGxheWJhY2sgdG8gZ2V0IHRoZSBsYXliYWNrIG9mIHRoZSBDQkFTUyBzeXN0ZW0gYmVoaW5kIHRoZSBsb2dnZWQgR1BTIHNoaXAgcG9zaXRpb24uIENCQVNTIGlzIGFzc3VtZWQgdG8gZm9sbG93IHRoZSBzaGlwIHRyYWNrIGJ1dCB3aXRoIGEgdGltZSBkZWxheS4gVGhlIGxheWJhY2sgZGlzdGFuY2UgaXMgY29udmVydGVkIHRvIGEgdGltZSBkZWxheSBieSBkaXZpZGluZyB0aGUgbGF5YmFjayBkaXN0YW5jZSBieSB0aGUgYXZlcmFnZSBzaGlwIHNwZWVkIG92ZXIgdGhlIHBhc3QgbWludXRlLiBUbyBnZXQgdGhlIENCQVNTIHBvc2l0aW9uIGZvciBhIGdpdmVuIHRpbWUsIHRoZSB0aGUgc2hpcCBwb3NpdGlvbiBmcm9tICJ4IiBudW1iZXIgb2Ygc2Vjb25kcyBhZ28gKHRoZSB0aW1lIGRlbGF5KSBpcyBncmFiYmVkIGZyb20gdGhlIHRhYmxlIGFuZCBhc3NpZ25lZCBhcyB0aGUgY3VycmVudCBDQkFTUyBwb3NpdGlvbi4gRm9yIG1vcmUgaW5mb3JtYXRpb24gc2VlIHRoZSBkb2N1bWVudGF0aW9uIGFuZCBzb3VyY2UgY29kZSBmb3IgbXl0b29sczo6Y2FsY1xfbGF5YmFjayAoYXJndW1lbnRzIHVzZWQgR1BTXF9Tb3VyY2U9Ik5vdGhzdGFyIiwgemVyb2VkPSJ3YXRlciIsIGNhdFxfZmFjdD0xKS4NCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCm9uZV9oejwtIG9uZV9oeiAlPiUgbXV0YXRlKExheWJhY2tfbT0gY2FsY19sYXliYWNrKHBheW91dCA9IHBheW91dCwgZGVwdGggPSBkZXB0aCwgR1BTX1NvdXJjZSA9ICJOb3J0aHN0YXIiLCB6ZXJvZWQgPSAid2F0ZXIiLCBjYXRfZmFjdCA9IDEpKQ0Kb25lX2h6PC0gb25lX2h6ICU+JSBtdXRhdGUoTGF5YmFja19zZWM9IHJvdW5kKExheWJhY2tfbS9TaGlwX1NwZWVkX21wc18xbWluQXZnKSkNCm9uZV9oejwtIG9uZV9oeiAlPiUgbXV0YXRlKFRpbWVUb01hdGNoPXRpbWVzdGFtcC1kc2Vjb25kcyhMYXliYWNrX3NlYykpDQp0ZW1wX29uZV9oejwtIG9uZV9oeiAlPiUgc2VsZWN0KHRpbWVzdGFtcCwgVHJhbnNlY3QsIFNoaXBMb24sIFNoaXBMYXQpDQpuYW1lcyh0ZW1wX29uZV9oeik8LSBjKCJUaW1lVG9NYXRjaCIsICJUcmFuc2VjdCIsICJDQkFTU0xvbiIsICJDQkFTU0xhdCIpDQpvbmVfaHo8LSBvbmVfaHogJT4lIGxlZnRfam9pbih0ZW1wX29uZV9oeiwgYnk9YygiVGltZVRvTWF0Y2giLCJUcmFuc2VjdCIpKSAlPiUgc2VsZWN0KC1UaW1lVG9NYXRjaCkNCnJtKHRlbXBfb25lX2h6KQ0Kb25lX2h6DQp3cml0ZV9jc3Yob25lX2h6LCAiRGF0YS9kZXJpdmVkL0NCQVNTL29uZWh6LmNzdiIpDQpgYGANCg0KIyMgR2VvcmVmZXJlbmNlIFZpZGVvIEhhYml0YXQgT2JzZXJ2YXRpb25zDQoNCkNhbGN1bGF0ZSBGcmFtZSBudW1iZXIuIFZpZGVvcyBhcmUgMSBtaW51dGUgbG9uZyBhdCAxMiBmcmFtZXMgcGVyIHNlY29uZC4NCg0KYGBge3J9DQpoYWI8LSBoYWIgJT4lIG11dGF0ZShGcmFtZV9udW09IChWaWQgKiAxMiAqIDYwKSArIFNlYyoxMikNCmBgYA0KDQpHZXQgdGltZXN0YW1wIGZvciBlYWNoIGZyYW1lIHRvIG5lYXJlc3Qgc2Vjb25kDQoNCmBgYHtyfQ0KaGFiPC0gaGFiICU+JSBsZWZ0X2pvaW4oc2VsZWN0KGNhbSwgdGltZXN0YW1wLCB1X3NlY29uZCwgRnJhbWVfbnVtLCBUcmFuc2VjdCksIGJ5PSBjKCJUcmFuc2VjdCIsICJGcmFtZV9udW0iKSkNCmhhYjwtIGhhYiAlPiUgbXV0YXRlKHRpbWVzdGFtcD1yb3VuZF9kYXRlKHRpbWVzdGFtcCArIGRtaWNyb3NlY29uZHModV9zZWNvbmQpLCB1bml0ID0gInNlY29uZCIpKSAlPiUgc2VsZWN0KC11X3NlY29uZCkNCmBgYA0KDQpMaW5rIHRvIHBvc2l0aW9uIGJ5IHRpbWVzdGFtcA0KDQpgYGB7ciBpbmNsdWRlPVRSVUV9DQpoYWI8LSBoYWIgJT4lIGxlZnRfam9pbihzZWxlY3Qob25lX2h6LCB0aW1lc3RhbXAsIFRyYW5zZWN0LCBDQkFTU0xhdCwgQ0JBU1NMb24pLCBieT1jKCJ0aW1lc3RhbXAiLCAiVHJhbnNlY3QiKSkNCmBgYA0KYGBge3IgZWNobz1UUlVFfQ0KaGFiDQpgYGANCmBgYHtyfQ0Kd3JpdGVfY3N2KGhhYiwgcGF0aCA9ICJEYXRhL2Rlcml2ZWQvQ0JBU1MvaGFiLmNzdiIpDQpgYGANCg0KIyMgUHJlcGFyZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBzZXQNCg0KT25seSBpbmNsdWRlIGhhYml0YXRzIHRoYXQgd2VyZSB0aGUgc2FtZSBhcyB0aGVpciBwcmV2aW91cyBhbmQgc3Vic2VxdWVudCBvYnNlcnZhdGlvbnMgdG8gcmVtb3ZlIHRyYW5zaXRpb25hbC9taXhlZCBhcmVhcyB0byBjcmVhdGUgc2V0IHVzZWQgZm9yIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uLg0KDQpgYGB7cn0NClRWX3NldDwtIGhhYiAlPiUgZ3JvdXBfYnkoVHJhbnNlY3QpICU+JSBjb25zdF9oYWIoIlN1YnN0cmF0ZSIpICU+JSB1bmdyb3VwKCkNCnByaW50KHVuaXF1ZShUVl9zZXQkU3Vic3RyYXRlKSkNClRWX3NldDwtIFRWX3NldCAlPiUgZmlsdGVyKFN1YnN0cmF0ZSE9Ik5vX1ZpcyIpICNSZW1vdmUgYmFkIHZpc2liaWxpdHkNClRWX3NldCRTdWJzdHJhdGU8LSBmYWN0b3IoVFZfc2V0JFN1YnN0cmF0ZSwgbGV2ZWxzID0gYygiUm9jayIsICJTYW5kIikpDQpUVl9zZXQ8LSBUVl9zZXQgJT4lIG11dGF0ZShTZXQ9aWZlbHNlKHRlc3QgPSBUcmFuc2VjdD09IlQxIiwgeWVzID0gIlZhbGlkYXRpb24iLCBubyA9ICJUcmFpbmluZyIpKSAjUmVzZXJ2ZSBUMSBmb3IgdmFsaWRhdGlvbiBvZiByZXN1bHRzLiBVc2Ugb3RoZXIgdHJhbnNlY3RzIGZvciB0cmFpbmluZyBkYXRhDQpgYGANCg0KIyMjIENyZWF0ZSBTcGF0aWFsIE9iamVjdA0KDQpgYGB7cn0NClRWX3NldDwtIFRWX3NldCAlPiUgZmlsdGVyKCFpcy5uYShDQkFTU0xhdCkpICNSZW1vdmUgd2hlcmUgdGhlcmUgaXMgbm8gcG9zaXRpb25pbmcgZGF0YQ0KVFZfc2V0PC0gc3BUcmFuc2Zvcm0oU3BhdGlhbFBvaW50c0RhdGFGcmFtZShjb29yZHMgPSBjYmluZChUVl9zZXQkQ0JBU1NMb24sIFRWX3NldCRDQkFTU0xhdCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBhcy5kYXRhLmZyYW1lKHNlbGVjdChUVl9zZXQsIC1jKENCQVNTTG9uLCBDQkFTU0xhdCkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2o0c3RyaW5nID0gbWFrZV9jcnMoIldHUzg0IikpLCBDUlNvYmogPSBtYWtlX2NycygiVVRNIDE2TiIpKQ0KVFZfc2V0PC0gVFZfc2V0W0VMX3NocCxdICNSZW1vdmUgcG9pbnRzIG91dHNpZGUgYmV5b25kIGJvdW5kcyBvZiB0aGUgc3R1ZHkgYXJlYQ0KYGBgDQoNClRoZSBncm91bmQtdHJ1dGggZGF0YXNldCBjb25zaXN0ZWQgb2YgYHIgbnJvdyhoYWIpYCBvYnNlcnZhdGlvbnMsIHdoZXJlIGVhY2ggb2JzZXJ2YXRpb24gd2FzIHRoZSBzdWJzdHJhdGUgY2xhc3NpZmljYXRpb24gZGV0ZXJtaW5lZCBmcm9tIHRoZSB2aWRlbyBhdCAxNSBzZWNvbmQgaW50ZXJ2YWxzLiBUaGlzIGNvbnNpc3RlZCBvZiBgciBzdW0oaGFiJFN1YnN0cmF0ZT09IlJvY2siKWAgb2JzZXJ2YXRpb25zIG9mIHJvY2ssIGByIHN1bShoYWIkU3Vic3RyYXRlPT0iU2FuZCIpYCBvYnNlcnZhdGlvbnMgb2Ygc2FuZCwgYW5kIGByIHN1bShoYWIkU3Vic3RyYXRlPT0iTm9fVmlzIilgIG9ic2VydmF0aW9ucyB3aGVyZSBzdWJzdHJhdGUgd2FzIG5vdCBkaXNjZXJuaWJsZS4gQWZ0ZXIgY2Vuc29yaW5nIHN1YnN0cmF0ZSBkZXRlcm1pbmF0aW9ucyB0aGF0IGRpZmZlcmVkIGZyb20gdGhlaXIgcHJldmlvdXMgYW5kIHN1YnNlcXVlbnQgb2JzZXJ2YXRpb25zLCB0aG9zZSB3aGVyZSBzdWJzdHJhdGUgd2FzIG5vdCB2aXNpYmxlLCBhbmQgb2JzZXJ2YXRpb25zIGJleW9uZCB0aGUgYm91bmRzIG9mIHRoZSBtdWx0aWJlYW0gc3VydmV5IG9yIGRpZCBub3QgaGF2ZSBhc3NvY2lhdGVkIHBvc2l0aW9uaW5nIGRhdGEsIHRoZXJlIHdlcmUgYHIgVFZfc2V0ICU+JSBmaWx0ZXIoU3Vic3RyYXRlPT0iUm9jayIpICU+JSBucm93KClgIG9ic2VydmF0aW9ucyBvZiByb2NrIGFuZCBgciBUVl9zZXQgJT4lIGZpbHRlcihTdWJzdHJhdGU9PSJTYW5kIikgJT4lIG5yb3coKWAgb2JzZXJ2YXRpb25zIG9mIHNhbmQuIFRoZXNlIGRhdGEgd2VyZSB0aGVuIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gc2V0cy4gVGhlIHRyYWluaW5nIGRhdGEgc2V0IGNvbnNpc3RlZCBvZiBgciBUVl9zZXQgJT4lIGZpbHRlcihTZXQ9PSJUcmFpbmluZyIgJiBTdWJzdHJhdGU9PSJSb2NrIikgJT4lIG5yb3coKWBvYnNlcnZhdGlvbnMgb2Ygcm9jayBhbmQgYHIgVFZfc2V0ICU+JSBmaWx0ZXIoU2V0PT0iVHJhaW5pbmciICYgU3Vic3RyYXRlPT0iU2FuZCIpICU+JSBucm93KClgIG9ic2VydmF0aW9ucyBvZiBzYW5kLiBUaGUgdmFsaWRhdGlvbiB0cmFuc2VjdCBjb25zaXN0ZWQgb2YgYHIgVFZfc2V0ICU+JSBmaWx0ZXIoU2V0PT0iVmFsaWRhdGlvbiIgJiBTdWJzdHJhdGU9PSJSb2NrIikgJT4lIG5yb3coKWAgb2JzZXJ2YXRpb25zIG9mIHJvY2sgYW5kIGByIFRWX3NldCAlPiUgZmlsdGVyKFNldD09IlZhbGlkYXRpb24iICYgU3Vic3RyYXRlPT0iU2FuZCIpICU+JSBucm93KClgIG9ic2VydmF0aW9ucyBvZiBzYW5kLg0KDQpGaWd1cmUgUzEuNA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KdHJhaW5pbmdfcGxvdDwtIHRtX3NoYXBlKEVMX3NocCkgKw0KICB0bV9wb2x5Z29ucyh0aXRsZSA9ICJUcmFpbmluZyIsIGNvbCA9ICJjYWRldGJsdWUxIiwgbGVnZW5kLnNob3cgPSBGQUxTRSkrDQp0bV9zaGFwZShmaWx0ZXIoVFZfc2V0LCBTZXQ9PSJUcmFpbmluZyIpKSsNCiAgdG1fZG90cyhjb2w9IlN1YnN0cmF0ZSIsIHBhbGV0dGU9YygicmVkIiwgIiNGRkVCQkUiKSwgbGVnZW5kLnNob3cgPSBUUlVFKSsNCiAgdG1fbGF5b3V0KGxlZ2VuZC5iZy5jb2xvciA9ICJncmF5IikrDQogICAgdG1fY3JlZGl0cyh0ZXh0ID0gImEpIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgc2l6ZSA9IDEpKw0KICAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkrDQogICAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiVHJhaW5pbmciLCBtYWluLnRpdGxlLnBvc2l0aW9uID0gLjQzKSANCg0KdmFsaWRhdGlvbl9wbG90PC0gdG1fc2hhcGUoRUxfc2hwKSArDQogIHRtX3BvbHlnb25zKHRpdGxlID0gIlZhbGlkYXRpb24iLCBjb2wgPSAiY2FkZXRibHVlMSIsIGxlZ2VuZC5zaG93ID0gRkFMU0UpKw0KICB0bV9zaGFwZShmaWx0ZXIoVFZfc2V0LCBTZXQ9PSJWYWxpZGF0aW9uIikpKw0KICB0bV9kb3RzKGNvbD0iU3Vic3RyYXRlIiwgcGFsZXR0ZT1jKCJyZWQiLCAiI0ZGRUJCRSIpLCBsZWdlbmQuc2hvdyA9IEZBTFNFKSsNCiAgdG1fY3JlZGl0cyh0ZXh0ID0gImIpIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgc2l6ZSA9IDEpKw0KICAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkrDQogICAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiVmFsaWRhdGlvbiIsIG1haW4udGl0bGUucG9zaXRpb24gPSAuMzkpIA0KDQoNCmd0cnV0aF9wbG90PC0gdG1hcF9hcnJhbmdlKHRyYWluaW5nX3Bsb3QsIHZhbGlkYXRpb25fcGxvdCkNCmd0cnV0aF9wbG90DQpgYGANCg0KYGBge3J9DQpoYWJfb25laHpfam9pbmVkPC0gaGFiICU+JSBsZWZ0X2pvaW4ob25lX2h6KQ0KYGBgDQogDQpUcmFuc2VjdHMgdG90YWwgdG8gYHIgaGFiX29uZWh6X2pvaW5lZCAlPiUgZ3JvdXBfYnkoVHJhbnNlY3QpICU+JSBzdW1tYXJpemUoc3RfdGltZT1taW4odGltZXN0YW1wLCBuYS5ybT1UUlVFKSwgZW5kX3RpbWU9bWF4KHRpbWVzdGFtcCwgbmEucm09VFJVRSksIGF2Z19zcGVlZF9tcHM9IG1lYW4oU2hpcF9TcGVlZF9tcHMsIG5hLnJtPVRSVUUpLCAuZ3JvdXBzPSJkcm9wIikgJT4lIG11dGF0ZSh0cmFuc2VjdF9kdXJhdGlvbj1hcy5udW1lcmljKGRpZmZ0aW1lKGVuZF90aW1lLCBzdF90aW1lLCB1bml0cyA9ICJzZWNzIikpKSAlPiUgbXV0YXRlKHRyYW5zZWN0X2xlbnRoX2ttPSAodHJhbnNlY3RfZHVyYXRpb24qYXZnX3NwZWVkX21wcykvMTAwMCkgJT4lIHB1bGwodHJhbnNlY3RfbGVudGhfa20pICU+JSBzdW0oKSAlPiUgcm91bmQoKWAga20gaW4gbGVuZ3RoDQoNCg0KIyBQcmVkaWN0aXZlIEhhYml0YXQgTWFwcGluZw0KDQpgYGB7cn0NCnByZWRpY3Rvcl92YXJzPC0gc3RhY2soYmF0aHksIGJhdGh5X2Rlcml2LCBiYWNrc2NhdHRlciwgYmFja3NjYXR0ZXJfZGVyaXYpDQpuX3ByZWRpY3RvcnM8LW5sYXllcnMocHJlZGljdG9yX3ZhcnMpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKHByZWRpY3Rvcl92YXJzLCAiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9wcmVkaWN0b3JfdmFycy5ncmQiLG92ZXJ3cml0ZT1UUlVFKQ0KcHJlZGljdG9yX3ZhcnM8LSBicmljaygiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9wcmVkaWN0b3JfdmFycy5ncmQiKQ0KYGBgDQoNCiMjIFN1cGVydmlzZWQgQ2xhc3NpZmljYXRpb24gKFJhbmRvbSBGb3Jlc3QpDQoNCiMjIyBWYXJpYWJsZSBTZWxlY3Rpb24NClRoZXJlIGFyZSBgciBuX3ByZWRpY3RvcnNgIHByZWRpY3RvciB2YXJpYWJsZXMgaW4gdG90YWwuIFRvIGRlY2lkZSBvbiB3aGljaCB2YXJpYWJsZXMgdG8gcmV0YWluIGluIHRoZSBmaW5hbCBtb2RlbCwgd2UgZmlyc3QgdXNlIHRoZSBCb3J1dGEgZmVhdHVyZSBzZWxlY3Rpb24gYWxnb3JpdGhtLCB3aGljaCB0aHJvdWdoIGFuIGl0ZXJhdGl2ZSBwcm9jZWR1cmUgZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBpbXBvcnRhbmNlIG9mIGEgcHJlZGljdG9yIGlzIHNpZ25pZmljYW50bHkgZ3JlYXRlciAocF9ib25mZXJyb25pIDwgMC4wNSkgdGhhbiB6ZXJvLiBBbnkgcHJlZGljdG9ycyB0aGF0IGRvIG5vdCBoYXZlIGFuIGltcG9ydGFuY2Ugc2lnbmlmaWNhbnRseSBncmVhdGVyIHRoYW4gemVybyBhcmUgcmVtb3ZlZCBmcm9tIHRoZSBtb2RlbC4gV2UgdGhlbiByZWR1Y2UgY28tbGluZWFyaXR5IGFtb25nIHByZWRpY3RvcnMgYW5kIHJlbW92ZSByZWR1bmRhbnQgdmFyaWFibGVzIGJ5IGVsaW1pbmF0aW5nIGFueSBwcmVkaWN0b3IgaWYgaXQgaXMgaGlnaGx5IGNvcnJlbGF0ZWQgKHxyfCA+IDAuOCkgd2l0aCBhbnkgdmFyaWFibGUgdGhhdCBoYWQgYSBoaWdoZXIgdmFyaWFibGUgaW1wb3J0YW5jZSBzY29yZS4NCg0KYGBge3IgfQ0KZGF0YV9kZjwtIHJhc3Rlcjo6ZXh0cmFjdCh4ID0gcHJlZGljdG9yX3ZhcnMsIHkgPSBmaWx0ZXIoVFZfc2V0LCBTZXQ9PSJUcmFpbmluZyIpLCBtZXRob2Q9InNpbXBsZSIsIGZhY3RvcnM9VFJVRSwgc3A9VFJVRSlAZGF0YQ0KZGF0YV9kZiRTdWJzdHJhdGU8LSBmYWN0b3IoZGF0YV9kZiRTdWJzdHJhdGUsIGxldmVscz1jKCJSb2NrIiwgIlNhbmQiKSkNCnNhbXBsZV9mcmFjdGlvbjwtIGRhdGFfZGYgJT4lIGNvdW50KFN1YnN0cmF0ZSkgJT4lIG11dGF0ZShwcm9wPW4vc3VtKG4pKSAlPiUgcHVsbChwcm9wKSAlPiUgbWluKCkgI0Rvd25zYW1wbGUgdG8gbWlub3JpdHkgY2xhc3MNCmRhdGFfZGY8LSBkYXRhX2RmICU+JSBzZWxlY3QoLWMoVHJhbnNlY3QsIFZpZCwgU2VjLCB0aW1lc3RhbXAsIFJlbGllZiwgRnJhbWVfbnVtLCBTZXQpKSAlPiUgbmEub21pdCgpIA0KDQpzZXQuc2VlZCg0KQ0KQm9ydXRhX29iajwtIEJvcnV0YShTdWJzdHJhdGUgfiAuICwgZGF0YT1kYXRhX2RmLCBwVmFsdWUgPSAuMDUsIG1jQWRqID0gVFJVRSwgbWF4UnVucyA9IDUwMDAsIGdldEltcD0gZ2V0SW1wUmZSYXcsIHNhbXBsZS5mcmFjdGlvbj1jKHNhbXBsZV9mcmFjdGlvbixzYW1wbGVfZnJhY3Rpb24pKQ0KQm9ydXRhX29iajwtIFRlbnRhdGl2ZVJvdWdoRml4KHggPSBCb3J1dGFfb2JqKQ0KUHJlZGljdG9yX0RlY2lzaW9uczwtZGF0YS5mcmFtZShEZWNpc2lvbiA9IEJvcnV0YV9vYmpbWyJmaW5hbERlY2lzaW9uIl1dKQ0KUHJlZGljdG9yX0RlY2lzaW9uczwtIFByZWRpY3Rvcl9EZWNpc2lvbnMgJT4lIG11dGF0ZShWYXJpYWJsZT0gcm93bmFtZXMoUHJlZGljdG9yX0RlY2lzaW9ucykpICU+JSBzZWxlY3QoVmFyaWFibGUsIERlY2lzaW9uKQ0KQm9ydXRhX0ltcDwtIGFzLmRhdGEuZnJhbWUoQm9ydXRhX29ialtbIkltcEhpc3RvcnkiXV0pICU+JSBzdW1tYXJpemUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucz1tZWFuKSkgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJWYXJpYWJsZSIsIHZhbHVlc190bz0gIkltcG9ydGFuY2UiKQ0Kc3VwX3JldGFpbmVkX3ByZWRpY3RvcnNfZGY8LSBQcmVkaWN0b3JfRGVjaXNpb25zICU+JSBmaWx0ZXIoRGVjaXNpb249PSJDb25maXJtZWQiKSAlPiUgbGVmdF9qb2luKEJvcnV0YV9JbXAsIGJ5PSJWYXJpYWJsZSIpICU+JSBhcnJhbmdlKGRlc2MoSW1wb3J0YW5jZSkpDQoNCnN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzPC0gcHJlZGljdG9yX3ZhcnNbW3doaWNoKG5hbWVzKHByZWRpY3Rvcl92YXJzKSAlaW4lIHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzX2RmJFZhcmlhYmxlKV1dDQpjb3JyX3ZhbHM8LSBsYXllclN0YXRzKHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzLCAncGVhcnNvbicsIG5hLnJtPVRSVUUpDQpjb3JyX3ZhbHM8LSBhcy5kYXRhLmZyYW1lKGNvcnJfdmFscyRgcGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudGApICU+JSBtdXRhdGUoVjE9cm93Lm5hbWVzKGNvcnJfdmFscyRgcGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudGApKQ0KY29ycl92YWxzPC0gY29ycl92YWxzICU+JSBwaXZvdF9sb25nZXIoY29scz0gLVYxLCBuYW1lc190byA9ICJWMiIsIHZhbHVlc190bz0iY29ycmVsYXRpb24iKQ0KY29ycl92YWxzPC0gY29ycl92YWxzICU+JSBmaWx0ZXIoVjEhPVYyKQ0Kc3VwX3JldGFpbmVkX3ByZWRpY3RvcnNfZGY8LSBzdXBfcmV0YWluZWRfcHJlZGljdG9yc19kZiAlPiUgbXV0YXRlKEtlZXA9VFJVRSkNCmZvciAoaSBpbiBucm93KHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzX2RmKToyKSB7DQogIGN1cnJfdmFyPC0gc3VwX3JldGFpbmVkX3ByZWRpY3RvcnNfZGYkVmFyaWFibGVbaV0NCiAgbW9yZV9pbXBvcnRhbnRfcHJlZGljdG9yczwtIHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzX2RmWzE6KGktMSksICJWYXJpYWJsZSJdDQogIG1heF9jb3JyPC0gY29ycl92YWxzICU+JSBmaWx0ZXIoKFYxPT1jdXJyX3ZhcikgJiAoVjIgJWluJSBtb3JlX2ltcG9ydGFudF9wcmVkaWN0b3JzKSkgJT4lIHB1bGwoY29ycmVsYXRpb24pICU+JSBhYnMoKSAlPiUgbWF4KCkNCiAgaWYobWF4X2NvcnIgPiAwLjgpe3N1cF9yZXRhaW5lZF9wcmVkaWN0b3JzX2RmJEtlZXBbaV08LSBGQUxTRX0NCiAgfQ0Kc3VwX3JldGFpbmVkX3ByZWRpY3RvcnNfZGY8LSBzdXBfcmV0YWluZWRfcHJlZGljdG9yc19kZiAlPiUgZmlsdGVyKEtlZXA9PVRSVUUpDQpzdXBfcmV0YWluZWRfcHJlZGljdG9yczwtIHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzW1t3aGljaChuYW1lcyhzdXBfcmV0YWluZWRfcHJlZGljdG9ycykgJWluJSBzdXBfcmV0YWluZWRfcHJlZGljdG9yc19kZiRWYXJpYWJsZSldXQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQp3cml0ZVJhc3RlcihzdXBfcmV0YWluZWRfcHJlZGljdG9ycywgIkRhdGEvZGVyaXZlZC9NdWx0aWJlYW0vcmV0YWluZWRfcHJlZGljdG9ycy5ncmQiLG92ZXJ3cml0ZT1UUlVFKQ0Kc3VwX3JldGFpbmVkX3ByZWRpY3RvcnM8LSBicmljaygiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9yZXRhaW5lZF9wcmVkaWN0b3JzLmdyZCIpDQpgYGANCg0KIyMjIFBsb3Qgb2YgUmV0YWluZWQgVmFyaWFibGVzDQoNCkZpZ3VyZSBTMS41DQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFfQ0Kc3VwX3JldGFpbmVkX3Zhcl9wbG90X2xpc3Q8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aCA9IG5sYXllcnMoc3VwX3JldGFpbmVkX3ByZWRpY3RvcnMpKQ0Kc3VwX3JldGFpbmVkX3ByZWRpY3RvcnNfc3RhcnM8LSBzdF9hc19zdGFycyhzdXBfcmV0YWluZWRfcHJlZGljdG9ycywgaWdub3JlX2ZpbGU9VFJVRSkNCmZvciAoaSBpbiAxOmxlbmd0aChzdXBfcmV0YWluZWRfdmFyX3Bsb3RfbGlzdCkpIHsNCiAgY3Vycl92YXI8LSBuYW1lcyhzdXBfcmV0YWluZWRfcHJlZGljdG9ycylbaV0NCiAgaWYgKGdyZXBsKHBhdHRlcm4gPSAiKF5ub3J0aG5lc3MpfCheZWFzdG5lc3MpfChjb3JyZWxhdGlvbikiLCBjdXJyX3ZhcikpIHsNCiAgICBjdXJyX3BhbDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsICJncmF5IiwgInJlZCIpKSgxMDApDQogICAgYnJlYWtzPC0gYygtMSwwLDEpDQogICAgbWlkcG9pbnQ8LSAwDQogIH0gZWxzZSBpZihncmVwbChwYXR0ZXJuID0gIihedHBpKSIsIGN1cnJfdmFyKSkgew0KICAgIGN1cnJfcGFsPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgImdyYXkiLCAicmVkIikpKDEwMCkNCiAgICBicmVha3M8LSBjKGZsb29yKG1pblZhbHVlKHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzW1tpXV0pKSwgMCwgY2VpbGluZyhtYXhWYWx1ZShzdXBfcmV0YWluZWRfcHJlZGljdG9yc1tbaV1dKSkpDQogICAgbWlkcG9pbnQ8LSAwDQogICAgfSBlbHNlew0KICAgIGN1cnJfcGFsPC0gcGFsMQ0KICAgIGJyZWFrczwtIE5VTEwNCiAgICBtaWRwb2ludDwtIE5VTEwNCiAgfQ0KICBjdXJyX3N0YXJzPC0gc3VwX3JldGFpbmVkX3ByZWRpY3RvcnNfc3RhcnNbLCwsaV0NCiAgc3VwX3JldGFpbmVkX3Zhcl9wbG90X2xpc3RbW2ldXTwtIHRtX3NoYXBlKGN1cnJfc3RhcnMsIHJhc3Rlci5kb3duc2FtcGxlID0gRkFMU0UpICsNCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjdXJyX3BhbCwgc3R5bGU9ICJjb250IiwgdGl0bGUgPSAiIiwgYnJlYWtzID0gYnJlYWtzLCBtaWRwb2ludCA9IG1pZHBvaW50KSsNCiAgICB0bV9sYXlvdXQobWFpbi50aXRsZSA9IGN1cnJfdmFyLCANCiAgICBtYWluLnRpdGxlLnBvc2l0aW9uID0gImNlbnRlciIsDQogICAgbWFpbi50aXRsZS5zaXplPTAuNzUpDQp9DQpzdXBfcmV0YWluZWRfdmFyX3Bsb3Q8LSB0bWFwX2FycmFuZ2Uoc3VwX3JldGFpbmVkX3Zhcl9wbG90X2xpc3QsIG5jb2w9NCkNCnN1cF9yZXRhaW5lZF92YXJfcGxvdA0KYGBgDQoNCiMjIyBGaXQgYW5kIHJ1biBtb2RlbA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiNGaXQgYW5kIHByZWRpY3QgdXNpbmcgcmFuZG9tIGZvcmVzdA0Kbl9yZXRhaW5lZF9wcmVkPC0gbmxheWVycyhzdXBfcmV0YWluZWRfcHJlZGljdG9ycykNCmJlZ2luQ2x1c3RlcihkZXRlY3RDb3Jlcyhsb2dpY2FsID0gRkFMU0UpLTEpDQpzZXQuc2VlZCg1KQ0Kc3VwX21vZGVsPC0gc3VwZXJDbGFzcyhpbWcgPXN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzLCB0cmFpbkRhdGEgPSBmaWx0ZXIoVFZfc2V0LCBTZXQ9PSJUcmFpbmluZyIpLCByZXNwb25zZUNvbCA9ICJTdWJzdHJhdGUiLCBtb2RlbCA9ICJyYW5nZXIiLCBtb2RlID0gImNsYXNzaWZpY2F0aW9uIiwgdmFsRGF0YSA9IGZpbHRlcihUVl9zZXQsIFNldD09IlZhbGlkYXRpb24iKSwgcHJlZGljdCA9IEZBTFNFLCBwcmVkVHlwZSA9ICJwcm9iIiwga2ZvbGQ9NSwgbWluRGlzdCA9IDAsIG1ldHJpYz0gIkthcHBhIiwgbnVtLnRyZWVzPTMwMDAsIHR1bmVHcmlkID0gZGF0YS5mcmFtZShtdHJ5PTI6KG5fcmV0YWluZWRfcHJlZC0xKSwgbWluLm5vZGUuc2l6ZT0xLCBzcGxpdHJ1bGU9ImdpbmkiKSwga2VlcC5pbmJhZyA9IFRSVUUsIHNhbXBsZS5mcmFjdGlvbj0gYyhzYW1wbGVfZnJhY3Rpb24sc2FtcGxlX2ZyYWN0aW9uKSwgc2NhbGUucGVybXV0YXRpb24uaW1wb3J0YW5jZSA9IEZBTFNFLCBpbXBvcnRhbmNlPSAicGVybXV0YXRpb24iLCBsb2NhbC5pbXBvcnRhbmNlID0gVFJVRSwgdmVyYm9zZSA9IEZBTFNFKQ0KZW5kQ2x1c3RlcigpDQoNCnN1cF9oYWI8LSBSU3Rvb2xib3g6OjpwcmVkaWN0LnN1cGVyQ2xhc3Mob2JqZWN0ID0gc3VwX21vZGVsLCBpbWcgPSBzdXBfcmV0YWluZWRfcHJlZGljdG9ycywgcHJlZFR5cGU9InJhdyIpDQpuYW1lcyhzdXBfaGFiKTwtICJTdWJzdHJhdGUiDQpzdXBfaGFiPC0gYXMuZmFjdG9yKHN1cF9oYWIpDQpsZXZlbHMoc3VwX2hhYilbWzFdXSRTdWJzdHJhdGU8LSBjKCJSb2NrIiwgIlNhbmQiKQ0KDQojVW5jZXJ0YWludHkNCnN1cF9oYWJfcHJvYjwtIFJTdG9vbGJveDo6OnByZWRpY3Quc3VwZXJDbGFzcyhzdXBfbW9kZWwsIHN1cF9yZXRhaW5lZF9wcmVkaWN0b3JzLCBwcmVkVHlwZSA9ICJwcm9iIikNCm5hbWVzKHN1cF9oYWJfcHJvYik8LSBjKCJSb2NrIiwgIlNhbmQiKQ0Kc3VwX2hhYl9lbnRyb3B5PC0gbXl0b29sczo6Y2FsY19lbnRyb3B5KHN1cF9oYWJfcHJvYikNCm5hbWVzKHN1cF9oYWJfZW50cm9weSk8LSAiU2hhbm5vbl9FbnRyb3B5Ig0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQp3cml0ZVJhc3RlcihzdXBfaGFiLCBmaWxlbmFtZSA9ICJQcm9kdWN0cy9NYXBwaW5nL0VsYm93X1N1YnN0cmF0ZV9TdXBlcnZpc2VkLnRpZiIsIG92ZXJ3cml0ZT1UUlVFKQ0Kd3JpdGVSYXN0ZXIoc3VwX2hhYiwgZmlsZW5hbWUgPSAiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19TdWJzdHJhdGVfU3VwZXJ2aXNlZC5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCnN1cF9oYWI8LSByYXN0ZXIoIlByb2R1Y3RzL01hcHBpbmcvRWxib3dfU3Vic3RyYXRlX1N1cGVydmlzZWQuZ3JkIikNCg0Kd3JpdGVSYXN0ZXIoc3VwX2hhYl9wcm9iLCBmaWxlbmFtZSA9ICJQcm9kdWN0cy9NYXBwaW5nL0VsYm93X1N1YnN0cmF0ZV9TdXBlcnZpc2VkX1Byb2IuZ3JkIiwgb3ZlcndyaXRlPVRSVUUpDQpzdXBfaGFiX3Byb2I8LSBicmljaygiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19TdWJzdHJhdGVfU3VwZXJ2aXNlZF9Qcm9iLmdyZCIpDQoNCndyaXRlUmFzdGVyKHN1cF9oYWJfZW50cm9weSwgZmlsZW5hbWUgPSAiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19TdWJzdHJhdGVfU3VwZXJ2aXNlZF9FbnRyb3B5LnRpZiIsIG92ZXJ3cml0ZT1UUlVFKQ0Kd3JpdGVSYXN0ZXIoc3VwX2hhYl9lbnRyb3B5LCBmaWxlbmFtZSA9ICJQcm9kdWN0cy9NYXBwaW5nL0VsYm93X1N1YnN0cmF0ZV9TdXBlcnZpc2VkX0VudHJvcHkuZ3JkIiwgb3ZlcndyaXRlPVRSVUUpDQpzdXBfaGFiX2VudHJvcHk8LSByYXN0ZXIoIlByb2R1Y3RzL01hcHBpbmcvRWxib3dfU3Vic3RyYXRlX1N1cGVydmlzZWRfRW50cm9weS5ncmQiKQ0KYGBgDQoNCiMjIyBNb2RlbCBEaWFnbm9zdGljcw0KTW9kZWwgZGlhZ25vc3RpY3Mgc2hvd2luZyB0aGF0IHRoZSBvdmVyYWxsIE91dC1PZi1CYWcgZXJyb3IgcmF0ZSBwbGF0ZWF1cywgbWVhbmluZyB0aGF0IGEgc3VmZmljaWVudCBudW1iZXIgb2YgZGVjaXNpb24gdHJlZXMgaGF2ZSBiZWVuIHVzZWQgaW4gdGhlIG1vZGVsDQoNCkZpZ3VyZSBTMS42DQoNCmBgYHtyIGVjaG89RkFMU0V9DQpPT0JfZGY8LSBleHRyYWN0X3Jhbmdlcl9vb2Ioc3VwX21vZGVsKQ0KZXJyX3ZzX250cmVlPC0gZ2dwbG90KGRhdGEgPSBPT0JfZGYsIGFlcyh4PW50cmVlcywgeT1PdmVyYWxsKSkrDQogIGdlb21fbGluZSgpK3lsYWIoIkVycm9yIikrZ2d0aXRsZSgiT3V0LU9mLUJhZyBFcnJvciB2cyBOdW1iZXIgb2YgVHJlZXMiKSt4bGFiKCJOdW1iZXIgb2YgVHJlZXMiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PS41KSkNCmBgYA0KYGBge3IgZWNobz1GQUxTRSwgLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD02fQ0KZXJyX3ZzX250cmVlDQpgYGANCg0KDQojIyMgUmVzdWx0aW5nIE1hcHMNCg0KVGhlIGxlZnQgcGFuZWwgc2hvd3MgdGhlIHByZWRpY3RlZCBzdWJzdHJhdGUgYWNyb3NzIHRoZSBzdHVkeSBhcmVhIGJhc2VkIG9uIG91ciBzdXBlcnZpc2VkIGNsYXNzaWZpY2F0aW9uIG1vZGVsLiBJbiBhZGRpdGlvbiB0byB0aGUgcHJlZGljdGlvbnMsIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsIGFsbG93cyBmb3IgYW4gZW50cm9weSBtYXAgd2hpY2ggcGxvdHMgdGhlIHVuY2VydGFpbnR5IGFzc29jaWF0ZWQgd2l0aCB0aGUgY2xhc3NpZmljYXRpb24gZm9yIGVhY2ggcGl4ZWwgYnkgcXVhbnRpZnlpbmcgdGhlIGxldmVsIG9mIGRpc2FncmVlbWVudCBhbW9uZyB0aGUgYWxsIHRoZSBkaWZmZXJlbnQgZGVjaXNpb24gdHJlZXMgaW4gb3VyIHJhbmRvbSBmb3Jlc3QgbW9kZWwuDQoNCkZpZ3VyZSBTMS43DQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpzdXBfc3ViX3Bsb3Q8LSB0bV9zaGFwZShzdF9hc19zdGFycyhzdXBfaGFiLCBpZ25vcmVfZmlsZT1UUlVFKSwgcmFzdGVyLmRvd25zYW1wbGUgPSBGQUxTRSkrDQogIHRtX3Jhc3Rlcihjb2w9IlN1YnN0cmF0ZSIsIHBhbGV0dGUgPSBjKCJyZWQiICwgIiNGRkVCQkUiKSwgdGl0bGUgPSAiU3VwZXJ2aXNlZCBTdWJzdHJhdGUiLCBzdHlsZSA9ICJjYXQiKSsNCiAgdG1fY3JlZGl0cyh0ZXh0ID0gImEpIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJ0b3AiKSwgc2l6ZSA9IDEpKw0KICAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkNCg0KZW50X3BhbDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsICJ5ZWxsb3ciLCAicmVkIikpDQpzdXBfZW50X3Bsb3Q8LSB0bV9zaGFwZShzdXBfaGFiX2VudHJvcHksIHJhc3Rlci5kb3duc2FtcGxlID0gRkFMU0UpKw0KICB0bV9yYXN0ZXIocGFsZXR0ZT1lbnRfcGFsKDEwMCksIHN0eWxlID0gImNvbnQiLCB0aXRsZSA9ICJTaGFubm9uIEVudHJvcHkiKSsNCiAgICB0bV9jcmVkaXRzKHRleHQgPSAiYikiLCBwb3NpdGlvbiA9IGMoInJpZ2h0IiwgInRvcCIpLCBzaXplID0gMSkrDQogICB0bV9ncmF0aWN1bGVzKGxpbmVzPUZBTFNFKSNFbnRyb3B5IE1hcCB0byBsb29rIGF0IHVuY2VydGFpbnR5DQoNCnRtYXBfYXJyYW5nZShzdXBfc3ViX3Bsb3QsIHN1cF9lbnRfcGxvdCkNCmBgYA0KDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpzdXBfYXJlYTwtIGFzLmRhdGEuZnJhbWUoZnJlcShzdXBfaGFiLCB1c2VOQT0ibm8iLCBtZXJnZT1UUlVFKSkNCnN1cF9hcmVhJFN1YnN0cmF0ZT1jKCJSb2NrIiwgIlNhbmQiKQ0Kc3VwX2FyZWE8LSBzdXBfYXJlYSAlPiUgbXV0YXRlKEFyZWFfbTI9IGNvdW50KihyZXMoYWxsX29mKHN1cF9oYWIpKVsxXSkqKHJlcyhhbGxfb2Yoc3VwX2hhYikpWzJdKSkNCnN1cF9hcmVhPC0gc3VwX2FyZWEgJT4lIG11dGF0ZShBcmVhX2ttMj1BcmVhX20yLzFlNikNCnN1cF9hcmVhICU+JSBzZWxlY3QoU3Vic3RyYXRlLCBBcmVhX2ttMikgJT4lIG11dGF0ZShBcmVhX2ttMj1yb3VuZChBcmVhX2ttMiwyKSkNCmBgYA0KDQoNCiMjIyBWYXJpYWJsZSBJbXBvcnRhbmNlDQoNCkZpZ3VyZSBTMS44DQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQ0KdmFyX2ltcDwtIGFzLmRhdGEuZnJhbWUoc3VwX21vZGVsJG1vZGVsJGZpbmFsTW9kZWwkdmFyaWFibGUuaW1wb3J0YW5jZSkNCm5hbWVzKHZhcl9pbXApPC0gIk1EQSINCnZhcl9pbXAkUHJlZGljdG9yPC0gcm93bmFtZXModmFyX2ltcCkNCnZhcl9pbXA8LSB2YXJfaW1wICU+JSBhcnJhbmdlKGRlc2MoTURBKSkNCg0KdmFyX2ltcF9wbG90PC0gZ2dwbG90KGRhdGEgPSB2YXJfaW1wLCBtYXBwaW5nID0gYWVzKHg9cmVvcmRlcihQcmVkaWN0b3IsIE1EQSksIHk9IE1EQSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSsNCiAgeGxhYigiVmFyaWFibGUiKSArDQogIHlsYWIoIk1lYW4gRGVjcmVhc2UgaW4gQWNjdXJhY3kiKSsNCiAgZ2d0aXRsZSgiVmFyaWFibGUgSW1wb3J0YW5jZSIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PS41KSkrDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIGV4cGFuZD1jKDAsMCkpKw0KICBjb29yZF9mbGlwKCkNCnZhcl9pbXBfcGxvdA0KYGBgDQoNCg0KIyMjIEFjdXJhY2N5IEFzc2Vzc21lbnQNCg0KQWNjdXJhY3kgYXNzZXNzbWVudCB3YXMgcGVyZm9ybWVkIGJ5IGNvbXBhcmluZyB0aGUgcHJlZGljdGlvbnMgdG8gdGhlIG9ic2VydmVkIGdyb3VuZC10cnV0aCBoYWJpdGF0IG9ic2VydmF0aW9ucyBvbiB0aGUgdHJhbnNlY3QgdGhhdCB3YXMgcmVzZXJ2ZWQgZm9yIHZhbGlkYXRpb24gYW5kIG5vdCB1c2VkIHRvIGZpdCB0aGUgbW9kZWwuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpwcmludChzdXBfbW9kZWwkdmFsaWRhdGlvbiRwZXJmb3JtYW5jZSR0YWJsZSkNCmFncmVlbWVudF9sZXZlbHM8LSBkYXRhLmZyYW1lKG1pbl9rPWMoLUluZiwgMCwgMC4yLCAuNCwgLjYsIC44KSwgbWF4X2s9YygwLC4yLC40LC42LC44LDEpLCBBZ3JlZW1lbnQ9YygicG9vciIsICJzbGlnaHQiLCAiZmFpciIsICJtb2RlcmF0ZSIsICJzdWJzdGFudGlhbCIsICJhbG1vc3QgcGVyZmVjdCIpKQ0KYGBgDQpPdmVyYWxsIEFjY3VyYWN5ID0gYHIgcm91bmQoc3VwX21vZGVsJHZhbGlkYXRpb24kcGVyZm9ybWFuY2Ukb3ZlcmFsbFtbIkFjY3VyYWN5Il1dICoxMDAsMSlgJSANCg0KS2FwcGEgPSBgciByb3VuZChzdXBfbW9kZWwkdmFsaWRhdGlvbiRwZXJmb3JtYW5jZSRvdmVyYWxsW1siS2FwcGEiXV0sMilgDQoNCkEgS2FwcGEgc2NvcmUgb2YgZ3JlYXRlciB0aGFuIGByIGFncmVlbWVudF9sZXZlbHMkbWluX2tbbWF4KHdoaWNoKHN1cF9tb2RlbCR2YWxpZGF0aW9uJHBlcmZvcm1hbmNlJG92ZXJhbGxbWyJLYXBwYSJdXT5hZ3JlZW1lbnRfbGV2ZWxzJG1pbl9rKSldYCBpbmRpY2F0ZXMgYHIgYWdyZWVtZW50X2xldmVscyRBZ3JlZW1lbnRbbWF4KHdoaWNoKHN1cF9tb2RlbCR2YWxpZGF0aW9uJHBlcmZvcm1hbmNlJG92ZXJhbGxbWyJLYXBwYSJdXT5hZ3JlZW1lbnRfbGV2ZWxzJG1pbl9rKSldYCBhZ3JlZW1lbnQgYmV0d2VlbiBwcmVkaWN0aW9ucyBhbmQgb2JzZXJ2YXRpb25zLg0KDQojIyBVbnN1cGVydmllZCBDbGFzc2lmaWNhdGlvbg0KDQojIyMgUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMNCg0KRmlyc3QsIHdlIGNvbmR1Y3QgYSBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIG9uIFotc2NvcmUgbm9ybWFsaXplZCB2YXJpYWJsZXMgKHNwY2E9VFJVRSkgdG8gcmVtb3ZlIGNvLWxpbmVhcml0eS4NCg0KYGBge3IgfQ0KUENBX2xheWVyczwtIHJhc3RlclBDQShwcmVkaWN0b3JfdmFycywgc3BjYSA9IFRSVUUpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKFBDQV9sYXllcnMkbWFwLCAiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9QQ0FfbWFwLmdyZCIsIG92ZXJ3cml0ZT1UUlVFKQ0KUENBX2xheWVycyRtYXA8LSBicmljaygiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9QQ0FfbWFwLmdyZCIpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NClBDQV9jb21wX2RmPC0gdGliYmxlKFBDPTE6bmxheWVycyhQQ0FfbGF5ZXJzJG1hcCksIE9ic2VydmVkPVBDQV9sYXllcnMkbW9kZWwkc2Rldl4yLCBTaW11bGF0ZWQ9YnJva2VuX3N0aWNrKG5sYXllcnMoUENBX2xheWVycyRtYXApKSkgI2RhdGFmcmFtZSBjb21wYXJpbmcgZWlnZW52YWx1ZXMgdG8gdGhvc2Ugb2YgcmFuZG9tIGRhdGENCm5fcGNzPC0gKHdoaWNoKFBDQV9jb21wX2RmJFNpbXVsYXRlZD5QQ0FfY29tcF9kZiRPYnNlcnZlZCAmIFBDQV9jb21wX2RmJFBDPjEpWzFdKS0xDQpuYW1lcyhuX3Bjcyk8LSBOVUxMDQpgYGANCg0KQXMgZWFjaCBzZXF1ZW50aWFsIHByaW5jaXBhbCBjb21wb25lbnQgZXhwbGFpbnMgYSBzbWFsbGVyIHBlcmNlbnRhZ2Ugb2YgdGhlIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSwgbmV4dCB3ZSBtdXN0IGRldGVybWluZSBob3cgbWFueSBwcmluY2lwYWwgY29tcG9uZW50cyB0byByZXRhaW4uIFdlIHdpbGwgcmV0YWluIG9ubHkgYSBzdWJzZXQgdGhhdCBhcmUgZGV0ZXJtaW5lZCB0byBiZSBpbXBvcnRhbnQuIFRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdXNlZCB0byBpZGVudGlmeSB0aGUgYXBwcm9wcmlhdGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHJldGFpbiAoSmFja3NvbiwgMTk5MykuIFdlIGNvbXBhcmVkIHRoZSB2YXJpYXRpb24gZXhwbGFpbmVkIGJ5IGVhY2ggUHJpbmNpcGFsIENvbXBvbmVudCBhbmQgdGhlIHZhcmlhdGlvbiBleHBlY3RlZCB0byBiZSBleHBsYWluZWQgYnkgc2ltdWxhdGVkIGRhdGEgaW4gd2hpY2ggdGhlIHZhcmlhbmNlIHdhcyBkaXN0cmlidXRlZCByYW5kb21seSBhbW9uZyB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgYXMgbW9kZWxlZCBieSBhIGJyb2tlbi1zdGljayBkaXN0cmlidXRpb24gKEZyb250aWVyLCAxOTc2KS4gVGhlIGZpcnN0IGByIG5fcGNzYCBleHBsYWluZWQgbW9yZSB0aGFuIGNvdWxkIGJlIGV4cGVjdGVkIGJ5IHJhbmRvbSBjaGFuY2Ugc28gdGhlIGZpcnN0IGByIG5fcGNzYCBwcmluY2lwYWwgY29tcG9uZW50cyB3ZXJlIHJldGFpbmVkLiBUaGVzZSBmaXJzdCBgciBuX3Bjc2AgcHJpbmNpcGFsIGNvbXBvbmVudHMgZXhwbGFpbiBgciByb3VuZCgoMTAwKmN1bXN1bShQQ0FfY29tcF9kZiRPYnNlcnZlZCkvc3VtKFBDQV9jb21wX2RmJE9ic2VydmVkKSlbbl9wY3NdKWAlIG9mIHRoZSB2YXJpYXRpb24gaW4gdGhlIG9yaWdpbmFsIGRhdGEuDQoNCg0KRmlndXJlIFMxLjkNCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NH0NClBDQV92YXJfcGxvdDwtIGdncGxvdChkYXRhID0gZ2F0aGVyKFBDQV9jb21wX2RmLCBrZXkgPSAiRGF0YSIsIHZhbHVlID0gIlZhcmlhbmNlIiwgT2JzZXJ2ZWQsIFNpbXVsYXRlZCksIG1hcHBpbmcgPSBhZXMoeD1QQywgeSA9IFZhcmlhbmNlLCBjb2xvcj1EYXRhKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV9saW5lKCkrDQogIHhsYWIoIlByaW5jaXBhbCBDb21wb25lbnQiKSsNCiAgZ2d0aXRsZSgiVmFyaWFuY2UgRXhwbGFpbmVkIHZzIFByaW5jaXBhbCBDb21wb25lbnQgTnVtYmVyIikrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSBtYXgoUENBX2NvbXBfZGYkUEMpLCBieT0xMCksbGltaXRzID0gYygxLE5BKSkNClBDQV92YXJfcGxvdA0KYGBgDQoNClBsb3Qgb2YgdGhlIHJldGFpbmVkIHByaW5jaXBhbCBjb21wb25lbnRzDQpGaWd1cmUgUzEuMTANCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9DQpyZXRfUENBX3Bsb3Q8LSB0bV9zaGFwZShzdF9hc19zdGFycyhQQ0FfbGF5ZXJzJG1hcFtbMTpuX3Bjc11dLCBpZ25vcmVfZmlsZT1UUlVFKSwgcmFzdGVyLmRvd25zYW1wbGUgPSBGQUxTRSkrDQogIHRtX3Jhc3RlcihwYWxldHRlID0gcGFsMSwgc3R5bGU9ICJjb250IiwgdGl0bGUgPSAiIiwgbWlkcG9pbnQgPSBOQSkgKw0KICB0bV9mYWNldHMoZnJlZS5zY2FsZXMgPSBUUlVFLCBuY29sID0gMykNCnJldF9QQ0FfcGxvdA0KYGBgDQoNCiMjIyBSdW4gY2x1c3RlcmluZyBvbiB0aGUgcmV0YWluZWQgcHJpbmNpcGFsIGNvbXBvbmVudHMNCg0KYGBge3IgfQ0KYmVnaW5DbHVzdGVyKGRldGVjdENvcmVzKGxvZ2ljYWwgPSBGQUxTRSktMSkNCnNldC5zZWVkKDUpDQp1bnN1cDwtIHVuc3VwZXJDbGFzcyhpbWcgPVBDQV9sYXllcnMkbWFwW1sxOm5fcGNzXV0sIG5DbGFzc2VzID0gNCwgbnN0YXJ0cz0xMDAsIG5JdGVyID0gMTAwMCwgY2x1c3Rlck1hcCA9IEZBTFNFLCBhbGdvcml0aG0gPSAiTWFjUXVlZW4iKQ0KZW5kQ2x1c3RlcigpDQpuYW1lcyh1bnN1cCRtYXApPC0gIkFjb3VzdGljX0NsdXN0ZXIiDQp1bnN1cCRtYXA8LSBhcy5mYWN0b3IodW5zdXAkbWFwKQ0KbGV2ZWxzKHVuc3VwJG1hcClbWzFdXSRDbHVzdGVyPC0gYygiMSIsICIyIiwgIjMiLCAiNCIpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKHVuc3VwJG1hcCwgZmlsZW5hbWUgPSAiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19BY291c3RpY0NsdXN0ZXJzX1Vuc3VwZXJ2aXNlZC50aWYiLCBvdmVyd3JpdGU9VFJVRSkNCndyaXRlUmFzdGVyKHVuc3VwJG1hcCwgZmlsZW5hbWUgPSAiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19BY291c3RpY0NsdXN0ZXJzX1Vuc3VwZXJ2aXNlZC5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCnVuc3VwJG1hcDwtIHJhc3RlcigiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19BY291c3RpY0NsdXN0ZXJzX1Vuc3VwZXJ2aXNlZC5ncmQiKQ0KYGBgDQoNCiMjIyBJbnRlcnByZXQgQWNvdXN0aWMgY2x1c3RlcnMNCg0KVXNpbmcgdGhlIGdyb3VuZC10cnV0aCBkYXRhIHBvaW50cywgdGhlIHN1YnN0cmF0ZSBvYnNlcnZhdGlvbnMgd2l0aGluIGVhY2ggY2x1c3RlciB3ZXJlIGNvdW50ZWQgdXAgYW5kIGVhY2ggY2x1c3RlciB3YXMgaW50ZXJwcmV0ZWQgYXMgdGhlIHN1YnN0cmF0ZSB0aGF0IG9jY3VyZWQgbW9zdCBvZnRlbiB3aXRoaW4gaXQuIFRoZSBmaXJ0IHBhbmVsIHNob3dzIHRoZSBjbHVzdGVyIG51bWJlciwgYW5kIHRoZSByaWdodCBwYW5lbCBzaG93cyB0aGUgY2xhc3NpZmllZCBtYXAgYmFzZWQgb24gbWFqb3JpdHkgdm90ZSBvZiB0aGUgZ3JvdW5kLXRydXRoIGhhYml0YXQgb2JzZXJ2YXRpb25zIGluIGVhY2ggY2x1c3RlciAoc2VlIHRhYmxlIGJlbG93KS4NCg0KYGBge3IgZWNobz1UUlVFfQ0KYWNvdXN0aWNfY2xhc3M8LSByYXN0ZXI6OmV4dHJhY3QoeCA9IHVuc3VwJG1hcCwgeSA9IGZpbHRlcihUVl9zZXQsIFNldD09IlRyYWluaW5nIiksIG1ldGhvZD0ic2ltcGxlIiwgZmFjdG9ycz1GQUxTRSwgc3A9VFJVRSlAZGF0YQ0KYWNvdXN0aWNfY2xhc3M8LSBhY291c3RpY19jbGFzcyAlPiUgc2VsZWN0KFN1YnN0cmF0ZSwgQWNvdXN0aWNfQ2x1c3RlcikgJT4lIG5hLm9taXQoKQ0Kdm90ZXM8LSBhY291c3RpY19jbGFzcyAlPiUgZ3JvdXBfYnkoQWNvdXN0aWNfQ2x1c3RlcikgJT4lIGNvdW50KFN1YnN0cmF0ZSkgJT4lIHVuZ3JvdXAoKQ0Kdm90ZXNfdGFibGU8LSB2b3RlcyAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFN1YnN0cmF0ZSwgdmFsdWVzX2Zyb209biwgdmFsdWVzX2ZpbGw9MCkNCnZvdGVzX3RhYmxlPC0gdm90ZXNfdGFibGUgJT4lIG11dGF0ZShBc3NpZ25lZF9DbGFzcyA9IGlmZWxzZSh0ZXN0ID0gU2FuZD5Sb2NrLCB5ZXMgPSAiU2FuZCIsIG5vID0gIlJvY2siKSkNCnZvdGVzX3RhYmxlDQpgYGANCg0KRmlndXJlIFMxLjExDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpxX3J1bGVzPC0gdm90ZXNfdGFibGUgJT4lIG11dGF0ZShBc3NpZ25lZF9DbGFzcz0gaWZlbHNlKHRlc3QgPSBBc3NpZ25lZF9DbGFzcz09IlJvY2siLCB5ZXMgPSAxLCBubyA9IDIpKSAlPiUgc2VsZWN0KEFjb3VzdGljX0NsdXN0ZXIsIEFzc2lnbmVkX0NsYXNzKSAlPiUgYXMubWF0cml4KCkNCg0KdW5zdXBfaGFiPC0gcmFzdGVyOjpyZWNsYXNzaWZ5KHVuc3VwJG1hcCwgcmNsPSBxX3J1bGVzKQ0KbmFtZXModW5zdXBfaGFiKTwtICJTdWJzdHJhdGUiDQp1bnN1cF9oYWI8LSBhcy5mYWN0b3IodW5zdXBfaGFiKQ0KbGV2ZWxzKHVuc3VwX2hhYilbWzFdXSRTdWJzdHJhdGU8LSBjKCJSb2NrIiwgIlNhbmQiKQ0KDQp1bnN1cF9jbHVzdGVyX3Bsb3Q8LSB0bV9zaGFwZShzdF9hc19zdGFycyh1bnN1cCRtYXAsIGlnbm9yZV9maWxlPVRSVUUpLCByYXN0ZXIuZG93bnNhbXBsZSA9IEZBTFNFKSsNCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjKCJibHVlIiwiZ3JlZW4iLCAicHVycGxlIiwgIm9yYW5nZSIpLCBicmVha3MgPSAxOjUpKw0KICB0bV9ncmF0aWN1bGVzKGxpbmVzPUZBTFNFKSsNCiAgdG1fY3JlZGl0cyh0ZXh0ID0gImEpIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJ0b3AiKSwgc2l6ZSA9IDEpDQoNCnVuc3VwX3N1Yl9wbG90PC0gdG1fc2hhcGUodW5zdXBfaGFiLCByYXN0ZXIuZG93bnNhbXBsZSA9IEZBTFNFKSsNCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjKCJyZWQiLCIjRkZFQkJFIikpKw0KICB0bV9ncmF0aWN1bGVzKGxpbmVzPUZBTFNFKSsNCiAgdG1fY3JlZGl0cyh0ZXh0ID0gImIpIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJ0b3AiKSwgc2l6ZSA9IDEpDQoNCg0KdG1hcF9hcnJhbmdlKHVuc3VwX2NsdXN0ZXJfcGxvdCx1bnN1cF9zdWJfcGxvdCkNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Kd3JpdGVSYXN0ZXIodW5zdXBfaGFiLCBmaWxlbmFtZSA9ICJQcm9kdWN0cy9NYXBwaW5nL0VsYm93X1N1YnN0cmF0ZV9VbnN1cGVydmlzZWQudGlmIiwgb3ZlcndyaXRlPVRSVUUpDQp3cml0ZVJhc3Rlcih1bnN1cF9oYWIsIGZpbGVuYW1lID0gIlByb2R1Y3RzL01hcHBpbmcvRWxib3dfU3Vic3RyYXRlX1Vuc3VwZXJ2aXNlZC5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCnVuc3VwX2hhYjwtIHJhc3RlcigiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19TdWJzdHJhdGVfVW5zdXBlcnZpc2VkLmdyZCIpDQpgYGANCg0KYGBge3IgZWNobz1GQUxTRX0NCnVuc3VwX2FyZWE8LSBhcy5kYXRhLmZyYW1lKGZyZXEodW5zdXBfaGFiLCB1c2VOQT0ibm8iLCBtZXJnZT1UUlVFKSkNCnVuc3VwX2FyZWEkU3Vic3RyYXRlPWMoIlJvY2siLCAiU2FuZCIpDQp1bnN1cF9hcmVhPC0gdW5zdXBfYXJlYSAlPiUgbXV0YXRlKEFyZWFfbTI9IGNvdW50KihyZXMoYWxsX29mKHVuc3VwX2hhYikpWzFdKSoocmVzKGFsbF9vZih1bnN1cF9oYWIpKVsyXSkpDQp1bnN1cF9hcmVhPC0gdW5zdXBfYXJlYSAlPiUgbXV0YXRlKEFyZWFfa20yPUFyZWFfbTIvMWU2KQ0KdW5zdXBfYXJlYSAlPiUgc2VsZWN0KFN1YnN0cmF0ZSwgQXJlYV9rbTIpICU+JSBtdXRhdGUoQXJlYV9rbTI9cm91bmQoQXJlYV9rbTIsMikpDQpgYGANCg0KIyMjIEFjdXJhY2N5IEFzc2Vzc21lbnQNCg0KQWNjdXJhY3kgYXNzZXNzbWVudCB3YXMgcGVyZm9ybWVkIGJ5IGNvbXBhcmluZyB0aGUgcHJlZGljdGlvbnMgdG8gdGhlIG9ic2VydmVkIGdyb3VuZC10cnV0aCBoYWJpdGF0IG9ic2VydmF0aW9ucyBvbiB0aGUgdHJhbnNlY3QgdGhhdCB3YXMgcmVzZXJ2ZWQgZm9yIHZhbGlkYXRpb24gYW5kIG5vdCB1c2VkIHRvIGZpdCB0aGUgbW9kZWwuDQoNCmBgYHtyIGVjaG89VFJVRX0NCnVuc3VwX3ZhbDwtIHZhbGlkYXRlTWFwKG1hcCA9IHVuc3VwX2hhYiwgdmFsRGF0YSA9IGZpbHRlcihUVl9zZXQsIFNldD09IlZhbGlkYXRpb24iKSwgcmVzcG9uc2VDb2wgPSAiU3Vic3RyYXRlIiwgbW9kZT0iY2xhc3NpZmljYXRpb24iLCBjbGFzc01hcHBpbmc9ZGF0YS5mcmFtZShjbGFzcz1jKCJSb2NrIiwgIlNhbmQiKSwgY2xhc3NJRD0gYygxLDIpKSkNCmBgYA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KcHJpbnQodW5zdXBfdmFsJHBlcmZvcm1hbmNlJHRhYmxlKQ0KYGBgDQpPdmVyYWxsIEFjY3VyYWN5ID0gYHIgcm91bmQodW5zdXBfdmFsJHBlcmZvcm1hbmNlJG92ZXJhbGxbWyJBY2N1cmFjeSJdXSAqMTAwLDEpYCUgDQoNCkthcHBhID0gYHIgcm91bmQodW5zdXBfdmFsJHBlcmZvcm1hbmNlJG92ZXJhbGxbWyJLYXBwYSJdXSwyKWANCg0KQSBLYXBwYSBzY29yZSBvZiBncmVhdGVyIHRoYW4gYHIgYWdyZWVtZW50X2xldmVscyRtaW5fa1ttYXgod2hpY2godW5zdXBfdmFsJHBlcmZvcm1hbmNlJG92ZXJhbGxbWyJLYXBwYSJdXT5hZ3JlZW1lbnRfbGV2ZWxzJG1pbl9rKSldYCBpbmRpY2F0ZXMgYHIgYWdyZWVtZW50X2xldmVscyRBZ3JlZW1lbnRbbWF4KHdoaWNoKHVuc3VwX3ZhbCRwZXJmb3JtYW5jZSRvdmVyYWxsW1siS2FwcGEiXV0+YWdyZWVtZW50X2xldmVscyRtaW5faykpXWAgYWdyZWVtZW50IGJldHdlZW4gcHJlZGljdGlvbnMgYW5kIG9ic2VydmF0aW9ucy4NCg0KDQojIyBDb21wYXJpc29uIG9mIFN1cGVydmlzZWQgYW5kIFVuc3VwZXJ2aXNlZA0KDQojIyMgRGlmZmVyZW5jZSBNYXBzDQoNCkZvciBhcmVhcyBvZiBkaXNhZ3JlZW1lbnQsIHRoZSBsZWdlbmQgaXMgZm9ybWF0dGVkIGFzIHN1cGVydmlzZWQvdW5zdXBlcnZpc2VkLg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KZGlmZl9tYXA8LSAoc3VwX2hhYioxMCkrdW5zdXBfaGFiDQpkaWZmX21hcFtzdXBfaGFiPT11bnN1cF9oYWJdPC0gMQ0KZGlmZl9tYXA8LSBhcy5mYWN0b3IoZGlmZl9tYXApDQpuYW1lcyhkaWZmX21hcCk8LSAiRGlmZmVyZW5jZSINCmxldmVscyhkaWZmX21hcClbWzFdXSREaWZmZXJlbmNlPC0gYygiQWdyZWVtZW50IiwgIlJvY2svU2FuZCIsICJTYW5kL1JvY2siKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQp3cml0ZVJhc3RlcihkaWZmX21hcCwgZmlsZW5hbWUgPSAiUHJvZHVjdHMvTWFwcGluZy9kaWZmZXJlbmNlX21hcC50aWYiLCBvdmVyd3JpdGU9VFJVRSkNCndyaXRlUmFzdGVyKGRpZmZfbWFwLCBmaWxlbmFtZSA9ICJQcm9kdWN0cy9NYXBwaW5nL2RpZmZlcmVuY2VfbWFwLmdyZCIsIG92ZXJ3cml0ZT1UUlVFKQ0KZGlmZl9tYXA8LSByYXN0ZXIoIlByb2R1Y3RzL01hcHBpbmcvZGlmZmVyZW5jZV9tYXAuZ3JkIikNCmBgYA0KDQpGaWd1cmUgUzEuMTIgIA0KDQpGb3IgYXJlYXMgb2YgZGlzYWdyZWVtZW50LCB0aGUgbGVnZW5kIGlzIGZvcm1hdHRlZCBhcyBzdXBlcnZpc2VkL3Vuc3VwZXJ2aXNlZC4gIA0KDQpgYGB7ciBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD04fQ0KZGlmZl9tYXBfcGxvdDwtIHRtX3NoYXBlKGRyb3BsZXZlbHMoc3RfYXNfc3RhcnMoZGlmZl9tYXAsIGlnbm9yZV9maWxlPVRSVUUpLCBleGNsdWRlPU5BKSwgcmFzdGVyLmRvd25zYW1wbGUgPSBGQUxTRSkrDQogIHRtX3Jhc3RlcihwYWxldHRlID0gYygicGFsZWdyZWVuIiwgInJlZCIsIiNGRkVCQkUiKSwgdGl0bGU9IiIsIGRyb3AubGV2ZWxzID0gVFJVRSkrDQogICB0bV9ncmF0aWN1bGVzKGxpbmVzPUZBTFNFKQ0KZGlmZl9tYXBfcGxvdA0KYGBgDQoNCg0KIyMjIEtoaXN0IGFuZCBLbG9jDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpoYWJfbWFwX2RmPC0gdGliYmxlKHN1cD0gdmFsdWVzKHN1cF9oYWIpLCB1bnN1cD0gdmFsdWVzKHVuc3VwX2hhYikpDQpoYWJfbWFwX2RmPC0gbmEub21pdChoYWJfbWFwX2RmKQ0KaGFiX21hcF9kZiRzdXA8LSBmYWN0b3IoaGFiX21hcF9kZiRzdXAsIGxldmVscyA9IGMoIjEiLCAiMiIpKQ0KaGFiX21hcF9kZiR1bnN1cDwtIGZhY3RvcihoYWJfbWFwX2RmJHVuc3VwLCBsZXZlbHMgPSBjKCIxIiwgIjIiKSkNCmhhYl9tYXBfY29uZl9tYXQ8LSBjb25mdXNpb25NYXRyaXgoZGF0YSA9IGhhYl9tYXBfZGYkdW5zdXAsIHJlZmVyZW5jZSA9IGhhYl9tYXBfZGYkc3VwKSR0YWJsZQ0KDQpQQTwtIHN1bShkaWFnKGhhYl9tYXBfY29uZl9tYXQpL3N1bShoYWJfbWFwX2NvbmZfbWF0KSkNClBFPC0gcmVwKE5BX3JlYWxfLCBucm93KGhhYl9tYXBfY29uZl9tYXQpKQ0KUE1heDwtIHJlcChOQV9yZWFsXywgbnJvdyhoYWJfbWFwX2NvbmZfbWF0KSkNCg0KZm9yIChpIGluIDE6bnJvdyhoYWJfbWFwX2NvbmZfbWF0KSkgew0KICBwX2lUPC0gKHJvd1N1bXMoaGFiX21hcF9jb25mX21hdClbaV0pL3N1bShoYWJfbWFwX2NvbmZfbWF0KQ0KICBwX1RpPC0gKGNvbFN1bXMoaGFiX21hcF9jb25mX21hdClbaV0pL3N1bShoYWJfbWFwX2NvbmZfbWF0KQ0KICBQRVtpXTwtIHBfaVQqcF9UaQ0KICBQTWF4W2ldPC0gbWluKGMocF9pVCxwX1RpKSkNCn0NClBFPC0gc3VtKFBFKQ0KUG1heDwtIHN1bShQTWF4KQ0KSzwtIChQQS1QRSkvKDEtUEUpDQpLbG9jPC0gKFBBLVBFKS8oUG1heC1QRSkNCktoaXN0bzwtIChQbWF4LVBFKS8oMS1QRSkNCmBgYA0KDQpJbiBhZGRpdGlvbiB0byBjb21wYXJpbmcgdGhlIGFncmVlbWVudCBiZXR3ZWVuIGdyb3VuZC10cnV0aCBvYnNlcnZhdGlvbnMgYW5kIHByZWRpY3Rpb25zLCBLYXBwYSBjYW4gYmUgdXNlZCB0byBjb21wYXJlIHRoZSBhZ3JlZW1lbnQgaW4gcHJlZGljdGlvbnMgYmV0d2VlbiB0aGUgdHdvIG1hcHMgYnkgbG9va2luZyBhdCB0aGUgYWdyZWVtZW50IGluIGNvcnJlc3BvbmRpbmcgY2xhc3NpZmljYXRpb25zIGZvciBlYWNoIHBpeGVsLiBBZGRpdGlvbmFsbHkgS2FwcGEgY2FuIGJlIGJyb2tlbiBkb3duIGludG8gdHdvIGNvbXBvbmVudHM6IEtsb2NhdGlvbiBhbmQgS2hpc3RvLCB3aGVyZSBLYXBwYSBpcyBkZWZpbmVkIGFzIHRoZSBwcm9kdWN0IG9mIEtsb2NhdGlvbiBhbmQgS2hpc3RvIChQb250aXVzLCAyMDAwOyBIYWdlbiwgMjAwMjsgU291c2EgZXQgYWwgMjAwMikuIEtsb2NhdGlvbiBpcyBhIG1lYXN1cmUgb2YgdGhlIHNpbWlsYXJpdHkgaW4gdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSBzdWJzdHJhdGUsIGFuZCBLaGlzdG8gaXMgbWVhc3VyZSBvZiB0aGUgc2ltaWxhcml0eSBpbiB0aGUgZnJlcXVlbmN5IG9mIGNsYXNzIHByZWRpY3Rpb25zIChQb250aXVzLCAyMDAwOyBIYWdlbiwgMjAwMjsgU291c2EgZXQgYWwgMjAwMikuIFRoZSB0d28gbWFwcyBoYXZlIGEgS2xvYyB2YWx1ZSBvZiBgciByb3VuZChLbG9jLDMpYCBpbmRpY2F0aW5nIGByIGFncmVlbWVudF9sZXZlbHMkQWdyZWVtZW50W0tsb2MgPiBhZ3JlZW1lbnRfbGV2ZWxzJG1pbl9rICYgS2xvYyA8PSBhZ3JlZW1lbnRfbGV2ZWxzJG1heF9rXWAgYWdyZWVtZW50IGluIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBjbGFzc2VzLCBhbmQgYSBLaGlzdG8gb2YgYHIgcm91bmQoS2hpc3RvLDMpYCBpbmRpY2F0aW5nIGByIGFncmVlbWVudF9sZXZlbHMkQWdyZWVtZW50W0toaXN0byA+IGFncmVlbWVudF9sZXZlbHMkbWluX2sgJiBLaGlzdG8gPD0gYWdyZWVtZW50X2xldmVscyRtYXhfa11gIGFncmVlbWVudCBpbiB0aGUgZnJlcXVlbmN5IG9mIGNsYXNzIHByZWRpY3Rpb25zLiBPdmVyYWxsIHRoaXMgY29ycmVzcG9uZHMgdG8gYSBLYXBwYSB2YWx1ZSBvZiBgciByb3VuZChLLDMpYCBpbmRpY2F0aW5nIGEgYHIgYWdyZWVtZW50X2xldmVscyRBZ3JlZW1lbnRbSyA+IGFncmVlbWVudF9sZXZlbHMkbWluX2sgJiBLIDw9IGFncmVlbWVudF9sZXZlbHMkbWF4X2tdYCBsZXZlbCBvZiBhZ3JlZW1lbnQgYmV0d2VlbiB0aGUgdHdvIG1hcHMuDQoNCg0KIyMjIE1vbnRlLUNhcmxvIFBlcm11dGF0aW9uIFRlc3QNCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCnZhbF9jb21wPC0gdGliYmxlKHJlZj1wdWxsKGZpbHRlcihUVl9zZXRAZGF0YSwgU2V0PT0iVmFsaWRhdGlvbiIpLCBTdWJzdHJhdGUpLA0KICAgICAgICAgICAgIHN1cD0gcmFzdGVyOjpleHRyYWN0KHggPSBzdXBfaGFiLCB5PWZpbHRlcihUVl9zZXQsIFNldD09ICJWYWxpZGF0aW9uIiksIG5hLnJtPUZBTFNFKSwNCiAgICAgICAgICAgICB1bnN1cD0gcmFzdGVyOjpleHRyYWN0KHggPSB1bnN1cF9oYWIsIHk9ZmlsdGVyKFRWX3NldCwgU2V0PT0gIlZhbGlkYXRpb24iKSwgbmEucm09RkFMU0UsKSkNCnZhbF9jb21wPC0gbmEub21pdCh2YWxfY29tcCkNCnZhbF9jb21wPC0gdmFsX2NvbXAgJT4lIG11dGF0ZShzdXA9aWZlbHNlKHRlc3QgPSBzdXA9PTEsIHllcyA9ICJSb2NrIiwgbm8gPSAiU2FuZCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuc3VwPWlmZWxzZSh0ZXN0ID0gdW5zdXA9PTEsIHllcyA9ICJSb2NrIiwgbm8gPSAiU2FuZCIpKQ0KdmFsX2NvbXA8LSB2YWxfY29tcCAlPiUgbXV0YXRlX2FsbChmYWN0b3IsIGxldmVscz1jKCJSb2NrIiwgIlNhbmQiKSkNCktzdXA8LSBjb25mdXNpb25NYXRyaXgoZGF0YT12YWxfY29tcCRzdXAsIHJlZmVyZW5jZT12YWxfY29tcCRyZWYsIHBvc2l0aXZlID0gIlJvY2siKSRvdmVyYWxsW1siS2FwcGEiXV0NCkt1bnN1cDwtIGNvbmZ1c2lvbk1hdHJpeChkYXRhPXZhbF9jb21wJHVuc3VwLCByZWZlcmVuY2U9dmFsX2NvbXAkcmVmLCBwb3NpdGl2ZSA9ICJSb2NrIikkb3ZlcmFsbFtbIkthcHBhIl1dDQpLYXBwYV9EaWZmPC0gS3N1cC1LdW5zdXANCiMgI01vbnRlIGNhcmxvDQppdGVyPC0gMTAwMA0Ka2FwcGFfcGVybTwtIHRpYmJsZShLMT1yZXAoTkFfcmVhbF8saXRlciksIEsyPSBOQV9yZWFsXykNCmthcHBhX3Blcm0kSzFbMV08LSBLc3VwDQprYXBwYV9wZXJtJEsyWzFdPC0gS3Vuc3VwDQpzZXQuc2VlZCg1KQ0KZm9yIChpIGluIDI6aXRlcikgew0KICByZWZfcGVybTwtIHNhbXBsZSh4ID0gdmFsX2NvbXAkcmVmLCBzaXplID0gbnJvdyh2YWxfY29tcCksIHJlcGxhY2UgPSBGQUxTRSkNCiAga2FwcGFfcGVybSRLMVtpXTwtIGNvbmZ1c2lvbk1hdHJpeChkYXRhPXZhbF9jb21wJHN1cCwgcmVmZXJlbmNlPXJlZl9wZXJtLCBwb3NpdGl2ZSA9ICJSb2NrIikkb3ZlcmFsbFtbIkthcHBhIl1dDQogIGthcHBhX3Blcm0kSzJbaV08LSBjb25mdXNpb25NYXRyaXgoZGF0YT12YWxfY29tcCR1bnN1cCwgcmVmZXJlbmNlPXJlZl9wZXJtLCBwb3NpdGl2ZSA9ICJSb2NrIikkb3ZlcmFsbFtbIkthcHBhIl1dDQogIH0NCmthcHBhX3Blcm08LSAga2FwcGFfcGVybSAlPiUgbXV0YXRlKGtkaWZmPUsxLUsyKQ0KcF92YWw8LSBzdW0oa2FwcGFfcGVybSRrZGlmZj49S2FwcGFfRGlmZikvaXRlcg0KYGBgDQoNCkEgb25lLXRhaWxlZCBNb250ZS1DYXJsbyBwZXJtdXRhdGlvbiB0ZXN0IHdpdGggYHIgaXRlcmAgaXRlcmF0aW9ucyBvbiB0aGUgZGlmZmVyZW5jZSBpbiBLYXBwYSB2YWx1ZXMgd2FzIHBlcmZvcm1lZCAoTWNLZW56aWUgZXQgYWwsIDE5OTYpLiBUaGlzIHJlc3VsdHMgb2YgdGhpcyB0ZXN0IHNob3dlZCB0aGF0IHRoZSBzdXBlcnZpc2VkIGNsYXNzaWZpY2F0aW9uIG1hcCBwcm92aWRlZCBzaWduaWZpY2FudGx5IGhpZ2hlciBhZ3JlZW1lbnQgd2l0aCB0aGUgZ3JvdW5kLXRydXRoIG9ic2VydmF0aW9ucyBhcyBjb21wYXJlZCB0byB0aGUgdW5zdXBlcnZpc2VkIGNsYXNzaWZpY2F0aW9uIG1hcCAocCA8PSBgciBwX3ZhbGApLg0KDQojIE92ZXJsYXlpbmcgdmVydGljYWwgcmVsaWVmIG9uIHN1YnN0cmF0ZQ0KQmVjYXVzZSBoaWdoZXIgcmVsaWVmIGZlYXR1cmVzIHJlbGF0aXZlbHkgcmFyZSwgYW5kIGFyZSBmYWlybHkgc21hbGwgcmVsYXRpdmUgdG8gdGhlIGVycm9yIGFzc29jaWF0ZWQgd2l0aCBwb3NpdGlvbmluZyBvZiB0aGUgQ0JBU1MsIHdlIGRpZCBub3QgZGlyZWN0bHkgdHJ5IHRvIHByZWRpY3QgdGhpcyBpbiB0aGUgc3VwZXJ2aXNlZCBhbmQgdW5zdXBlcnZpc2VkIG1vZGVscy4gSG93ZXZlciwgd2UgY2FuIG1lYXN1cmUgdmVydGljYWwgcmVsaWVmIGFzIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcy4gVG90YWwgZGVwdGggd2FzIGNhbGN1bGF0ZWQgZnJvbSB0aGUgQ0JBU1Mgc2Vuc29ycyBhcyBDQkFTUyBkZXB0aCArIENCQVNTIGFsdGl0dWRlICh3aGVyZSBhbHRpdHVkZSBtZWFzdXJlbWVudHMgaGF2ZSBiZWVuIGFkanVzdGVkIGZvciB0aGUgcGl0Y2ggb2YgdGhlIHN5c3RlbSkuIFZlcnRpY2FsIHJlbGllZiBmb3IgZWFjaCAxNSBzZWNvbmQgYmluIHdhcyBjYWxjdWxhdGVkIGFzIHRoZSBtYXhpbXVtIGNoYW5nZSBpbiBkZXB0aCBvdmVyIHRoYXQgMTUgc2Vjb25kIG9ic2VydmF0aW9uIHdpbmRvdy4gQSBib3ggcGxvdCBvZiB0aGUgYXNzaWduZWQgdmlzdWFsIHJlbGllZiBjbGFzcywgYW5kIHRoZSBtZWFzdXJlZCB2ZXJ0aWNhbCByZWxpZWYgZnJvbSB0aGUgc2Vuc29ycyBpcyBzaG93biBiZWxvdy4gSW4gYmx1ZSwgb3JhbmdlLCBhbmQgcmVkIGFyZSB0aGUgcmVsaWVmIGN1dG9mZnMgZm9yIGxvdywgbW9kZXJhdGUsIGFuZCBoaWdoIHJlbGllZiByZXNwZWN0aXZlbHkgYXMgcHJvcG9zZWQgYnkgU21pdGggZXQgYWwgKDIwMTEpIGluIHRoZWlyIHJlZWYgZmlzaCBzdXJ2ZXkgb2YgdGhlIEZsb3JpZGEgS2V5cy4gVGhpcyBhcHBlYXJzIHRvIG1hdGNoIHVwIGZhaXJseSB3ZWxsIHdpdGggb3VyIG9ic2VydmF0aW9ucywgc28gd2Ugd2lsbCB1c2UgdGhlc2UgY3V0b2ZmczoNCmxvdyByZWxpZWYgPD0gMW0gOyAxbSA8IG1vZGVyYXRlIHJlbGllZiA8PSAybTsgaGlnaCByZWxpZWYgPiAybS4NCg0KRmlndXJlIFMxLjEzDQoNCmBgYHtyIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTR9DQpvbmVfaHo8LSBvbmVfaHogJT4lIG11dGF0ZShUb3RhbF9EZXB0aD0gZGVwdGgrIGFsdGl0dWRlKmNvcyhyYWRpYW5zKHBpdGNoKSkpICNDYWxjdWxhdGUgVG90YWwgZGVwdGgNCg0Kb25lX2h6PC0gb25lX2h6ICU+JSBtdXRhdGUodmVydGljYWxfcmVsaWVmXzE1cyA9IE5BX3JlYWxfKQ0KZm9yIChpIGluIDg6KG5yb3cob25lX2h6KS03KSkgew0KICBjdXJyX2lkeDwtIChpLTcpOihpKzcpDQogIGlmKGxlbmd0aCh1bmlxdWUob25lX2h6JFRyYW5zZWN0W2N1cnJfaWR4XSkpIT0xKXsNCiAgICBuZXh0KCkgI1ByZXZlbnQgYXZlcmFnaW5nIGFjcm9zcyBkaWZmZXJlbnQgdHJhbnNlY3RzDQogICAgfQ0KICBkdmFsdWVzPC0gb25lX2h6JFRvdGFsX0RlcHRoW2N1cnJfaWR4XQ0KICBkbWluPC0gbWluKGR2YWx1ZXMsIG5hLnJtPVRSVUUpDQogIGRtYXg9IG1heChkdmFsdWVzLCBuYS5ybT1UUlVFKQ0KICB2cmVsaWVmPC0gZG1heC1kbWluDQogIGlmKGlzLmluZmluaXRlKHZyZWxpZWYpKXt2cmVsaWVmPC1OQX0NCiAgb25lX2h6JHZlcnRpY2FsX3JlbGllZl8xNXNbaV08LSB2cmVsaWVmDQp9DQoNCmhhYl9yZWxpZWY8LSBoYWIgJT4lIGxlZnRfam9pbihvbmVfaHosIGJ5PSBjKCJ0aW1lc3RhbXAiLCJUcmFuc2VjdCIpKSAlPiUgc2VsZWN0KHRpbWVzdGFtcCwgVHJhbnNlY3QsIFJlbGllZiwgdmVydGljYWxfcmVsaWVmXzE1cykgJT4lIGZpbHRlcihSZWxpZWYhPSJOb25lIiAmIFJlbGllZiAhPSAiTm9fVmlzIikNCmhhYl9yZWxpZWYkUmVsaWVmPC0gZmFjdG9yKGhhYl9yZWxpZWYkUmVsaWVmLCBsZXZlbHM9YygiTG93X1JlbGllZiIsICJNb2RlcmF0ZV9SZWxpZWYiLCAiSGlnaF9SZWxpZWYiKSkNCg0KdmlzdWFsX3JlbGllZl9wbG90PC0gZ2dwbG90KGRhdGEgPSBoYWJfcmVsaWVmLCBtYXBwaW5nID0gYWVzKHg9IFJlbGllZiwgeT12ZXJ0aWNhbF9yZWxpZWZfMTVzKSkrDQogIGFubm90YXRlKCJyZWN0IiwgeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAwLCB5bWF4ID0gMSwgZmlsbD0gImRvZGdlcmJsdWUiLCBhbHBoYT0uMikrDQogIGFubm90YXRlKCJyZWN0IiwgeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAxLCB5bWF4ID0gMiwgZmlsbD0gIm9yYW5nZSIsIGFscGhhPS4yKSsNCiAgYW5ub3RhdGUoInJlY3QiLCB4bWluID0gLUluZiwgeG1heCA9IEluZiwgeW1pbiA9IDIsIHltYXggPSBJbmYsIGZpbGw9ICJyZWQiLCBhbHBoYT0uMikrDQogIGdlb21fYm94cGxvdCgpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDgsIGJ5PSAxKSwgZXhwYW5kID0gYygwLDApKSsNCiAgeWxhYigiVmVydGljYWwgUmVsaWVmIChtKSBvdmVyIDE1IHMiKSsNCiAgeGxhYigiUmVsaWVmIENsYXNzIikNCnZpc3VhbF9yZWxpZWZfcGxvdA0KYGBgDQoNClRoZXNlIHRocmVzaG9sZHMgYXJlIHRoZW4gdXNlZCB0byBkZWxpbmVhdGUgaGlnaGVyIHJlbGllZiBhcmVhcyBkaXJlY3RseSBmcm9tIHRoZSBiYXRoeW1ldHJ5IGJ5IGNhbGN1bGF0aW5nIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYSBjZW50cmFsIHBpeGVsIGFuZCB0aGUgbWluaW11bSBkZXB0aCBvZiBzdXJyb3VuZGluZyBwaXhlbHMgdXNpbmcgYSBzbGlkaW5nIDN4MyB3aW5kb3cuDQoNCkZpZ3VyZSBTMS4xNA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KdzwtIG1hdHJpeChkYXRhID0gMSwgbnJvdyA9IDMsbmNvbCA9IDMpICMzeDMgd2luZG93DQp3WzVdPC0gTkENCm1pbl9iYXRoeTwtIGZvY2FsKHggPSBiYXRoeSwgdz13LCBmdW49bWluLCBuYS5ybT1UUlVFKSAjQ2FsY3VsYXRlIG1pbmltdW0gZGVwdGggYXJvdW5kIGEgY2VudHJhbCBwaXhlbA0KbWluX2JhdGh5W2lzLmluZmluaXRlKG1pbl9iYXRoeSldPC0gTkEgI1JlcGxhY2UgSW5mIHdpdGggTkENCnJlbGllZjwtIGJhdGh5LW1pbl9iYXRoeSAjQ2FsY3VsYXRlIHZlcnRpY2FsIHJlbGllZg0KDQpyZWxpZWZfcXJ1bGVzPC0gbWF0cml4KGMoLUluZiwxLDIsIDEsIDIsIEluZiwgMSwyLDMpLCBucm93ID0gMyxuY29sID0gMykNCnJlbGllZl9jbGFzc2lmaWVkPC0gcmFzdGVyOjpyZWNsYXNzaWZ5KHJlbGllZiwgcmVsaWVmX3FydWxlcywgaW5jbHVkZS5sb3dlc3Q9RkFMU0UsIHJpZ2h0PVRSVUUpDQpyZWxpZWZfY2xhc3NpZmllZDwtIGFzLmZhY3RvcihyZWxpZWZfY2xhc3NpZmllZCkNCmxldmVscyhyZWxpZWZfY2xhc3NpZmllZClbWzFdXSRSZWxpZWZfQ2xhc3M8LSBjKCJMb3ciLCJNb2QiLCAiSGlnaCIpDQpgYGANCg0KYGBge3IgaW5jbHVkZT0gRkFMU0V9DQp3cml0ZVJhc3RlcihtaW5fYmF0aHksICJEYXRhL2Rlcml2ZWQvTXVsdGliZWFtL21pbl9iYXRoeS5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCm1pbl9iYXRoeTwtIHJhc3RlcigiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9taW5fYmF0aHkuZ3JkIikNCndyaXRlUmFzdGVyKHJlbGllZiwgIkRhdGEvZGVyaXZlZC9NdWx0aWJlYW0vcmVsaWVmLmdyZCIsIG92ZXJ3cml0ZT1UUlVFKQ0KcmVsaWVmPC0gcmFzdGVyKCJEYXRhL2Rlcml2ZWQvTXVsdGliZWFtL3JlbGllZi5ncmQiKQ0Kd3JpdGVSYXN0ZXIocmVsaWVmX2NsYXNzaWZpZWQsICJEYXRhL2Rlcml2ZWQvTXVsdGliZWFtL3JlbGllZl9jbGFzc2lmaWVkLmdyZCIsb3ZlcndyaXRlPVRSVUUpDQpyZWxpZWZfY2xhc3NpZmllZDwtIHJhc3RlcigiRGF0YS9kZXJpdmVkL011bHRpYmVhbS9yZWxpZWZfY2xhc3NpZmllZC5ncmQiKQ0KYGBgDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpSZWxpZWZfTWFwX1Bsb3Q8LSB0bV9zaGFwZShyZWxpZWYsIHJhc3Rlci5kb3duc2FtcGxlID0gRkFMU0UpKw0KICB0bV9yYXN0ZXIocGFsZXR0ZT1wYWwxLCBtaWRwb2ludCA9IE5BLCBzdHlsZT0iY29udCIsIHRpdGxlID0gIlJlbGllZiAobSkiKSsNCiAgIHRtX2dyYXRpY3VsZXMobGluZXM9RkFMU0UpDQpSZWxpZWZfQ2xhc3NpZmllZF9QbG90PC0gdG1fc2hhcGUoc3RfYXNfc3RhcnMocmVsaWVmX2NsYXNzaWZpZWQsIGlnbm9yZV9maWxlPVRSVUUpLCByYXN0ZXIuZG93bnNhbXBsZSA9IEZBTFNFKSsNCiAgdG1fcmFzdGVyKHBhbGV0dGU9YygiZ3JheSIsICJ5ZWxsb3ciLCAicmVkIiksIHRpdGxlID0gIlJlbGllZiBDbGFzcyIpKw0KICAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkNCnRtYXBfYXJyYW5nZShSZWxpZWZfTWFwX1Bsb3QsIFJlbGllZl9DbGFzc2lmaWVkX1Bsb3QpDQpgYGANCg0KDQpGaWd1cmUgUzEuMTUNCmBgYHtyIH0NCnN1Yl9yZWxpZWY8LSAoc3VwX2hhYioxMCkrcmVsaWVmX2NsYXNzaWZpZWQNCnN1Yl9yZWxpZWZbc3ViX3JlbGllZj09MjJdPC0yMQ0Kc3ViX3JlbGllZltzdWJfcmVsaWVmPT0yM108LTIxICNDb2xsYXBzZSBzYW5kIHRvIG9uZSBjYXRlZ29yeQ0Kc3ViX3JlbGllZjwtIGFzLmZhY3RvcihzdWJfcmVsaWVmKQ0KbmFtZXMoc3ViX3JlbGllZik8LSAiSGFiaXRhdCINCmxldmVscyhzdWJfcmVsaWVmKVtbMV1dJGhhYml0YXQ8LSAgYygiTFIgUm9jayIsICJNUiBSb2NrIiwgIkhSIFJvY2siLCAiU2FuZCIpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCndyaXRlUmFzdGVyKHN1Yl9yZWxpZWYsIGZpbGVuYW1lID0gIlByb2R1Y3RzL01hcHBpbmcvRWxib3dfU3VwZXJ2aXNlZF9IYWJSZWxpZWYudGlmIiwgb3ZlcndyaXRlPVRSVUUpDQp3cml0ZVJhc3RlcihzdWJfcmVsaWVmLCBmaWxlbmFtZSA9ICJQcm9kdWN0cy9NYXBwaW5nL0VsYm93X1N1cGVydmlzZWRfSGFiUmVsaWVmLmdyZCIsIG92ZXJ3cml0ZT1UUlVFKQ0Kc3ViX3JlbGllZjwtIHJhc3RlcigiUHJvZHVjdHMvTWFwcGluZy9FbGJvd19TdXBlcnZpc2VkX0hhYlJlbGllZi5ncmQiKQ0KYGBgDQoNCmBgYHtyfQ0Kc3ViX3JlbGllZl9wbG90PC0gdG1fc2hhcGUoZHJvcGxldmVscyhzdF9hc19zdGFycyhzdWJfcmVsaWVmLCBpZ25vcmVfZmlsZT1UUlVFKSwgZXhjbHVkZT1OQSksIHJhc3Rlci5kb3duc2FtcGxlID0gRkFMU0UpKw0KICB0bV9yYXN0ZXIocGFsZXR0ZSA9IGMoImRvZGdlcmJsdWUiLCAib3JhbmdlIiwgInJlZCIsICIjRkZFQkJFIikpKw0KICAgdG1fZ3JhdGljdWxlcyhsaW5lcz1GQUxTRSkNCnN1Yl9yZWxpZWZfcGxvdA0KYGBgDQoNCg0KYGBge3IgZWNobz1GQUxTRX0NCmhhYl9hcmVhPC0gYXMuZGF0YS5mcmFtZShmcmVxKHN1Yl9yZWxpZWYsIHVzZU5BPSJubyIsIG1lcmdlPVRSVUUpKQ0KaGFiX2FyZWEkaGFiaXRhdDwtIGMoIkxSX1JvY2siLCAiTVJfUm9jayIsICJIUl9Sb2NrIiwgIlNhbmQiKQ0KaGFiX2FyZWE8LSBoYWJfYXJlYSAlPiUgbXV0YXRlKEFyZWFfbTI9IGNvdW50KihyZXMoYWxsX29mKHN1Yl9yZWxpZWYpKVsxXSkqKHJlcyhhbGxfb2Yoc3ViX3JlbGllZikpWzJdKSkNCmhhYl9hcmVhPC0gaGFiX2FyZWEgJT4lIG11dGF0ZShBcmVhX2ttMj1BcmVhX20yLzFlNikNCmhhYl9hcmVhPC0gaGFiX2FyZWEgJT4lIG11dGF0ZShBcmVhX3BlcmNlbnQ9MTAwKkFyZWFfa20yL3N1bShBcmVhX2ttMikpDQpoYWJfYXJlYSAlPiUgc2VsZWN0KGhhYml0YXQsIEFyZWFfa20yLCBBcmVhX3BlcmNlbnQpICU+JSBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCAuZm5zPXJvdW5kLCAyKSkNCmBgYA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0Kc2F2ZS5pbWFnZShmaWxlID0gJ0RhdGEvUl9lbnZpcm9ubWVudC9FbGJvd19SX01hcHBpbmdfRW52aXJvbm1lbnQuUkRhdGEnKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQp0bWFwX3NhdmUodG09Z3RydXRoX3Bsb3QsDQogICAgICAgICAgZmlsZW5hbWUgPSAiUHJvZHVjdHMvZmlndXJlcy9ndHJ1dGhfcGxvdC50aWZmIiwNCiAgICAgICAgICB3aWR0aD04LA0KICAgICAgICAgIGhlaWdodCA9IDgsDQogICAgICAgICAgdW5pdHM9ImluIiwNCiAgICAgICAgICBkcGk9MzAwKQ0KDQp0bWFwX3NhdmUodG09c3VwX3JldGFpbmVkX3Zhcl9wbG90LA0KICAgICAgICAgIGZpbGVuYW1lID0gIlByb2R1Y3RzL2ZpZ3VyZXMvc3VwX3JldGFpbmVkX3Zhcl9wbG90LnRpZmYiLA0KICAgICAgICAgIHdpZHRoPTcsDQogICAgICAgICAgaGVpZ2h0ID0gOCwNCiAgICAgICAgICB1bml0cz0iaW4iLA0KICAgICAgICAgIGRwaT0zMDApDQoNCnRtYXBfc2F2ZSh0bT0gdG1hcF9hcnJhbmdlKHN1cF9zdWJfcGxvdCwgc3VwX2VudF9wbG90KSwNCiAgICAgICAgICBmaWxlbmFtZSA9ICJQcm9kdWN0cy9maWd1cmVzL1N1cFN1YkVudC50aWZmIiwNCiAgICAgICAgICB3aWR0aD04LA0KICAgICAgICAgIGhlaWdodCA9IDgsDQogICAgICAgICAgdW5pdHM9ImluIiwNCiAgICAgICAgICBkcGk9MzAwKQ0KDQpnZ3NhdmUoZmlsZW5hbWUgPSAidmFyX2ltcF9wbG90LnRpZmYiLA0KICAgICAgIHBsb3QgPSB2YXJfaW1wX3Bsb3QrdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpLCANCiAgICAgICBkZXZpY2UgPSAidGlmZiIsIA0KICAgICAgIHBhdGggPSAiUHJvZHVjdHMvZmlndXJlcyIsIA0KICAgICAgIHdpZHRoID0gNiwgDQogICAgICAgaGVpZ2h0ID0gNCwgDQogICAgICAgdW5pdHMgPSAiaW4iLCANCiAgICAgICBkcGkgPSAzMDApDQoNCmdnc2F2ZShmaWxlbmFtZSA9ICJlcnJfdnNfbnRyZWUudGlmZiIsDQogICAgICAgcGxvdCA9IGVycl92c19udHJlZSt0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSksIA0KICAgICAgIGRldmljZSA9ICJ0aWZmIiwgDQogICAgICAgcGF0aCA9ICJQcm9kdWN0cy9maWd1cmVzIiwgDQogICAgICAgd2lkdGggPSA2LCANCiAgICAgICBoZWlnaHQgPSA0LCANCiAgICAgICB1bml0cyA9ICJpbiIsIA0KICAgICAgIGRwaSA9IDMwMCkNCg0KZ2dzYXZlKGZpbGVuYW1lID0gIlBDQV92YXJfcGxvdC50aWZmIiwNCiAgICAgICBwbG90ID0gUENBX3Zhcl9wbG90K3RoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSAgLCANCiAgICAgICBkZXZpY2UgPSAidGlmZiIsIA0KICAgICAgIHBhdGggPSAiUHJvZHVjdHMvZmlndXJlcyIsIA0KICAgICAgIHdpZHRoID0gNiwgDQogICAgICAgaGVpZ2h0ID0gNCwgDQogICAgICAgdW5pdHMgPSAiaW4iLCANCiAgICAgICBkcGkgPSAzMDApDQoNCnRtYXBfc2F2ZSh0bT1yZXRfUENBX3Bsb3QsDQogICAgICAgICAgZmlsZW5hbWUgPSAiUHJvZHVjdHMvZmlndXJlcy9yZXRfUENBX3Bsb3QudGlmZiIsDQogICAgICAgICAgd2lkdGg9NiwNCiAgICAgICAgICBoZWlnaHQgPSA4LA0KICAgICAgICAgIHVuaXRzPSJpbiIsDQogICAgICAgICAgZHBpPTMwMCkNCg0KdG1hcF9zYXZlKHRtPXRtYXBfYXJyYW5nZSh1bnN1cF9jbHVzdGVyX3Bsb3QsdW5zdXBfc3ViX3Bsb3QpLA0KICAgICAgICAgIGZpbGVuYW1lID0gIlByb2R1Y3RzL2ZpZ3VyZXMvY2x1c3RfdW5zdXBfX3Bsb3QudGlmZiIsDQogICAgICAgICAgd2lkdGg9OCwNCiAgICAgICAgICBoZWlnaHQgPSA4LA0KICAgICAgICAgIHVuaXRzPSJpbiIsDQogICAgICAgICAgZHBpPTMwMCkNCg0KdG1hcF9zYXZlKHRtPWRpZmZfbWFwX3Bsb3QsDQogICAgICAgICAgZmlsZW5hbWUgPSAiUHJvZHVjdHMvZmlndXJlcy9kaWZmX21hcF9wbG90LnRpZmYiLA0KICAgICAgICAgIHdpZHRoPTYsDQogICAgICAgICAgaGVpZ2h0ID0gOCwNCiAgICAgICAgICB1bml0cz0iaW4iLA0KICAgICAgICAgIGRwaT0zMDApDQoNCnRtYXBfc2F2ZSh0bT10bWFwX2FycmFuZ2UoUmVsaWVmX01hcF9QbG90LCBSZWxpZWZfQ2xhc3NpZmllZF9QbG90KSwNCiAgICAgICAgICBmaWxlbmFtZSA9ICJQcm9kdWN0cy9maWd1cmVzL3JlbGllZl9tYXBfcGxvdC50aWZmIiwNCiAgICAgICAgICB3aWR0aD04LA0KICAgICAgICAgIGhlaWdodCA9IDgsDQogICAgICAgICAgdW5pdHM9ImluIiwNCiAgICAgICAgICBkcGk9MzAwKQ0KDQp0bWFwX3NhdmUodG09c3ViX3JlbGllZl9wbG90LA0KICAgICAgICAgIGZpbGVuYW1lID0gIlByb2R1Y3RzL2ZpZ3VyZXMvc3ViX3JlbGllZl9wbG90LnRpZmYiLA0KICAgICAgICAgIHdpZHRoPTYsDQogICAgICAgICAgaGVpZ2h0ID0gOCwNCiAgICAgICAgICB1bml0cz0iaW4iLA0KICAgICAgICAgIGRwaT0zMDApDQoNCmdnc2F2ZShmaWxlbmFtZSA9ICJ2aXN1YWxfcmVsaWVmX3Bsb3QudGlmZiIsDQogICAgICAgcGxvdCA9IHZpc3VhbF9yZWxpZWZfcGxvdCwgDQogICAgICAgZGV2aWNlID0gInRpZmYiLCANCiAgICAgICBwYXRoID0gIlByb2R1Y3RzL2ZpZ3VyZXMiLCANCiAgICAgICB3aWR0aCA9IDYsIA0KICAgICAgIGhlaWdodCA9IDYsIA0KICAgICAgIHVuaXRzID0gImluIiwgDQogICAgICAgZHBpID0gMzAwKQ0KYGBgDQoNCg==