Polynomial Regression
The formula is y = \(b_{0}\) + \(b_{1}x_{1}\) + \(b_{2}x^{2}_{1}\).
used in Healthcare/ Epidemiology data
Position Salary dataset
# Importing the dataset
dataset = read.csv('./Part 2 - Regression/Section 6 - Polynomial Regression/R/Position_Salaries.csv')
# use the columns level and salary
dataset = dataset[2:3]
# no data splitting due to small dataset
# no feature scaling needed
# for a baseline comparison, use Simple Linear Regression
lin_reg = lm(formula = Salary ~ .,
data = dataset)
summary(lin_reg)
Call:
lm(formula = Salary ~ ., data = dataset)
Residuals:
Min 1Q Median 3Q Max
-170818 -129720 -40379 65856 386545
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -195333 124790 -1.565 0.15615
Level 80879 20112 4.021 0.00383 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 182700 on 8 degrees of freedom
Multiple R-squared: 0.669, Adjusted R-squared: 0.6277
F-statistic: 16.17 on 1 and 8 DF, p-value: 0.003833
# Fitting Polynomial Regression to the dataset
# polynomial features of indep variables (to any degree you want)
# 1 indep + dep. vars
# add column using $ and name it
# dataset$column^2 returns squared column for all level column values
dataset$Level2 = dataset$Level^2
# dataset$column^3 returns cubed column for all level column values
dataset$Level3 = dataset$Level^3
dataset$Level4 = dataset$Level^4
poly_reg = lm(formula = Salary ~ .,
data = dataset)
summary(poly_reg)
Call:
lm(formula = Salary ~ ., data = dataset)
Residuals:
1 2 3 4 5 6 7 8 9 10
-8357 18240 1358 -14633 -11725 6725 15997 10006 -28695 11084
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 184166.7 67768.0 2.718 0.04189 *
Level -211002.3 76382.2 -2.762 0.03972 *
Level2 94765.4 26454.2 3.582 0.01584 *
Level3 -15463.3 3535.0 -4.374 0.00719 **
Level4 890.2 159.8 5.570 0.00257 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 20510 on 5 degrees of freedom
Multiple R-squared: 0.9974, Adjusted R-squared: 0.9953
F-statistic: 478.1 on 4 and 5 DF, p-value: 1.213e-06
#---------------- Visualizing the Linear Regression results
# install.packages('ggplot2')
library(ggplot2)
ggplot() + # x= indep y= dep var
geom_point(aes(x = dataset$Level, y = dataset$Salary), # real points
colour = 'red') + # predict function
geom_line(aes(x = dataset$Level, y = predict(lin_reg, newdata = dataset)), # predicted
colour = 'blue') +
ggtitle('Truth or Bluff (Linear Regression)') +
xlab('Level') +
ylab('Salary')

# the linear regression line does not fit the real data points,
# this is clearly a polynomial problem
# predicted salaries are linear but real data points are polynomial
# real salary level 5= $125,000 vs predicted= $240,000
#-------------- Visualizing the Polynomial Regression results
#
library(ggplot2)
ggplot() +
geom_point(aes(x = dataset$Level, y = dataset$Salary), # real
colour = 'red') + # predict function, change to poly_reg
geom_line(aes(x = dataset$Level, y = predict(poly_reg, newdata = dataset)), # predicted
colour = 'blue') +
ggtitle('Truth or Bluff (Polynomial Regression)') +
xlab('Level') +
ylab('Salary')

# the predicted line fits better with the data points, curved line
#------------ Visualizing the Regression Model results
# (for higher resolution and smoother curve)
#
library(ggplot2)
x_grid = seq(min(dataset$Level), max(dataset$Level), 0.1)
ggplot() +
geom_point(aes(x = dataset$Level, y = dataset$Salary),
colour = 'red') +
geom_line(aes(x = x_grid, y = predict(poly_reg,
newdata = data.frame(Level = x_grid,
Level2 = x_grid^2,
Level3 = x_grid^3,
Level4 = x_grid^4))),
colour = 'blue') +
ggtitle('Truth or Bluff (Polynomial Regression)') +
xlab('Level') +
ylab('Salary')

# Predicting a new result with Linear Regression
# make a prediction on based on level 6.5
# make a new dataframe row
# y_pred.2 = predict(lin_reg, data.frame(Level = 6.5))
predict(lin_reg, data.frame(Level = 6.5))
1
330378.8
# Predicting a new result with Polynomial Regression
# add polynomial features for each level column
y_pred.3 = predict(poly_reg, data.frame(Level = 6.5,
Level2 = 6.5^2,
Level3 = 6.5^3,
Level4 = 6.5^4))
y_pred.3
1
158862.5
Classification
Logistic Regression
age and email action taken (Y/N | 1/0) correlation, {linear regression is not right model for this but shows a trend between variables}. Sigmoid function forces values 0 to 1, S-shaped curve on plot which makes the best fitting line for variables. Used for predicting probability (\(p^-\)) (p_hat)
Logistic Regression formula \(ln\)(p / 1 - p) = \(b_{0}\) + \(b_{1}*x\)
Example: on x-axis, 4 age column values at random, y-axis is p_hat, the s-curve on the plot. Person of age 20 has a p_hat probability value of 0.7% (p^- = 0.7) of taking action with an email. Person age 40 has p^- = 85% of taking action with an email. Any values below 50% the y_hat value is pushed down towards 0, any value above 50% is pushed up towards 1, so the results in a binary 0/1 outcome.
# Importing the dataset
dataset = read.csv('./Part 3 - Classification/Section 14 - Logistic Regression/R/Social_Network_Ads.csv')
dataset = dataset[3:5]
# Encoding the target feature as factor
dataset$Purchased = factor(dataset$Purchased, levels = c(0, 1))
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Purchased, SplitRatio = 0.75)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling is best practice for Logistic Regression
# [-3] is the last column of dataset
training_set[-3] = scale(training_set[-3])
test_set[-3] = scale(test_set[-3])
# Fitting Logistic Regression to the Training set
# glm (general linear model) builds the logistic regression
# predict the dependent variable of Purchased based on indep. variables: age, estimated salary
classifier = glm(formula = Purchased ~ .,
family = binomial,
data = training_set)
# Predicting the Test set results
prob_pred = predict(classifier,
type = 'response',
newdata = test_set[-3])
y_pred.logr = ifelse(prob_pred > 0.5, 1, 0)
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred.logr > 0.5)
cm
FALSE TRUE
0 57 7
1 10 26
prob_pred[1]
2
0.01623954
y_pred.logr[1]
2
0
the 1st probability predicted = 0.162 and the test_set 1st value is 0, this mean user #2 is unlikely to purchase. Using the y_pred.logr variable the model predicted that user #2 will not purchase an item.
the model correctly predicted a purchase (57+26) 83 times and 17 incorrect predictions
# Visualising the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
prob_set = predict(classifier, type = 'response', newdata = grid_set)
y_grid = ifelse(prob_set > 0.5, 1, 0)
plot(set[, -3],
main = 'Logistic Regression (Training set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

# Visualising the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
prob_set = predict(classifier, type = 'response', newdata = grid_set)
y_grid = ifelse(prob_set > 0.5, 1, 0)
plot(set[, -3],
main = 'Logistic Regression (Test set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

NA
NA
NA
Training set observation datapoints, red points are training observation where (dependent variable) Purchased=0 and the green points are training set observation where purchased=1. red zone is prediction region non-purchase. classifier predicted that the higher age the more estimated salary and to purchased item. Classifier is straight line for linear models. Focus on the dot color and the zone they fall.
K-Nearest Neighbor
KNN process:
- choose the number k of neighbors
- take the KNNs of the new data point, according to Euclidean distance
- among these KNNs, count the number of data points in each category
- assign the new data point to the category where you counted the most neighbors
- model is done
# Importing the dataset
dataset = read.csv('./Part 3 - Classification/Section 15 - K-Nearest Neighbors (K-NN)/R/Social_Network_Ads.csv')
dataset = dataset[3:5]
# Encoding the target feature as factor
dataset$Purchased = factor(dataset$Purchased, levels = c(0, 1))
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Purchased, SplitRatio = 0.75)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling
training_set[-3] = scale(training_set[-3])
test_set[-3] = scale(test_set[-3])
# Fitting classifier to the Training set
# Create your classifier here
# build a KNN classifier
library(class)
# fit a KNN to Training set and Predict the Test set
# remove last column of training set
y_pred.KNN = knn(train= training_set[,-3],
test= test_set[, -3],
cl= training_set[, 3],
k= 5)
# y_pred.KNN[1:5]
# Predicting the Test set results
# for KNN comment out
# y_pred = predict(classifier, newdata = test_set[-3])
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred.KNN)
cm
y_pred.KNN
0 1
0 59 5
1 6 30
#============ Visualizing the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
# y_grid = predict(classifier, newdata = grid_set)
# for KNN replace the predict and its arguments with KNN arguments
y_grid = knn(train= training_set[,-3],
test= grid_set, # replace test_set
cl= training_set[, 3],
k= 5)
plot(set[, -3],
main = 'KNN Classifier (Training set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

# Visualising the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
# for KNN replace the predict and its arguments with KNN arguments
y_grid = knn(train= training_set[,-3],
test= grid_set, # replace test_set
cl= training_set[, 3],
k= 5)
# y_grid = predict(classifier, newdata = grid_set)
plot(set[, -3], main = 'KNN Classifier (Test set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

NA
NA
NA
Support Vector Machines
Started in 1960s and 1990s. Separate datapoints on a plot and classify them. Goal: find best decision boundary using a max margin hyperplane line that has a max margin (distance away from line and distance between max margin lines) and datapoints outside the max margin lines are positive or negative hyperplane.
classify apples and oranges, training on best examples of each fruit
# Importing the dataset
dataset = read.csv('./Part 3 - Classification/Section 16 - Support Vector Machine (SVM)/R/Social_Network_Ads.csv')
dataset = dataset[3:5]
# Encoding the target feature as factor
dataset$Purchased = factor(dataset$Purchased, levels = c(0, 1))
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Purchased, SplitRatio = 0.75)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling
training_set[-3] = scale(training_set[-3])
test_set[-3] = scale(test_set[-3])
# Fitting classifier to the Training set
# Create your classifier here
# library e1071 for SVM
library(e1071)
# read the documentation
classifier.SVM = svm(formula= Purchased ~ .,
data= training_set,
type= 'C-classification', # classification
kernel= 'linear'
)
# Predicting the Test set results
y_pred.SVM = predict(classifier.SVM, newdata = test_set[-3])
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred.SVM)
cm
y_pred.SVM
0 1
0 57 7
1 13 23
#============= Visualizing the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier.SVM, newdata = grid_set)
plot(set[, -3],
main = 'SVM Classifier (Training set)',
xlab = 'Age',
ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

#============ Visualizing the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier.SVM,
newdata = grid_set)
plot(set[, -3], main = 'SVM Classifier (Test set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

NA
NA
mapping datapoints to a Higher dimensional shape separates points by mapping the points to a algebraic function then to have a hyperplane a separator, but this is very computer intensive and not practical.
Kernel SVM
Decision boundaries for when data points are clustered in circles and is not linear. Gaussian RBF Kernel.
# Importing the dataset
dataset = read.csv('./Part 3 - Classification/Section 17 - Kernel SVM/R/Social_Network_Ads.csv')
dataset = dataset[3:5]
# Encoding the target feature as factor
dataset$Purchased = factor(dataset$Purchased, levels = c(0, 1))
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Purchased, SplitRatio = 0.75)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling
training_set[-3] = scale(training_set[-3])
test_set[-3] = scale(test_set[-3])
# Fitting classifier to the Training set
# Create your classifier here
#=========== Kernel SVM
library(e1071)
# Gaussian classifier, radial
classifier.SVM = svm(formula= Purchased ~ .,
data = training_set,
type = "C-classification",
kernel= 'radial')
# Predicting the Test set results
y_pred = predict(classifier.SVM, newdata = test_set[-3])
y_pred[1:5]
2 4 5 9 12
0 0 0 0 0
Levels: 0 1
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred)
cm
y_pred
0 1
0 58 6
1 4 32
#============== Visualizing the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier.SVM, newdata = grid_set)
plot(set[, -3],
main = 'Kernel SVM Classifier (Training set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

#================== Visualizing the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier.SVM, newdata = grid_set)
plot(set[, -3], main = 'Kernel SVM Classifier (Test set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

Confusion matrix shows 10 incorrect results and 90 correct results
The SVM mapped data to a 3D plane, green zone is users who purchased item and red zone is no purchased item.
Naive Bayes Theorem Classification
Bayes Theorem of probability formula \(P(A|B)\) = \(P(B|A) * P(A) \div P(B)\)
Example: machine.1 makes 30 items/hr and machine.2 makes 20 items/hr. Out of all items made, 1 % is defective, and out of all defective items 50% came from machine.1 and 50% from machine.2. What is the probability that a item made by machine.2 is defective?
- P(Machine.2) = 20/50
- P(Defect) = 1%
- P(Machine.2 | Defect)= 50%
- P(Defect | Machine.2) = ?
P(Defect | Machine.2) = P(Machine.2 | Defect) * P(Defect) / P(Machine.2) P(Defect | Machine.2) = 0.5 * 0.01 / 0.4 == 0.0125 (1.25%)
Example 2 Naive Bayes (assumed independence of variables: x= age, y= salary, datapoints grouped: walks to work or drives to work. X= features P(Walks | X) = P(X | Walks) * P(Walks) / P(X)
P(Drives | X) = P(X | Drives) * P(Drives) / P(X)
repeat steps for both Walks and Drives class:
- prior probability: P(Walks)
- marginal likelihood: P(X)
- likelihood: P(X | Walks)
- posterior probability: P(Walks | X)
step 1: we have no data, so calculate the points in the walks group from a plot. - P(Walks) = number of walkers / total datapoints
step 2: select a radius on a plot, a circle to contain datapoints, look at features (age and salary) and these points will be similar. the probability of a new datapoint would fall into this radius. Count the number of points inside circle. - P(X) = number of observations / total observations
step 3: use the radius circle for similar features of datapoints, what is the likelihood of features of walkers given that person who walks (ignore the drivers). Count the number of datapoints for walkers inside the circle. - P(X | Walkers) = number of similar points for walkers / total number of walkers
step 4: P(Walks | X) = (3/10) * (10/30) / (4/30) == 0.75 (75% likelihood of datapoint being a Walker)
repeat for driver
step 4: P(Drives | X)= (1/20) * (20/30) / (4/30) = 0.25 (25%)
# Importing the dataset
dataset = read.csv('./Part 3 - Classification/Section 18 - Naive Bayes/R/Social_Network_Ads.csv')
dataset = dataset[3:5]
# Encoding the target feature as factor
dataset$Purchased = factor(dataset$Purchased, levels = c(0, 1))
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Purchased, SplitRatio = 0.75)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling
training_set[-3] = scale(training_set[-3])
test_set[-3] = scale(test_set[-3])
# Fitting classifier to the Training set
# Create your classifier here
# === Bayes Classifier
library(e1071)
# press F1 for documentation when mouse is on function name
classifier.Bayes = naiveBayes(x= training_set[-3],
y= training_set$Purchased)
# Predicting the Test set results
y_pred = predict(classifier.Bayes, newdata = test_set[-3])
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred)
#============== Visualizing the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier.Bayes, newdata = grid_set)
plot(set[, -3],
main = 'Naive Bayes Classifier (Training set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

#========== Visualizing the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier.Bayes, newdata = grid_set)
plot(set[, -3], main = 'Naive Bayes Classifier (Test set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

NA
NA
Decision Tree Classification

The Decision Tree Classifier first condition is age < 44.5 and salary < $90,000, our model classifies person as will not buy the item, if salary is > $90,000 person will buy the item . This was made by not running the code of feature scaling and visualizations, but the classifier and plotting function.
Random Forest Classification
Ensemble Learning = take multiple algorithms to make powerful algorithm
step 1: pick at random k data points from training set
step 2: build decision tree associated to these k data points
step 3: choose the number Ntree of trees you want to build and repeat steps 1 & 2
step 4: for a new data point, make each one of your Ntree trees predict the value of Y for the data point in question, and assign the new data point the average across all of the predicted Y values
This model is used for remote controller free gaming consoles (Microsoft connect)
# Importing the dataset
dataset = read.csv('./Part 3 - Classification/Section 20 - Random Forest Classification/R/Social_Network_Ads.csv')
dataset = dataset[3:5]
# Encoding the target feature as factor
dataset$Purchased = factor(dataset$Purchased, levels = c(0, 1))
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Purchased, SplitRatio = 0.75)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling
training_set[-3] = scale(training_set[-3])
test_set[-3] = scale(test_set[-3])
# Fitting Random Forest Classification to the Training set
# install.packages('randomForest')
library(randomForest)
randomForest 4.6-14
Type rfNews() to see new features/changes/bug fixes.
set.seed(123)
classifier = randomForest(x = training_set[-3],
y = training_set$Purchased,
ntree = 500)
# Predicting the Test set results
y_pred = predict(classifier, newdata = test_set[-3])
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred)
#========== Visualizing the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier, grid_set)
plot(set[, -3],
main = 'Random Forest Classification (Training set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

#======== Visualizing the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('Age', 'EstimatedSalary')
y_grid = predict(classifier, grid_set)
plot(set[, -3], main = 'Random Forest Classification (Test set)',
xlab = 'Age', ylab = 'Estimated Salary',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 1, 'springgreen3', 'tomato'))
points(set, pch = 21, bg = ifelse(set[, 3] == 1, 'green4', 'red3'))

# Choosing the number of trees
plot(classifier)

Association Rule
“people who bought X also bought Y”
Apriori algorithm
movie recommendation:
support(M) = # user watchlist containing M / # user watchlists
movie recommendation:
confidence(M1 -> M2) = # user watchlist containing M1, M2 / # user watchlists containing M1
movie recommendation:
lift(M1 -> M2) = confidence M1, M2 / support M2
market basket optimization:
support(J) = # transactions containing J / # transactions
market basket optimization:
confidence(J1 -> J2) = # transactions containing J1, J2 / # transactions containing J1
market basket optimization:
{what is the random likelihood that person likes this item? Lift is the improvement recommendation}
lift(J1 -> J2) = confidence J1, J2 / support J2
Process:
- set a min support and confidence
- take all the subsets in transactions having higher support than min support
- take all the rules of these subsets having higher confidence than min confidence
- sort the rules by decreasing lift, highest lift is the value you want
# Apriori
# Data Preprocessing
# install.packages('arules')
library(arules)
dataset = read.csv('./Part 5 - Association Rule Learning/Section 28 - Apriori/R/Market_Basket_Optimisation.csv', header = FALSE)
# sparse matrix
dataset = read.transactions('./Part 5 - Association Rule Learning/Section 28 - Apriori/R/Market_Basket_Optimisation.csv', sep = ',', rm.duplicates = TRUE)
distribution of transactions with duplicates:
1
5
summary(dataset)
transactions as itemMatrix in sparse format with
7501 rows (elements/itemsets/transactions) and
119 columns (items) and a density of 0.03288973
most frequent items:
mineral water eggs spaghetti french fries chocolate (Other)
1788 1348 1306 1282 1229 22405
element (itemset/transaction) length distribution:
sizes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 19 20
1754 1358 1044 816 667 493 391 324 259 139 102 67 40 22 17 4 1 2 1
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.000 2.000 3.000 3.914 5.000 20.000
includes extended item information - examples:
itemFrequencyPlot(dataset, topN = 30)

# Training Apriori on the dataset
# items bought 3x's a day divided by total products = {3*7/7500} = 0.028 => 0.003
# items bought 4x's a day divided by total products = {4*7/7500} = 0.0037 => 0.004
# confidence value is arbitrary choice
#
rules = apriori(data = dataset,
parameter = list(support = 0.004,
confidence = 0.2)) # use small values for more rules
Apriori
Parameter specification:
Algorithmic control:
Absolute minimum support count: 30
set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[119 item(s), 7501 transaction(s)] done [0.00s].
sorting and recoding items ... [114 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 4 done [0.00s].
writing ... [811 rule(s)] done [0.00s].
creating S4 object ... done [0.00s].
# Visualizing the results
# get the highest rules by lift
inspect(sort(rules, by = 'lift')[1:10])
NA
the rules show that people who bought {light cream} will buy {chicken} 29% [Confidence] of the cases with a lift value of 4.84
Eclat
this algorithm is similar as above, but it only has the support variable using sets
simple results of items commonly purchased together using the support parameter
# Data Preprocessing
# install.packages('arules')
library(arules)
dataset = read.csv('./Part 5 - Association Rule Learning/Section 29 - Eclat/R/Market_Basket_Optimisation.csv')
dataset = read.transactions('./Part 5 - Association Rule Learning/Section 29 - Eclat/R/Market_Basket_Optimisation.csv', sep = ',', rm.duplicates = TRUE)
distribution of transactions with duplicates:
1
5
summary(dataset)
transactions as itemMatrix in sparse format with
7501 rows (elements/itemsets/transactions) and
119 columns (items) and a density of 0.03288973
most frequent items:
mineral water eggs spaghetti french fries chocolate (Other)
1788 1348 1306 1282 1229 22405
element (itemset/transaction) length distribution:
sizes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 19 20
1754 1358 1044 816 667 493 391 324 259 139 102 67 40 22 17 4 1 2 1
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.000 2.000 3.000 3.914 5.000 20.000
includes extended item information - examples:
itemFrequencyPlot(dataset, topN = 10)

# Training Eclat on the dataset
rules = eclat(data = dataset, parameter = list(support = 0.003, minlen = 2))
Eclat
parameter specification:
algorithmic control:
Absolute minimum support count: 22
create itemset ...
set transactions ...[119 item(s), 7501 transaction(s)] done [0.01s].
sorting and recoding items ... [115 item(s)] done [0.00s].
creating sparse bit matrix ... [115 row(s), 7501 column(s)] done [0.00s].
writing ... [1328 set(s)] done [0.01s].
Creating S4 object ... done [0.00s].
# Visualising the results
inspect(sort(rules, by = 'support')[1:10])
NA
NA
End of section
Principal Component Analysis (PCA)
Unsupervised algorithm for : noise filtering, visualization, feature extraction, time series predictions, gene data analysis.
Goal: identify patterns in data, detect the correlation between variables by reducing the dimensions
# Importing the dataset
dataset = read.csv('./Part 9 - Dimensionality Reduction/Section 43 - Principal Component Analysis (PCA)/R/Wine.csv')
# Splitting the dataset into the Training set and Test set
# install.packages('caTools')
library(caTools)
set.seed(123)
split = sample.split(dataset$Customer_Segment, SplitRatio = 0.8)
training_set = subset(dataset, split == TRUE)
test_set = subset(dataset, split == FALSE)
# Feature Scaling
training_set[-14] = scale(training_set[-14])
test_set[-14] = scale(test_set[-14])
# Applying PCA
# install.packages('caret')
library(caret)
Loading required package: lattice
Loading required package: ggplot2
Attaching package: ‘ggplot2’
The following object is masked from ‘package:randomForest’:
margin
Registered S3 method overwritten by 'data.table':
method from
print.data.table
# install.packages('e1071')
library(e1071)
pca = preProcess(x = training_set[-14], method = 'pca', pcaComp = 2)
training_set = predict(pca, training_set)
training_set = training_set[c(2, 3, 1)]
test_set = predict(pca, test_set)
test_set = test_set[c(2, 3, 1)]
# Fitting SVM to the Training set
# install.packages('e1071')
library(e1071)
classifier = svm(formula = Customer_Segment ~ .,
data = training_set,
type = 'C-classification',
kernel = 'linear')
# Predicting the Test set results
y_pred = predict(classifier, newdata = test_set[-3])
# Making the Confusion Matrix
cm = table(test_set[, 3], y_pred)
# Visualising the Training set results
library(ElemStatLearn)
set = training_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('PC1', 'PC2')
y_grid = predict(classifier, newdata = grid_set)
plot(set[, -3],
main = 'SVM (Training set)',
xlab = 'PC1', ylab = 'PC2',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 2, 'deepskyblue', ifelse(y_grid == 1, 'springgreen3', 'tomato')))
points(set, pch = 21, bg = ifelse(set[, 3] == 2, 'blue3', ifelse(set[, 3] == 1, 'green4', 'red3')))

# Visualising the Test set results
library(ElemStatLearn)
set = test_set
X1 = seq(min(set[, 1]) - 1, max(set[, 1]) + 1, by = 0.01)
X2 = seq(min(set[, 2]) - 1, max(set[, 2]) + 1, by = 0.01)
grid_set = expand.grid(X1, X2)
colnames(grid_set) = c('PC1', 'PC2')
y_grid = predict(classifier, newdata = grid_set)
plot(set[, -3], main = 'SVM (Test set)',
xlab = 'PC1', ylab = 'PC2',
xlim = range(X1), ylim = range(X2))
contour(X1, X2, matrix(as.numeric(y_grid), length(X1), length(X2)), add = TRUE)
points(grid_set, pch = '.', col = ifelse(y_grid == 2, 'deepskyblue', ifelse(y_grid == 1, 'springgreen3', 'tomato')))
points(set, pch = 21, bg = ifelse(set[, 3] == 2, 'blue3', ifelse(set[, 3] == 1, 'green4', 'red3')))

NA
NA
LS0tCnRpdGxlOiAiKipMZWFybmluZyBSIE5vdGVib29rKioiCmF1dGhvcjogIlphbmUgRGF4IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKLS0tCjxzdHlsZT4KYm9keXtmb250LXNpemU6IDE2cHg7fQpoMSB7Y29sb3I6ICM5OTAwY2M7fQo8L3N0eWxlPgoKIyMjIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBieSAqKlVkZW15KioKCiMgc3RlcCAxIC0gaW1wb3J0IGRhdGFzZXQgCmBgZGF0YXNldCA9IHJlYWQuY3N2KCdEYXRhLmNzdicpYGAKCiMgc3RlcCAyIC0gbWlzc2luZyBkYXRhCk9mdGVuIGRhdGFzZXRzIGhhdmUgbWlzc2luZyBkYXRhLCBhbmQgdGhlIGNvbW1vbiBwcmFjdGljZSBpcyB0byByZW1vdmUgbWlzc2luZyBkYXRhIHJvd3MgYnV0IGl0IGhhcyBhIG5lZ2F0aXZlIGltcGFjdCBvbiBvYnNlcnZhdGlvbnMuIApCZXN0IHByYWN0aWNlIGlzIHRvIHRha2UgdGhlIG1lYW5zIG9mIGVhY2ggY29sdW1ucyB3aXRoIG1pc3NpbmcgZGF0YS4gCgotIHRvIHNlbGVjdCB0aGUgZGF0YWZyYW1lIGNvbHVtbnMsIHVzZSBgYCRgYCwgaWYtZWxzZSBmdW5jdGlvbgotIGBgaXMubmFgYCBpcyBhIGZ1bmN0aW9uIHRoYXQgY2hlY2tzIGZvciBOYU5zIGluIGNvbHVtbnMuCi0gdG8gZ2V0IHRoZSBhdmVyYWdlIHVzZSBgYGF2ZSgpYGAKLSBjcmVhdGUgYSBmdW5jdGlvbiwgaGVyZSBpdCdzIG5hbWVkIEZVTiwgdG8gZ2V0IHRoZSBtZWFuIG9mIGNvbHVtbiBBZ2UgYW5kIHJldHVybiBUcnVlCi0gZ2V0IHRoZSBhdmVyYWdlIG9mIGNvbHVtbiBTYWxhcnkgYW5kIGdldCB0aGUgZnVuY3Rpb24gdG8gY2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzIGFuZCByZXR1cm4gVHJ1ZQoKYGBge3J9CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDEgRGF0YSBQcmVwcm9jZXNzaW5nL1IvRGF0YS5jc3YnKQoKZGF0YXNldCRBZ2UgPSBpZmVsc2UoaXMubmEoZGF0YXNldCRBZ2UpLAogICAgICAgICAgICAgICAgICAgICBhdmUoZGF0YXNldCRBZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgbWVhbih4LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICAgICAgZGF0YXNldCRBZ2UpICMgcmV0dXJuIGNvbHVtbgogICAgICAgICAgICAgICAgCmRhdGFzZXQkU2FsYXJ5ID0gaWZlbHNlKGlzLm5hKGRhdGFzZXQkU2FsYXJ5KSwKICAgICAgICAgICAgICAgICAgICAgICAgYXZlKGRhdGFzZXQkU2FsYXJ5LCBGVU4gPSBmdW5jdGlvbih4KSBtZWFuKHgsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0JFNhbGFyeSkKZGF0YXNldCAgICAgICAgICAgICAgICAgICAgICAgCmBgYCAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgCkNhdGVnb3JpY2FsIGRhdGEKTmVlZCB0byBlbmNvZGUgY2F0ZWdvcmljYWwgZGF0YSBpbnRvIG51bWVyaWNhbCBkYXRhIHR5cGUKCi0gZm9yIHRoaXMgZGF0YXNldCBDb3VudHJ5IGlzIGNhdGVnb3JpY2FsLCBuZWVkIHRvIGVuY29kZQoKIyBTdGVwIDMgLSBFbmNvZGluZyBjYXRlZ29yaWNhbCBkYXRhCk5lZWQgdGhlIGBgZmFjdG9yKClgYCBmdW5jdGlvbiB3aGljaCB0YWtlcyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIHN0b3JlcyBpdCBpbiBsZXZlbHMgKGhlbHBzIHJlZHVjZSByZWR1bmRhbmN5IGFuZCBDUFUgbWVtb3J5KS4gUmVxdWlyZXMgYGBjKClgYCB2ZWN0b3JzLiAKCkV4YW1wbGU6CgotIGBgY29mZmVlLnNpemUgPSBjKCJTbWFsbCIsIk1lZGl1bSIsIkxhcmdlIiwiWExhcmdlIilgYAotIGBgY29mZmVlLnNpemUuZmFjdG9yID0gZmFjdG9yKGNvZmZlZS5zaXplKWBgCi0gYGBjb2ZmZWUuc2l6ZS5mYWN0b3IgPSBmYWN0b3IobGV2ZWxzPSBjb2ZmZWUuc2l6ZSwgb3JkZXJlZCA9IFQsIGxhYmVscyA9IGMoMSwyLDMsNCkpYGAKLSBgYGNvZmZlZS5zaXplLmZhY3RvcmBgCgoKYGBge3IgZmFjdG9yIGNhdGVnb3JpY2FsIGRhdGF9CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDEgRGF0YSBQcmVwcm9jZXNzaW5nL1IvRGF0YS5jc3YnKQoKIyB3YW50IHRvIGNvbnZlcnQgY291bnRyaWVzIHRvIG51bWJlciByZXByZXNlbnRhdGlvbiAKZGF0YXNldCRDb3VudHJ5ID0gZmFjdG9yKGRhdGFzZXQkQ291bnRyeSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJ0ZyYW5jZScsICdTcGFpbicsICdHZXJtYW55JyksCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKDEsIDIsIDMpKQoKZGF0YXNldCRQdXJjaGFzZWQgPSBmYWN0b3IoZGF0YXNldCRQdXJjaGFzZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJ05vJywgJ1llcycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKDAsIDEpKQpkYXRhc2V0ICAgICAgICAgICAgICAgICAgIApgYGAKCiMgc3RlcCA0IC0gVHJhaW4vVGVzdCBzcGxpdCBvZiBkYXRhCk5lZWQgdG8gaW1wb3J0IGEgbGlicmFyeSB0aGF0IG1ha2VzIFRyYWluIFRlc3Qgc3BsaXQgZWFzeS4KCmBgYHtyIHRyYWluLXRlc3Qtc3BsaXR9CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDEgRGF0YSBQcmVwcm9jZXNzaW5nL1IvRGF0YS5jc3YnKQoKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKCiMgbG9hZCBsaWJyYXJ5CmxpYnJhcnkoY2FUb29scykKCnNldC5zZWVkKDEyMykKCiMtLS0tLS0gVHJhaW4gLyBUZXN0IHNwbGl0CiMgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQkRGVwZW5kZW50VmFyaWFibGUKc3BsaXQgPSBzYW1wbGUuc3BsaXQoZGF0YXNldCRQdXJjaGFzZWQsIAogICAgICAgICAgICAgICAgICAgICBTcGxpdFJhdGlvID0gMC44KSAjIHJldHVybnMgVC9GLCAKCiMgVHJ1ZSA9PiBUcmFpbmluZyBzZXQsIEZhbHNlID0+IFRlc3Qgc2V0CnRyYWluaW5nX3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBUUlVFKQp0ZXN0X3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBGQUxTRSkKCmRhdGFzZXQKYGBgICAgICAgICAgICAgICAgICAgICAgICAKCgojIHN0ZXAgNSAtIEZlYXR1cmUgU2NhbGluZwpFdWNsaWRlYW4gRGlzdGFuY2UgYmV0d2VlbiBjb29yZGluYXRlcyA9PSB4IGFuZCB5IHZhbHVlcywgd2hlbiBvbmUgdmFyaWFibGVzIGhhcyBtb3JlIHZhcmlhbmNlIGl0IGJlY29tZXMgZG9taW5hbnQgaW4gZGF0YSB3aGVuIHRoZXkgYXJlIHNxdWFyZWQuIFRoaXMgaXMgd2h5IHZhcmlhYmxlcyBuZWVkIHRvIGJlIGluIHRoZSBzYW1lIHNjYWxlIHZhbHVlIHJhbmdlLgoKVHdvIEZlYXR1cmUgU2NhbGluZyBtZXRob2RzOgoKLSAqKlN0YW5kYXJkaXphdGlvbioqIDogJFhfe3N0YW5kfSQgPSAkeCQgLSAgbWVhbigkeCQpICRcZGl2JCBzdGQoJHgkKQotICoqTm9ybWFsaXphdGlvbioqIDogJFhfe25vcm19JCA9ICR4JCAtICBtaW4oJHgkKSAkXGRpdiQgbWF4KCR4JCkgLSBtaW4oJHgkKSAgICAgICAgICAgICAgICAgICAKCk5vdCBhbGwgUiBsaWJyYXJpZXMgcmVxdWlyZSBmZWF0dXJlIHNjYWxpbmcgdG8gYmUgZG9uZSBiZWZvcmVoYW5kLCBzb21lIGRlYWwgd2l0aCBpdCBmb3IgeW91LgoKYGBge3IgRmVhdHVyZSBTY2FsaW5nfSAgICAKZGF0YXNldCA9IHJlYWQuY3N2KCcuL1BhcnQgMSBEYXRhIFByZXByb2Nlc3NpbmcvUi9EYXRhLmNzdicpCiMtLS0tLS0gRmVhdHVyZSBTY2FsaW5nCiMgd2F0Y2ggb3V0IGZvciBlcnJvcnMgZnJvbSBjYXRlZ29yaWNhbCBmYWN0b3JzLCBmYWN0b3JzIGFyZSBub3QgbnVtZXJpY2FsCiMgbmVlZCB0byBleGNsdWRlIGNhdGVnb3JpY2FsIGNvbHVtbnMKIyBncmFiIGp1c3QgdGhlIG51bWVyaWNhbCwgdXNlIHNsaWNpbmcgZm9yIGNvbHVtbnMgMiBhbmQgMyBBZ2UgYW5kIFNhbGFyeQojIHNsaWNlIG5vdGF0aW9uIFssIDI6M10KCnRyYWluaW5nX3NldFssIDI6M10gPSBzY2FsZSh0cmFpbmluZ19zZXRbLCAyOjNdKQp0ZXN0X3NldFssIDI6M10gPSBzY2FsZSh0ZXN0X3NldFssIDI6M10pICAgCgoKCmBgYCAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgCkVuZCBvZiBEYXRhIFByZXByb2Nlc3NpbmcKCjxocj4KCiMgU2ltcGxlICoqTGluZWFyIFJlZ3Jlc3Npb24qKgooZGVwZW5kZW50IHZhcikgPSAoY29uc3RhbnQpICsgKGNvZWZmaWNpZW50LCB1bml0IGNoYW5nZXMpICogKEluZGVwLiB2YXIpCgpUaGUgZm9ybXVsYTogKip5KiogPSAkYl97MH0kICsgJGJfezF9JCAgKiAkeF97MX0kIAoKRXhhbXBsZToKCi0gRXhwZXJpZW5jZSAoeCkKLSBTYWxhcnkgKHkpCi0gUXVlcnk6IGRvZXMgc2FsYXJ5IGZhY3RvciBpbiB3aXRoIGV4cGVyaWVuY2UgPwotIFNhbGFyeSA9IChjb25zdGFudCkgKyAoY29lZmZpY2llbnQpICogRXhwZXJpZW5jZSAgICAKLSBwbG90IGl0LCB0aGUgY29uc3RhbnQgb2YgMCBzYXlzIHRoYXQgcGVyc29uIHdpdGggMCBleHBlcmllbmNlIHdpbGwgaGF2ZSBzYWxhcnkgb2YgJDMwaywgdGhlIGNvZWZmaWNpZW50IHNob3dzIHRoYXQgMSB5ZWFyIG9mIGV4cGVyaWVuY2UgaW5jcmVhc2VzIHNhbGFyeSBieSAkMTBrCgpGaW5kIHRoZSBsaW5lIG9mIGJlc3QgZml0IGlzICoqT3JkaW5hcnkgTGVhc3QgU3F1YXJlcyoqCgotIG9uIHRoZSBwbG90LCB0aGUgZG90IGFib3ZlIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBsaW5lIGlzIHdoZXJlIHBlcnNvbiBpcyB3aXRoIHNhbGFyeSBhbmQgZXhwZXJpZW5jZSAoJHlfe2l9JCkgYW5kIHRoZSBsaW5lIGlzIHdoZXJlIHRoZSBtb2RlbCBzYXlzIHdoZXJlIHRoZSBwZXJzb24gc2hvdWxkIGJlIGluIHJlZ2FyZHMgdG8gc2FsYXJ5ICgkeV57LX1fe2l9JCkgLSB0aGUgbW9kZWwgZXZhbHVhdGlvbiB2YWx1ZQotIHN1bSggJHlfe2l9JCAtICR5XnstfV97aX0kKV4yLCB0aGVuIGZpbmQgdGhlIG1pbmltdW0gdmFsdWUgCgpGaXR0aW5nIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiB0byB0aGUgVHJhaW5pbmcgc2V0CgotIG11c3QgdW5kZXJzdGFuZCB3aGljaCBpcyB0aGUgZGVwZW5kZW50IHZhciBhbmQgd2hpY2ggaXMgdGhlIGluZGVwLiB2YXJpYWJsZQotIGluZGVwLiB2YXIgPSBleHBlcmllbmNlIGluIHllYXJzCi0gZGVwLiB2YXIgPSBzYWxhcnkgaW4gJAoKYGBge3IgTGluZWFyIFJlZ3Jlc3Npb259CiMgc3RlcCAxCmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gNCAtIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbi9SL1NhbGFyeV9EYXRhLmNzdicpCgojIHN0ZXAgMywgNApzcGxpdCA9IHNhbXBsZS5zcGxpdChkYXRhc2V0JFNhbGFyeSwgU3BsaXRSYXRpbyA9IDIvMykKdHJhaW5pbmdfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IFRSVUUpCnRlc3Rfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IEZBTFNFKQoKIyBubyBmZWF0dXJlIHNjYWxpbmcgaXMgcmVxdWlyZWQgd2l0aCB0aGlzIGNhVG9vbHMgbGlicmFyeQoKIy0tLS0tLS0tIExpbmVhciBSZWdyZXNzaW9uIGxtKCkgCiMgdGhlICd+JyBtZWFucyAiYXMgYSBmdW5jdGlvbiBvZiIKIyB+IHNlcGFyYXRlcyBkZXBlbmRlbnQgdmFyaWFibGUgZnJvbSB0aGUgaW5kZXAuIHZhcgojIChkZXAuIHZhcikgfiAoaW5kZXAuIHZhcikKIyBob3cgaXQgd2lsbCBiZSBwbG90dGVkOiAoeS1heGlzIHZhcikgfiAoeC1heGlzIHZhcikKcmVncmVzc29yID0gbG0oZm9ybXVsYSA9IFNhbGFyeSB+IFllYXJzRXhwZXJpZW5jZSwKICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX3NldCkKCiMtLS0tIFByZWRpY3RpbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKIyBwcmVkaWN0IHNhbGFyaWVzIGJhc2VkIG9uIFllYXJzIEV4cGVyaWVuY2UKeV9wcmVkID0gcHJlZGljdChyZWdyZXNzb3IsIG5ld2RhdGEgPSB0ZXN0X3NldCkKCnN1bW1hcnkocmVncmVzc29yKQpgYGAKICAgICAgICAgICAgICAgICAgICAgICAgCnRoZSBgYHN1bW1hcnkocmVncmVzc29yKWBgIHNob3dzIHRoYXQgaW4gdGhlIGNvZWZmaWNpZW50cyBzZWN0aW9uIHRoYXQgdGhlIGBgWWVhcnNFeHBlcmllbmNlYGAgaGFzIDMgc3RhcnMsIG1lYW5pbmcgaXQgaXMgaGlnaGx5IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIFRoZSBsb3dlciB0aGUgcC12YWx1ZSBtZWFucyBpdCBpcyBtb3JlIHNpZ25pZmljYW50LCB3aGVuIHAgPCAwLjA1ID09IGluZGVwLiB2YXIgKGBgWWVhcnNFeHBlcmllbmNlYGApIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCgpMb29rIGF0IHdoYXQgdGhlIHNhbGFyeSBpcyBmb3IgZmlyc3Qgcm93IGl0ZW0gYW5kIGNvbXBhcmUgd2l0aCBwcmVkaWN0ZWQgdmFsdWU6IAoKLSBTYWxhcnlfRGF0YSA6IDEuMyBZZWFyc0V4cGVyaWVuY2UsIFNhbGFyeSA0NjIwNQotIExpbmVhciBNb2RlbCBwcmVkaWN0aW9uOiBTYWxhcnkgMzc3NjYKCgoKIyMgVmlzdWFsaXNpbmcgdGhlIFRyYWluaW5nIHNldCByZXN1bHRzCgpgYGB7cn0KIyBzaGlmdCArIENtZCArIGMgICBpcyBzaG9ydGN1dCBmb3IgY29tbWVudCBsaW5lCmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gNCAtIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbi9SL1NhbGFyeV9EYXRhLmNzdicpCgpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdHJhaW5pbmdfc2V0JFllYXJzRXhwZXJpZW5jZSwgIyBtYWtlIHN1cmUgeW91IHNlbGVjdCBjb3JyZWN0IGRhdGFzZXQhCiAgICAgICAgICAgICAgICAgeSA9IHRyYWluaW5nX3NldCRTYWxhcnkpLAogICAgICAgICAgICAgY29sb3VyID0gJ3JlZCcpICsgCiAgZ2VvbV9saW5lKGFlcyh4ID0gdHJhaW5pbmdfc2V0JFllYXJzRXhwZXJpZW5jZSwgCiAgICAgICAgICAgICAgICB5ID0gcHJlZGljdChyZWdyZXNzb3IsIG5ld2RhdGEgPSB0cmFpbmluZ19zZXQpKSwKICAgICAgICAgICAgY29sb3VyID0gJ2JsdWUnKSArCiAgZ2d0aXRsZSgnU2FsYXJ5IHZzIEV4cGVyaWVuY2UgKFRyYWluaW5nIHNldCknKSArCiAgeGxhYignWWVhcnMgb2YgZXhwZXJpZW5jZScpICsKICB5bGFiKCdTYWxhcnknKQoKIyBSZWQgZG90cyBhcmUgdGhlIFJlYWwgZGF0YQojIGJsdWUgbGluZSBpcyB0aGUgbW9kZWwgcHJlZGljdGlvbgpgYGAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAKIyMgVmlzdWFsaXNpbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKYGBge3J9CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gNCAtIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbi9SL1NhbGFyeV9EYXRhLmNzdicpCgpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdGVzdF9zZXQkWWVhcnNFeHBlcmllbmNlLCAKICAgICAgICAgICAgICAgICB5ID0gdGVzdF9zZXQkU2FsYXJ5KSwKICAgICAgICAgICAgIGNvbG91ciA9ICdyZWQnKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gdHJhaW5pbmdfc2V0JFllYXJzRXhwZXJpZW5jZSwgCiAgICAgICAgICAgICAgICB5ID0gcHJlZGljdChyZWdyZXNzb3IsIG5ld2RhdGEgPSB0cmFpbmluZ19zZXQpKSwKICAgICAgICAgICAgY29sb3VyID0gJ2JsdWUnKSArCiAgZ2d0aXRsZSgnU2FsYXJ5IHZzIEV4cGVyaWVuY2UgKFRlc3Qgc2V0KScpICsKICB4bGFiKCdZZWFycyBvZiBleHBlcmllbmNlJykgKwogIHlsYWIoJ1NhbGFyeScpICAgICAgICAgICAKCmBgYCAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAKCiMgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gSW50dWl0aW9uClRoZSBmb3JtdWxhIGlzOgoKLSBkZXBlbmRlbnQgdmFyICh5KSwgY29uc3RhbnQgKGIpLCBpbmRlcC4gdmFycyAoYjEsIGIyLCAuLi4pCi0gKip5KiogPSAkYl97MH0kICsgJGJfezF9ICogeF97MX0kICsgJGJfezJ9ICogeF97Mn0kICsgLi4uCgpMaW5lYXIgUmVncmVzc2lvbiBBc3N1bXB0aW9uczoKCi0gbGluZWFyaXR5Ci0gaG9tb3NjZWRhc3RpY2l0eQotIG11bHRpdmFyaWF0ZSBub3JtYWxpdHkKLSBpbmRlcGVuZGVuY2Ugb2YgZXJyb3JzCi0gbGFjayBvZiBtdWx0aWNvbGxpbmVhcml0eQoKVXNpbmcgdGhlIGRhdGFzZXQgYGA1MF9TdGFydHVwc2BgCgotIGZvciB0aGUgY29sdW1uIFN0YXRlLCBpdCBoYXMgY2F0ZWdvcmljYWwgZGF0YSwgc28gbmVlZCB0byBnZXQgZHVtbXkgdmFyaWFibGVzIHRvIHJlcGxhY2Ugc3RyaW5ncyBmb3IgaW50ZWdlciB2YWx1ZXMuIAotIE1ha2UgbmV3IGNvbHVtbnMgZm9yIGVhY2ggY2F0ZWdvcnksIGZvciBOZXcgWW9yayBjb2x1bW4sIGV2ZXJ5IE5ldyBZb3JrIHZhbHVlIGdldHMgMS4gCi0gKHByb2ZpdCkgPSBjb25zdGFudCArIChSJkQgU3BlbmQpICogKFImRCBTcGVuZCB2YWx1ZXMpICsgKEFkbWluKSAqIChBZG1pbiB2YWx1ZXMpICsgKE1hcmtldGluZykgKiAoTWFya2V0aW5nIHZhbHVlcykgKyAoZHVtbXlfdmFyKQotICoqV0FSTklORyoqOiBhbHdheXMgb21pdCAxIGR1bW15IHZhcmlhYmxlIHJlZ2FyZGxlc3MgbnVtYmVyIG9mIGR1bW15IHZhcmlhYmxlcy4gRG8gbm90IGluY2x1ZGUgbW9yZSB0aGFuIDEgZHVtbXkgdmFyaWFibGUKCiMjIFAtdmFsdWUKSXMgdGhlIHJlc3VsdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50PyAKCi0gJEhfezB9JCA9IE51bGwgaHlwb3RoZXNpcyAoZGVmYXVsdCkKLSAkSF97MX0kID0gQWx0IGh5cG90aGVzaXMKCkNvaW4gZmxpcDogCgp8W0ggb3IgVF0gfCAjIGZsaXBzIHwgUHJvYmFiaWxpdHkgfAp8LS0tLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfAp8IFQgICAgICAgfCAxICAgICAgIHwgMC41MCB8CnwgVCAgICAgICB8IDIgICAgICAgfCAwLjI1IHwKfCBUICAgICAgIHwgMyAgICAgICB8IDAuMTIgfAp8IFQgICAgICAgfCA0ICAgICAgIHwgMC4wNiAgICoqcCA8IDAuMDUgPSBTaWduaWZpY2FudCoqfAp8IFQgICAgICAgfCA1ICAgICAgIHwgMC4wMyB8CnwgVCAgICAgICB8IDYgICAgICAgfCAwLjAxIHwKCiAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAKIyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCBNZXRob2RzCgotIHN0ZXAgMDogc2VsZWN0IHdoYXQgY29sdW1ucyB0byBpbmNsdWRlIHtnYXJiYWdlIGluID0gZ2FyYmFnZSBvdXR9CiAgYW5kIHlvdSB3aWxsIG5lZWQgdG8gYmUgYWJsZSB0byBleHBsYWluIHlvdXIgbW9kZWwsIHNvIGtlZXAgd2hhdCBpcyBlc3NlbnRpYWwKLSAqKjUgTWV0aG9kcyBvZiBidWlsZGluZyBtb2RlbHMqKiA6CgogIDEuIGFsbC1pbiA9IHB1dHRpbmcgaW4gYWxsIHZhcmlhYmxlcyB7ZG9uJ3QgZG8gdW5sZXNzIHlvdSBuZWVkIHRvfQogIDIuICoqYmFja3dhcmQgZWxpbWluYXRpb24qKiA6CiAgCiAgICAtIDIuMS4gc2VsZWN0IGEgc2lnbmlmaWNhbmNlIGxldmVsICgkXGFscGhhJCA9IDAuMDUpCiAgICAtIDIuMi4gZml0IHRoZSBmdWxsIG1vZGVsIHdpdGggYWxsIHBvc3NpYmxlIHByZWRpY3RvcnMKICAgIC0gMi4zLiBjb25zaWRlciB0aGUgcHJlZGljdG9yIHdpdGggaGlnaGVzdCBwLXZhbHVlLCBpZiBwID4gJFxhbHBoYSQgdGhlbiBnbyB0byBzdGVwIDIuNCwgZWxzZTogYnJlYWssIG1vZGVsIGlzIHJlYWR5CiAgICAtIDIuNCByZW1vdmUgdGhlIHByZWRpY3RvcgogICAgLSAyLjUgZml0IHRoZSBtb2RlbCB3aXRob3V0IHRoaXMgdmFyaWFibGUsIGdvIHRvIHN0ZXAgMi4zCiAgICAKICAzLiAqKmZvcndhcmQgc2VsZWN0aW9uKiogOgogIAogICAgLSAzLjEgc2VsZWN0IGEgc2lnbmlmaWNhbmNlIGxldmVsICgkXGFscGhhJCA9IDAuMDUpCiAgICAtIDMuMiBmaXQgYWxsIHNpbXBsZSByZWdyZXNzaW9uIG1vZGVscyB5IH4gJHhfe259JCwgc2VsZWN0IHRoZSBvbmUgd2l0aCBsb3dlc3QgcC12YWx1ZQogICAgLSAzLjMga2VlcCB0aGlzIHZhcmlhYmxlIGFuZCBmaXQgYWxsIHBvc3NpYmxlIG1vZGVscyB3aXRoIG9uZSBleHRyYSBwcmVkaWN0b3IgYXNzZWQgdG8gdGhlIG9uZShzKSB5b3UgYWxyZWFkeSBoYXZlCiAgICAtIDMuNCBjb25zaWRlciB0aGUgcHJlZGljdG9yIHdpdGggbG93ZXN0IHAtdmFsdWUsIGlmIHAgPCAkXGFscGhhJCBnbyB0byBzdGVwIDMuMywgZWxzZTogYnJlYWssIG1vZGVsIGlzIHJlYWR5IChrZWVwIHByZXZpb3VzIG1vZGVsKQogICAgCiAgNC4gKipiaWRpcmVjdGlvbmFsIGVsaW1pbmF0aW9uIChzdGVwd2lzZSByZWdyZXNzaW9uKSoqOgogIAogICAgLSA0LjEgc2VsZWN0IHNpZ25pZmljYW5jZSBsZXZlbCB0byBlbnRlciBhbmQgc3RheSBpbiB0aGUgbW9kZWwgJFxhbHBoYSQgPSAwLjA1CiAgICAtIDQuMiAgZG8gZm9yd2FyZCBzZWxlY3Rpb24sIG5ldyB2YXJpYWJsZXMgbXVzdCBiZSBwIDwgJFxhbHBoYSQgdG8gZW50ZXIKICAgIC0gNC4zIGRvIGFsbCBzdGVwcyBvZiBiYWNrd2FyZCBlbGltaW5hdGlvbiwgb2xkIHZhcmlhYmxlcyBtdXN0IGJlIHAgPCAkXGFscGhhJCB0byBzdGF5CiAgICAtIDQuNCBubyBuZXcgdmFyaWFibGVzIGNhbiBlbnRlciBhbmQgbm8gb2xkIHZhcmlhYmxlcyBjYW4gZXhpdCwgbW9kZWwgaXMgcmVhZHkKICAKICA1LiAqKnNjb3JlIGNvbXBhcmlzb24qKiA6CiAgCiAgICAtIDUuMSBzZWxlY3QgYSBjcml0ZXJpb24gb2YgZ29vZG5lc3Mgb2YgZml0IChBa2Fpa2UgY3JpdGVyaW9uKQogICAgLSA1LjIgY29uc3RydWN0IGFsbCBwb3NzaWJsZSByZWdyZXNzaW9uIG1vZGVscyAKICAgIC0gNS4zIHNlbGVjdCBvbmUgb2YgdGhlIGJlc3QgY3JpdGVyaW9uLCB5b3VyIG1vZGVsIGlzIHJlYWR5IAogICAgCiAgICAqRXhhbXBsZTogMTAgY29sdW1ucyBtZWFucyA9PSAxMDIzIG1vZGVscyoKCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAKIyAqKk11bHRpcGxlKiogTGluZWFyIFJlZ3Jlc3Npb24gICAgICAgICAgICAgICAgICAgICAKCmBgYHtyIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9ufSAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gNSAtIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uL1IvNTBfU3RhcnR1cHMuY3N2JykKCiMgRW5jb2RpbmcgY2F0ZWdvcmljYWwgZGF0YQpkYXRhc2V0JFN0YXRlID0gZmFjdG9yKGRhdGFzZXQkU3RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygnTmV3IFlvcmsnLCAnQ2FsaWZvcm5pYScsICdGbG9yaWRhJyksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygxLCAyLCAzKSkgIyBlbmNvZGluZyAKCiMgU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdGhlIFRyYWluaW5nIHNldCBhbmQgVGVzdCBzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKbGlicmFyeShjYVRvb2xzKQoKc2V0LnNlZWQoMTIzKQoKc3BsaXQgPSBzYW1wbGUuc3BsaXQoZGF0YXNldCRQcm9maXQsIFNwbGl0UmF0aW8gPSAwLjgpCgojIC0tLS0gVHJhaW4vVGVzdCBzcGxpdAp0cmFpbmluZ19zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gVFJVRSkKdGVzdF9zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gRkFMU0UpICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAKIy0tLS0tLSBjYVRvb2xzIGxpYnJhcnkgZG9lcyBub3QgbmVlZCBmZWF0dXJlIHNjYWxpbmcKIyBGZWF0dXJlIFNjYWxpbmcKIyB0cmFpbmluZ19zZXQgPSBzY2FsZSh0cmFpbmluZ19zZXQpCiMgdGVzdF9zZXQgPSBzY2FsZSh0ZXN0X3NldCkKIyAtLS0tLS0KCiMtLS0tIEZpdHRpbmcgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gdG8gdGhlIFRyYWluaW5nIHNldAoKIyAocHJvZml0KSB+IChjb2x1bW4xICsgY29sdW1uMiArIGNvbHVtbjMgKyBjb2x1bW40KQojIDw8IHNob3J0Y3V0ID4+IGlzICcuJwojIChwcm9maXQpIH4gKGFsbCBvdGhlciBjb2x1bW5zKQojIChwcm9maXQpIH4gLgpyZWdyZXNzb3IgPSBsbShmb3JtdWxhID0gUHJvZml0IH4gLiwKICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX3NldCkKCgojLS0tLSBQcmVkaWN0aW5nIHRoZSBUZXN0IHNldCByZXN1bHRzCnlfcHJlZCA9IHByZWRpY3QocmVncmVzc29yLCAKICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9zZXQpICAgICAgICAgICAgICAgICAgIAoKIyBzZWUgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyAKc3VtbWFyeShyZWdyZXNzb3IpCgojIHNlZSB0aGUgcHJlZGljdGlvbnMKeV9wcmVkCmBgYCAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgIApUaGUgc3VtbWFyeSBzdGF0aXN0aWNzIGhhdmUgc3RhdGUyIGFuZCBzdGF0ZTMgd2hpY2ggYXJlIGR1bW15IHZhcmlhYmxlcyBSIGNyZWF0ZWQgYmFzZWQgb24gdGhlIGZhY3RvcnMgZW5jb2RlZCBhYm92ZS4gVGhlIHAtdmFsdWUgc2hvdyB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIHZhbHVlIG9mIGluZGVwZW5kZW50IHZhcmlhYmxlcy4KTG93ZXIgdGhlIHAtdmFsdWUgdGhlIG1vcmUgc2lnbmlmaWNhbnQuIAoKRm9yIHRoZSBkYXRhIGFib3ZlLCBSJkQgU3BlbmRpbmcgaGFzIGEgc3Ryb25nIHN0YXRpc3RpY2FsIHNpZ25pZmljYW50ICgzIHN0YXJzICoqKikgZWZmZWN0IG9uIHByb2ZpdHMgKGRlcGVuZGVudCB2YXJpYWJsZSkuIFRha2Vhd2F5OiBMb29rIGF0IGNvbXBhbmllcyB0aGF0IHNwZW5kIG1vbmV5IG9uIFImRCBhcyBpdCBwbGF5cyBiaWdnZXN0IHJvbGUgb24gcHJvZml0cy4gCgpUaGUgcmVhbCBkYXRhc2V0IHByb2ZpdHMgdnMgcHJlZGljdGVkIHByb2ZpdHM6IDE4MjkwMS45OSAocm93IDQpIHZzIHByZWRpY3RlZCAxNzM5ODEuMDkgKHJvdyA0KQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAKIyMgQmFja3dhcmQgRWxpbWluYXRpb24gbW9kZWwKcmV1c2UgdGhlIGNvZGUgYWJvdmUKCmBgYHtyIEJhY2t3YXJkIEVsaW19CgpkYXRhc2V0ID0gcmVhZC5jc3YoJy4vUGFydCAyIC0gUmVncmVzc2lvbi9TZWN0aW9uIDUgLSBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbi9SLzUwX1N0YXJ0dXBzLmNzdicpCgoKIyBFbmNvZGluZyBjYXRlZ29yaWNhbCBkYXRhCmRhdGFzZXQkU3RhdGUgPSBmYWN0b3IoZGF0YXNldCRTdGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdOZXcgWW9yaycsICdDYWxpZm9ybmlhJywgJ0Zsb3JpZGEnKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKDEsIDIsIDMpKSAjIGVuY29kaW5nIAoKc2V0LnNlZWQoMTIzKQoKc3BsaXQgPSBzYW1wbGUuc3BsaXQoZGF0YXNldCRQcm9maXQsIFNwbGl0UmF0aW8gPSAwLjgpCgojIC0tLS0gVHJhaW4vVGVzdCBzcGxpdAp0cmFpbmluZ19zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gVFJVRSkKdGVzdF9zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gRkFMU0UpICAgCgoKIyBHb2FsOiBuZWVkIHRvIHJlbW92ZSB0aGUgbm9uLXN0YXQgc2lnbmlmIGluZGVwIHZhcmlhYmxlcywgZGVsZXRlIHRoZSAuLCAKIyAgICAgICBpdGVyYXRlIHRocm91Z2ggc3RlcHMgYW5kIHJlbW92ZSBjb2x1bW5zIChpbmRlcCB2YXJpYWJsZXMpCiMgY29sdW1ucyB3aXRoIHNwYWNlcyA9PiB1c2UgZG90cyAKCiMgIG9yaWdpbmFsOgojICAgICBQcm9maXQgfiBSJkQuU3BlbmQgKyBBZG1pbmlzdHJhdGlvbiArIE1hcmtldGluZy5TcGVuZCArIFN0YXRlCgojIHN0ZXAgMSAgYWxwaGEgPSAwLjA1CiMgc3RlcCAyCnJlZ3Jlc3NvciA9IGxtKGZvcm11bGEgPSBQcm9maXQgfiBSLkQuU3BlbmQgKyBNYXJrZXRpbmcuU3BlbmQsCiAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhc2V0KQoKIyBzdGVwIDMKIyBmaW5kIHRoZSB2YWx1ZXMgd2l0aCBoaWdoZXN0IHAtdmFsdWVzLCBsb29rIGZvciB0aGUgc3RhcnMgdW5kZXIgUHIoPnx0fCkKc3VtbWFyeShyZWdyZXNzb3IpCgojIHN0ZXAgNCByZW1vdmUgdGhlIHByZWRpY3RvciAodmFyaWFibGVzIGFmdGVyIHRoZSB+KSwgc3RlcCAzCiMgc3RlcCAzOiAKIyAgICAgICBTdGF0ZTIgcD0gMC45OTAgKDk5JSA+IDAuMDUpID09PiByZW1vdmUgaXQKIyAgICAgICBTdGF0ZTMgcD0gMC45NDMgKDk0JSA+IDAuMDUpID09PiByZW1vdmUgaXQKIyAgICAgICBBZG1pbiAgcD0gMC42MDIgKDYwJSA+IDAuMDUpID09PiByZW1vdmUgaXQKCiMgZ28gdG8gc3RlcCAyLCByZW1vdmUgU3RhdGUsIHJlbW92ZSBBZG1pbgoKIyBTdW1tYXJ5IHN0YXRzIG5vdyBzaG93IHRoYXQgTWFya2V0aW5nIFNwZW5kaW5nIHZhcmlhYmxlIGlzIDAuMDYgCiMgYW5kIGhhcyBhICcuJyBtZWFuaW5nIGl0IGlzICh3ZWFrKSBzdGF0IHNpZ25pZmljYW50IAoKIyBjYW4gcmVtb3ZlIHRoZSBNYXJrZXRpbmcgU3BlbmRpbmcgYW5kIGp1c3QgaGF2ZSB0aGUgMSBoaWdobHkgc3RhdCBzaWduaWZpY2FudCB2YXJpYWJsZQojIFN0ZXAgNQpgYGAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAKIyMgQXV0b21hdGUgQmFja3dhcmQgRWxpbWluYXRpb24gaW4gUgpgYGB7ciBCYWNrd2FyZCBFbGltIEF1dG99CiMgYXV0b21hdGljIGJhY2t3YXJkIGVsaW1pbmF0aW9uCmJhY2t3YXJkRWxpbWluYXRpb24gPC0gZnVuY3Rpb24oeCwgc2wpIHsKICAgIG51bVZhcnMgPSBsZW5ndGgoeCkKICAgIGZvciAoaSBpbiBjKDE6bnVtVmFycykpewogICAgICByZWdyZXNzb3IgPSBsbShmb3JtdWxhID0gUHJvZml0IH4gLiwgZGF0YSA9IHgpCiAgICAgIG1heFZhciA9IG1heChjb2VmKHN1bW1hcnkocmVncmVzc29yKSlbYygyOm51bVZhcnMpLCAiUHIoPnx0fCkiXSkKICAgICAgaWYgKG1heFZhciA+IHNsKXsKICAgICAgICBqID0gd2hpY2goY29lZihzdW1tYXJ5KHJlZ3Jlc3NvcikpW2MoMjpudW1WYXJzKSwgIlByKD58dHwpIl0gPT0gbWF4VmFyKQogICAgICAgIHggPSB4WywgLWpdCiAgICAgIH0KICAgICAgbnVtVmFycyA9IG51bVZhcnMgLSAxCiAgICB9CiAgICByZXR1cm4oc3VtbWFyeShyZWdyZXNzb3IpKQogIH0KICAKU0wgPSAwLjA1CmRhdGFzZXQgPSBkYXRhc2V0WywgYygxLDIsMyw0LDUpXQpiYWNrd2FyZEVsaW1pbmF0aW9uKHRyYWluaW5nX3NldCwgU0wpCiAgICAgICAgICAgICAgICAgICAKYGBgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgCiMgUG9seW5vbWlhbCBSZWdyZXNzaW9uICAgICAgICAgICAgICAgICAgICAKVGhlIGZvcm11bGEgaXMgKip5KiogPSAkYl97MH0kICsgJGJfezF9eF97MX0kICsgJGJfezJ9eF57Mn1fezF9JC4KCgp1c2VkIGluIEhlYWx0aGNhcmUvIEVwaWRlbWlvbG9neSBkYXRhICAgIAoKUG9zaXRpb24gU2FsYXJ5IGRhdGFzZXQKICAgICAgICAgICAgICAgICAgIApgYGB7cn0gICAgICAgICAgICAgICAgICAgICAgCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gNiAtIFBvbHlub21pYWwgUmVncmVzc2lvbi9SL1Bvc2l0aW9uX1NhbGFyaWVzLmNzdicpCgojIHVzZSB0aGUgY29sdW1ucyBsZXZlbCBhbmQgc2FsYXJ5CmRhdGFzZXQgPSBkYXRhc2V0WzI6M10gICAgICAgICAgICAgICAgICAgIAoKIyBubyBkYXRhIHNwbGl0dGluZyBkdWUgdG8gc21hbGwgZGF0YXNldAojIG5vIGZlYXR1cmUgc2NhbGluZyBuZWVkZWQKCiMgZm9yIGEgYmFzZWxpbmUgY29tcGFyaXNvbiwgdXNlIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbgpsaW5fcmVnID0gbG0oZm9ybXVsYSA9IFNhbGFyeSB+IC4sCiAgICAgICAgICAgICBkYXRhID0gZGF0YXNldCkKCnN1bW1hcnkobGluX3JlZykKCgojIEZpdHRpbmcgUG9seW5vbWlhbCBSZWdyZXNzaW9uIHRvIHRoZSBkYXRhc2V0CiMgcG9seW5vbWlhbCBmZWF0dXJlcyBvZiBpbmRlcCB2YXJpYWJsZXMgKHRvIGFueSBkZWdyZWUgeW91IHdhbnQpCiMgMSBpbmRlcCArIGRlcC4gdmFycwojIGFkZCBjb2x1bW4gdXNpbmcgJCBhbmQgbmFtZSBpdAoKIyAgIGRhdGFzZXQkY29sdW1uXjIgcmV0dXJucyBzcXVhcmVkIGNvbHVtbiBmb3IgYWxsIGxldmVsIGNvbHVtbiB2YWx1ZXMKZGF0YXNldCRMZXZlbDIgPSBkYXRhc2V0JExldmVsXjIKIyAgIGRhdGFzZXQkY29sdW1uXjMgcmV0dXJucyBjdWJlZCBjb2x1bW4gZm9yIGFsbCBsZXZlbCBjb2x1bW4gdmFsdWVzCmRhdGFzZXQkTGV2ZWwzID0gZGF0YXNldCRMZXZlbF4zCmRhdGFzZXQkTGV2ZWw0ID0gZGF0YXNldCRMZXZlbF40Cgpwb2x5X3JlZyA9IGxtKGZvcm11bGEgPSBTYWxhcnkgfiAuLAogICAgICAgICAgICAgIGRhdGEgPSBkYXRhc2V0KQoKc3VtbWFyeShwb2x5X3JlZykKCgoKIy0tLS0tLS0tLS0tLS0tLS0gVmlzdWFsaXppbmcgdGhlIExpbmVhciBSZWdyZXNzaW9uIHJlc3VsdHMKIyBpbnN0YWxsLnBhY2thZ2VzKCdnZ3Bsb3QyJykKbGlicmFyeShnZ3Bsb3QyKQoKZ2dwbG90KCkgKyAjIHg9IGluZGVwICB5PSBkZXAgdmFyCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGFzZXQkTGV2ZWwsIHkgPSBkYXRhc2V0JFNhbGFyeSksICMgcmVhbCBwb2ludHMKICAgICAgICAgICAgIGNvbG91ciA9ICdyZWQnKSArICAgICAgICMgcHJlZGljdCBmdW5jdGlvbgogIGdlb21fbGluZShhZXMoeCA9IGRhdGFzZXQkTGV2ZWwsIHkgPSBwcmVkaWN0KGxpbl9yZWcsIG5ld2RhdGEgPSBkYXRhc2V0KSksICMgcHJlZGljdGVkCiAgICAgICAgICAgIGNvbG91ciA9ICdibHVlJykgKwogIGdndGl0bGUoJ1RydXRoIG9yIEJsdWZmIChMaW5lYXIgUmVncmVzc2lvbiknKSArCiAgeGxhYignTGV2ZWwnKSArCiAgeWxhYignU2FsYXJ5JykKCiMgdGhlIGxpbmVhciByZWdyZXNzaW9uIGxpbmUgZG9lcyBub3QgZml0IHRoZSByZWFsIGRhdGEgcG9pbnRzLCAKIyB0aGlzIGlzIGNsZWFybHkgYSBwb2x5bm9taWFsIHByb2JsZW0KIyBwcmVkaWN0ZWQgc2FsYXJpZXMgYXJlIGxpbmVhciBidXQgcmVhbCBkYXRhIHBvaW50cyBhcmUgcG9seW5vbWlhbAojIHJlYWwgc2FsYXJ5IGxldmVsIDU9ICQxMjUsMDAwIHZzIHByZWRpY3RlZD0gJDI0MCwwMDAKCgojLS0tLS0tLS0tLS0tLS0gVmlzdWFsaXppbmcgdGhlIFBvbHlub21pYWwgUmVncmVzc2lvbiByZXN1bHRzCiMgCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRhc2V0JExldmVsLCB5ID0gZGF0YXNldCRTYWxhcnkpLCAjIHJlYWwKICAgICAgICAgICAgIGNvbG91ciA9ICdyZWQnKSArICAgICAgIyBwcmVkaWN0IGZ1bmN0aW9uLCBjaGFuZ2UgdG8gcG9seV9yZWcKICBnZW9tX2xpbmUoYWVzKHggPSBkYXRhc2V0JExldmVsLCB5ID0gcHJlZGljdChwb2x5X3JlZywgbmV3ZGF0YSA9IGRhdGFzZXQpKSwgIyBwcmVkaWN0ZWQKICAgICAgICAgICAgY29sb3VyID0gJ2JsdWUnKSArCiAgZ2d0aXRsZSgnVHJ1dGggb3IgQmx1ZmYgKFBvbHlub21pYWwgUmVncmVzc2lvbiknKSArCiAgeGxhYignTGV2ZWwnKSArCiAgeWxhYignU2FsYXJ5JykKCiMgdGhlIHByZWRpY3RlZCBsaW5lIGZpdHMgYmV0dGVyIHdpdGggdGhlIGRhdGEgcG9pbnRzLCBjdXJ2ZWQgbGluZQoKCgojLS0tLS0tLS0tLS0tIFZpc3VhbGl6aW5nIHRoZSBSZWdyZXNzaW9uIE1vZGVsIHJlc3VsdHMgCiMgKGZvciBoaWdoZXIgcmVzb2x1dGlvbiBhbmQgc21vb3RoZXIgY3VydmUpCiMgCmxpYnJhcnkoZ2dwbG90MikKCnhfZ3JpZCA9IHNlcShtaW4oZGF0YXNldCRMZXZlbCksIG1heChkYXRhc2V0JExldmVsKSwgMC4xKQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRhc2V0JExldmVsLCB5ID0gZGF0YXNldCRTYWxhcnkpLAogICAgICAgICAgICAgY29sb3VyID0gJ3JlZCcpICsKICBnZW9tX2xpbmUoYWVzKHggPSB4X2dyaWQsIHkgPSBwcmVkaWN0KHBvbHlfcmVnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoTGV2ZWwgPSB4X2dyaWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMZXZlbDIgPSB4X2dyaWReMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExldmVsMyA9IHhfZ3JpZF4zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTGV2ZWw0ID0geF9ncmlkXjQpKSksCiAgICAgICAgICAgIGNvbG91ciA9ICdibHVlJykgKwogIGdndGl0bGUoJ1RydXRoIG9yIEJsdWZmIChQb2x5bm9taWFsIFJlZ3Jlc3Npb24pJykgKwogIHhsYWIoJ0xldmVsJykgKwogIHlsYWIoJ1NhbGFyeScpCgojIFByZWRpY3RpbmcgYSBuZXcgcmVzdWx0IHdpdGggTGluZWFyIFJlZ3Jlc3Npb24KIyBtYWtlIGEgcHJlZGljdGlvbiBvbiBiYXNlZCBvbiBsZXZlbCA2LjUgCiMgbWFrZSBhIG5ldyBkYXRhZnJhbWUgcm93CiMgeV9wcmVkLjIgPSBwcmVkaWN0KGxpbl9yZWcsIGRhdGEuZnJhbWUoTGV2ZWwgPSA2LjUpKQoKcHJlZGljdChsaW5fcmVnLCBkYXRhLmZyYW1lKExldmVsID0gNi41KSkKCiMgUHJlZGljdGluZyBhIG5ldyByZXN1bHQgd2l0aCBQb2x5bm9taWFsIFJlZ3Jlc3Npb24KIyBhZGQgcG9seW5vbWlhbCBmZWF0dXJlcyBmb3IgZWFjaCBsZXZlbCBjb2x1bW4KeV9wcmVkLjMgPSBwcmVkaWN0KHBvbHlfcmVnLCBkYXRhLmZyYW1lKExldmVsID0gNi41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIExldmVsMiA9IDYuNV4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIExldmVsMyA9IDYuNV4zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIExldmVsNCA9IDYuNV40KSkKCnlfcHJlZC4zCmBgYAoKCgojIFN1cHBvcnQgVmVjdG9yIFJlZ3Jlc3Npb24KCmBgYHtyIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmV9CiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignUGFydCAyIC0gUmVncmVzc2lvbi9TZWN0aW9uIDcgLSBTdXBwb3J0IFZlY3RvciBSZWdyZXNzaW9uIChTVlIpL1IvUG9zaXRpb25fU2FsYXJpZXMuY3N2JykKZGF0YXNldCA9IGRhdGFzZXRbMjozXQoKIyBGaXR0aW5nIFN1cHBvcnQgVmVjdG9yIFJlZ3Jlc3Npb24gdG8gdGhlIGRhdGFzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdlMTA3MScpCmxpYnJhcnkoZTEwNzEpCgpyZWdyZXNzb3IgPSBzdm0oZm9ybXVsYSA9IFNhbGFyeSB+IC4sCiAgICAgICAgICAgICAgICBkYXRhID0gZGF0YXNldCwKICAgICAgICAgICAgICAgIHR5cGUgPSAnZXBzLXJlZ3Jlc3Npb24nLCAjIFZFUlkgSU1QT1JUQU5ULCBvcHRpb25zIGZvciB0eXBlIChzZWUgZG9jcykKICAgICAgICAgICAgICAgIGtlcm5lbCA9ICdyYWRpYWwnKQoKCiMgUHJlZGljdGluZyBhIG5ldyByZXN1bHQsIHRoaXMgaXMgc2hvd24gaW4gRGF0YSBlbnZpcm9ubWVudCByaWdodCBwYW5lbAp5X3ByZWQgPSBwcmVkaWN0KHJlZ3Jlc3NvciwgZGF0YS5mcmFtZShMZXZlbCA9IDYuNSkpCgojLS0tLS0tLS0tLS0gVmlzdWFsaXppbmcgdGhlIFNWUiByZXN1bHRzCiMgaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRhc2V0JExldmVsLCB5ID0gZGF0YXNldCRTYWxhcnkpLAogICAgICAgICAgICAgY29sb3VyID0gJ3JlZCcpICsgIyByZWFsIGRhdGEgcG9pbnRzCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YXNldCRMZXZlbCwgeSA9IHByZWRpY3QocmVncmVzc29yLCBuZXdkYXRhID0gZGF0YXNldCkpLAogICAgICAgICAgICBjb2xvdXIgPSAnYmx1ZScpICsgIyBwcmVkaWN0ZWQgcG9pbnRzCiAgZ2d0aXRsZSgnVHJ1dGggb3IgQmx1ZmYgKFNWUiAxKScpICsKICB4bGFiKCdMZXZlbCcpICsKICB5bGFiKCdTYWxhcnknKQoKIy0tLS0tLS0tLS0tIFZpc3VhbGl6aW5nIHRoZSBTVlIgcmVzdWx0cyAKIyAoZm9yIGhpZ2hlciByZXNvbHV0aW9uIGFuZCBzbW9vdGhlciBjdXJ2ZSkKIyBpbnN0YWxsLnBhY2thZ2VzKCdnZ3Bsb3QyJykKbGlicmFyeShnZ3Bsb3QyKQp4X2dyaWQgPSBzZXEobWluKGRhdGFzZXQkTGV2ZWwpLCBtYXgoZGF0YXNldCRMZXZlbCksIDAuMSkKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRhc2V0JExldmVsLCB5ID0gZGF0YXNldCRTYWxhcnkpLAogICAgICAgICAgICAgY29sb3VyID0gJ3JlZCcpICsKICBnZW9tX2xpbmUoYWVzKHggPSB4X2dyaWQsIHkgPSBwcmVkaWN0KHJlZ3Jlc3NvciwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoTGV2ZWwgPSB4X2dyaWQpKSksCiAgICAgICAgICAgIGNvbG91ciA9ICdibHVlJykgKwogIGdndGl0bGUoJ1RydXRoIG9yIEJsdWZmIChTVlIgMiknKSArCiAgeGxhYignTGV2ZWwnKSArCiAgeWxhYignU2FsYXJ5JykKCmBgYAoKCgoKIyBEZWNpc2lvbiBUcmVlIFJlZ3Jlc3Npb24KRGVjaXNpb24gVHJlZXMgaGF2ZSAyIHR5cGVzIChDQVJUKTogKipDbGFzc2lmaWNhdGlvbiBUcmVlcyoqIGFuZCAqKlJlZ3Jlc3Npb24gVHJlZXMqKiAobW9yZSBjb21wbGV4KQoKcHJlZGljdGluZyAzcmQgdmFyaWFibGUgKHkpLCB1c2luZyB4MSBhbmQgeDIKCmBgYHtyIERlY2lzaW9uIFRyZWUgUmVncmVzc2lvbn0KIyBJbXBvcnRpbmcgdGhlIGRhdGFzZXQKZGF0YXNldCA9IHJlYWQuY3N2KCdQYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gOCAtIERlY2lzaW9uIFRyZWUgUmVncmVzc2lvbi9SL1Bvc2l0aW9uX1NhbGFyaWVzLmNzdicpCmRhdGFzZXQgPSBkYXRhc2V0WzI6M10KCiMgRml0dGluZyBTdXBwb3J0IFZlY3RvciBSZWdyZXNzaW9uIHRvIHRoZSBkYXRhc2V0CiMgaW5zdGFsbC5wYWNrYWdlcygnZTEwNzEnKQpsaWJyYXJ5KGUxMDcxKQoKIyBGaXR0aW5nIERlY2lzaW9uIFRyZWUgUmVncmVzc2lvbiB0byB0aGUgZGF0YXNldAojIGluc3RhbGwucGFja2FnZXMoJ3JwYXJ0JykKbGlicmFyeShycGFydCkKCiMgUlBBUlQgPSBSZWN1cnNpdmUgUGFydGl0aW9uaW5nIApyZWdyZXNzb3IgPSBycGFydChmb3JtdWxhID0gU2FsYXJ5IH4gLiwKICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFzZXQsCiAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gMSkpICMgbmV3IHBhcnQgCgoKIyBQcmVkaWN0aW5nIGEgbmV3IHJlc3VsdCB3aXRoIERlY2lzaW9uIFRyZWUgUmVncmVzc2lvbgp5X3ByZWQgPSBwcmVkaWN0KHJlZ3Jlc3NvciwgZGF0YS5mcmFtZShMZXZlbCA9IDYuNSkpCgojLS0tLS0tLS0tLS0tLS0tIFZpc3VhbGl6aW5nIHRoZSBEZWNpc2lvbiBUcmVlIFJlZ3Jlc3Npb24gCiMgcmVzdWx0cyAoaGlnaGVyIHJlc29sdXRpb24pCiMgaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpCgpsaWJyYXJ5KGdncGxvdDIpCgp4X2dyaWQgPSBzZXEobWluKGRhdGFzZXQkTGV2ZWwpLCBtYXgoZGF0YXNldCRMZXZlbCksIDAuMDEpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGFzZXQkTGV2ZWwsIHkgPSBkYXRhc2V0JFNhbGFyeSksCiAgICAgICAgICAgICBjb2xvdXIgPSAncmVkJykgKwogIGdlb21fbGluZShhZXMoeCA9IHhfZ3JpZCwgeSA9IHByZWRpY3QocmVncmVzc29yLCBuZXdkYXRhID0gZGF0YS5mcmFtZShMZXZlbCA9IHhfZ3JpZCkpKSwKICAgICAgICAgICAgY29sb3VyID0gJ2JsdWUnKSArCiAgZ2d0aXRsZSgnVHJ1dGggb3IgQmx1ZmYgKERlY2lzaW9uIFRyZWUgUmVncmVzc2lvbiknKSArCiAgeGxhYignTGV2ZWwnKSArCiAgeWxhYignU2FsYXJ5JykKCgoKYGBgCgp0aGlzIHNob3dzIHdpdGggdGhlIGJsdWUgbGluZSB0aGUgYXZlcmFnZSBvZiBzYWxhcmllcyBmb3IgbGV2ZWwgNi41IGlzICQyNTAsMDAwCgpgYGB7cn0KIyBQbG90dGluZyB0aGUgdHJlZQpwbG90KHJlZ3Jlc3NvcikKdGV4dChyZWdyZXNzb3IpCmBgYAoKCiMgUmFuZG9tIEZvcmVzdCBSZWdyZXNzaW9uIApFbnNlbWJsZSBMZWFybmluZyA9IHRha2UgbXVsdGlwbGUgYWxnb3JpdGhtcyB0byBtYWtlIHBvd2VyZnVsIGFsZ29yaXRobQoKICAtIHN0ZXAgMTogcGljayBhdCByYW5kb20gayBkYXRhIHBvaW50cyBmcm9tIHRyYWluaW5nIHNldAogIC0gc3RlcCAyOiBidWlsZCBkZWNpc2lvbiB0cmVlIGFzc29jaWF0ZWQgdG8gdGhlc2UgayBkYXRhIHBvaW50cwogIC0gc3RlcCAzOiBjaG9vc2UgdGhlIG51bWJlciAqTip0cmVlIG9mIHRyZWVzIHlvdSB3YW50IHRvIGJ1aWxkIGFuZCByZXBlYXQgc3RlcHMgMSAmIDIKICAtIHN0ZXAgNDogZm9yIGEgbmV3IGRhdGEgcG9pbnQsIG1ha2UgZWFjaCBvbmUgb2YgeW91ciAqTip0cmVlIHRyZWVzIHByZWRpY3QgdGhlIHZhbHVlIG9mIFkgZm9yIHRoZSBkYXRhIHBvaW50IGluIHF1ZXN0aW9uLCBhbmQgYXNzaWduIHRoZSBuZXcgZGF0YSBwb2ludCB0aGUgYXZlcmFnZSBhY3Jvc3MgYWxsIG9mIHRoZSBwcmVkaWN0ZWQgWSB2YWx1ZXMKCmBgYHtyIFJhbmRvbSBGb3Jlc3QgUmVncmVzc2lvbn0KIyBJbXBvcnRpbmcgdGhlIGRhdGFzZXQKZGF0YXNldCA9IHJlYWQuY3N2KCdQYXJ0IDIgLSBSZWdyZXNzaW9uL1NlY3Rpb24gOSAtIFJhbmRvbSBGb3Jlc3QgUmVncmVzc2lvbi9SL1Bvc2l0aW9uX1NhbGFyaWVzLmNzdicpCmRhdGFzZXQgPSBkYXRhc2V0WzI6M10KCiMtLS0tLSBGaXR0aW5nIFJhbmRvbSBGb3Jlc3QgUmVncmVzc2lvbiB0byB0aGUgZGF0YXNldAojIGluc3RhbGwucGFja2FnZXMoJ3JhbmRvbUZvcmVzdCcpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQoKc2V0LnNlZWQoMTIzNCkgIyBjb21tb24gcmFuZG9tIHNlZWQgaW4gUgoKIyAtLSBidWlsZCBSYW5kb20gRm9yZXN0IE1vZGVsCnJlZ3Jlc3NvciA9IHJhbmRvbUZvcmVzdCh4ID0gZGF0YXNldFstMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZGF0YXNldCRTYWxhcnksCiAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMCkgIyBjaGFuZ2UgdmFsdWVzIHRvIGZpbmQgYmVzdCB2YWx1ZSwgNTAwCgojIFByZWRpY3RpbmcgYSBuZXcgcmVzdWx0CnlfcHJlZCA9IHByZWRpY3QocmVncmVzc29yLCBkYXRhLmZyYW1lKExldmVsID0gNi41KSkKIyB5X3ByZWQgPSAxNjA5MDggZm9yIG50cmVlPSA1MDAKCiMtLS0tLS0tLS0tLSBWaXN1YWxpemluZyB0aGUgUmFuZG9tIEZvcmVzdCBSZWdyZXNzaW9uIE1vZGVsIAojIHJlc3VsdHMgKGZvciBoaWdoZXIgcmVzb2x1dGlvbiBhbmQgc21vb3RoZXIgY3VydmUpCiMgaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpCmxpYnJhcnkoZ2dwbG90MikKCnhfZ3JpZCA9IHNlcShtaW4oZGF0YXNldCRMZXZlbCksIG1heChkYXRhc2V0JExldmVsKSwgMC4wMSkKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRhc2V0JExldmVsLCB5ID0gZGF0YXNldCRTYWxhcnkpLAogICAgICAgICAgICAgY29sb3VyID0gJ3JlZCcpICsKICBnZW9tX2xpbmUoYWVzKHggPSB4X2dyaWQsIHkgPSBwcmVkaWN0KHJlZ3Jlc3NvciwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoTGV2ZWwgPSB4X2dyaWQpKSksCiAgICAgICAgICAgIGNvbG91ciA9ICdibHVlJykgKwogIGdndGl0bGUoJ1RydXRoIG9yIEJsdWZmIChSYW5kb20gRm9yZXN0IFJlZ3Jlc3Npb24pJykgKwogIHhsYWIoJ0xldmVsJykgKwogIHlsYWIoJ1NhbGFyeScpCgoKCmBgYApUaGUgYmx1ZSBsaW5lIGlzIHRoZSBwcmVkaWN0ZWQgYXZlcmFnZXMgb2Ygc2FsYXJpZXMsIHdpdGggNTAwIHRyZWVzLCB0aGUgbW9kZWwgcHJlZGljdGVkIHNhbGFyeSBvZiAkMTYwLDkwOCBmb3IgbGV2ZWwgNi41CgoKCiMgUiBTcXVhcmVkCgotICoqc3VtIG9mIHNxdWFyZXMgcmVzaWR1YWxzKiogKFNTcmVzKSA9IHN1bSgkeV97aX0kIC0gJHlfe2leaX0kKV4yCi0gYXZlcmFnZSBsaW5lIG9uIHBsb3QgZm9yICoqc3VtIG9mIHNxdWFyZXMgdG90YWwqKiA9IHN1bSgkeV97aX0kIC0gJHlfe2F2Z30kKV4yCi0gJFJeezJ9JCA9IDEgLSAkU1Nfe3Jlc30kICRcZGl2JCAkU1Nfe3RvdH0kIAoKdGhlIHRvdGFsIG9mIHN1bSBvZiBzcXVhcmVzLCB0cnkgdG8gZml0IGEgbGluZSB0byBtaW5pbWl6ZSB0aGUgbGluZSBvZiBsZWFzdCBzcXVhcmVzIHJlc2lkdWFscywgKmhvdyBnb29kIGlzIHlvdXIgbGluZSBjb21wYXJlZCB0byB0aGUgYXZlcmFnZSA/KiBUaGUgY2xvc2VyICRSXjIkIGlzIHRvIDEgdGhlIGJldHRlciEgCgoKIyMgKiphZGp1c3RlZCBSIHNxdWFyZWQqKiBpcyB1c2VkIGZvciBtdWx0aXBsZSByZWdyZXNzaW9uLiAKCi0gcCA9IG51bWJlciBvZiByZWdyZXNzb3JzCi0gbj0gc2FtcGxlIHNpemUKLSBhZGouICRSXjIkID0gMSAtICgxIC0gJFJeMiQpIChuIC0gMSkvIChuIC0gcCAtIDEpCgoKaG93IHdlbGwgeW91ciBtb2RlbCBoYXMgYmVlbiBmaXR0ZWQsIGNsb3NlciB0byAxIHRoZSBiZXR0ZXIsIGJ1dCBpdCBpcyBiaWFzZWQgT0xTIG1ldGhvZCwgUl4yIG5ldmVyIGRlY3JlYXNlcywgYWRkaW5nIG1vcmUgdmFyaWFibGVzIFJeMiBncm93cy4gQWRqdXN0ZWQgUl4yIHBlbmFsaXplcyB0aGUgZ3Jvd2luZyB2YWx1ZSBmcm9tIHZhcmlhYmxlcywgaGVuY2UgaXQgZGVjcmVhc2VzIGluIHZhbHVlLiBEcm9wcGl1bmcgdW5oZWxwZnVsIGNvbHVtbnMgZm9yIG1vZGVscyBoZWxwcyB0aGUgYWRqdXN0ZWQgUl4yIHZhbHVlIGdldCBjbG9zZXIgdG8gMSwgd2hpY2ggaXMgYWxsIGdvb2QgbmV3cyBhbmQgd2hhdCB3ZSB3YW50LiAKClNvIGluIG1vZGVscyBhYm92ZSwgdGhlIDNyZCBhbmQgNHRoIG1vZGVscyBSXjIgdmFsdWVzOgoKLSBgYGxtKGZvcm11bGE9IFByb2ZpdCB+IFImRC5TcGVuZCArIE1hcmtldGluZy5TcGVuZCwgZGF0YSA9IGRhdGFzZXQpYGAgJFJeMiQgPSAwLjk0ODMKLSBgYGxtKGZvcm11bGE9IFByb2ZpdCB+IFImRC5TcGVuZCwgZGF0YSA9IGRhdGFzZXQpYGAgJFJeMiQgPSAwLjk0NTQKCkNvbXBhcmluZyB0aGUgbGFzdCAyIG1vZGVsczogIGBgMC45NDU0IC0gMC45NDgzID0gLTAuMDAyOWBgLCBzbyBsYXN0IG1vZGVsIGRpZCB3b3JzZSB0aGVuIDNyZCBtb2RlbAoKCgojIyBjb2VmZmljaWVudHMgClVuZGVyc3RhbmRpbmcgdGhlIGNvZWZmaWNpZW50cwpgYGAKbG0oZm9ybXVsYSA9IFByb2ZpdCB+IFIuRC5TcGVuZCArIE1hcmtldGluZy5TcGVuZCwgZGF0YSA9IGRhdGFzZXQpCgpSZXNpZHVhbHM6CiAgIE1pbiAgICAgMVEgTWVkaWFuICAgICAzUSAgICBNYXggCi0zMzY0NSAgLTQ2MzIgICAtNDE0ICAgNjQ4NCAgMTcwOTcgCgpDb2VmZmljaWVudHM6CiAgICAgICAgICAgICAgICAgRXN0aW1hdGUgU3RkLiBFcnJvciB0IHZhbHVlIFByKD58dHwpICAgIAooSW50ZXJjZXB0KSAgICAgNC42OThlKzA0ICAyLjY5MGUrMDMgIDE3LjQ2NCAgIDwyZS0xNiAqKioKUi5ELlNwZW5kICAgICAgIDcuOTY2ZS0wMSAgNC4xMzVlLTAyICAxOS4yNjYgICA8MmUtMTYgKioqCk1hcmtldGluZy5TcGVuZCAyLjk5MWUtMDIgIDEuNTUyZS0wMiAgIDEuOTI3ICAgICAwLjA2IC4gIAotLS0KU2lnbmlmLiBjb2RlczogIDAg4oCYKioq4oCZIDAuMDAxIOKAmCoq4oCZIDAuMDEg4oCYKuKAmSAwLjA1IOKAmC7igJkgMC4xIOKAmCDigJkgMQoKUmVzaWR1YWwgc3RhbmRhcmQgZXJyb3I6IDkxNjEgb24gNDcgZGVncmVlcyBvZiBmcmVlZG9tCk11bHRpcGxlIFItc3F1YXJlZDogIDAuOTUwNSwJQWRqdXN0ZWQgUi1zcXVhcmVkOiAgMC45NDgzIApGLXN0YXRpc3RpYzogNDUwLjggb24gMiBhbmQgNDcgREYsICBwLXZhbHVlOiA8IDIuMmUtMTYKYGBgCgpwb3NpdGl2ZSB2YWx1ZSBpcyBwb3NpdGl2ZSBjb3JyZWxhdGVkLCBpbmNyZWFzZSBpbiBSLkQuU3BlbmQgPT0gaW5jcmVhc2UgaW4gUHJvZml0LCBhbmQgdmljZS12ZXJzYS4KCgpNYWduaXR1ZGUgaXMgY29sdW1uIHVuZGVyIEVzdGltYXRlLCBSLkQuU3BlbmQgYGA3Ljk2NmUtMDFgYCBpbiB1bml0cyBvZiBpbmRlcGVuZGVudCB2YXJpYWJsZSB2YWx1ZXMuIENvcnJlY3QgdG8gc2F5OiBSRCBTcGVuZCBoYXMgZ3JlYXRlciBpbXBhY3Qgb24gcHJvZml0IHBlciB1bml0IG9mIFJEIFNwZW5kIHRoYW4gCm1hcmtldGluZyBzcGVuZC4gKkZvciBldmVyeSB1bml0IGluY3JlYXNlIG9mIFJEIFNwZW5kICgkMSksIHByb2ZpdHMgd2lsbCBpbmNyZWFzZSBieSAwLjc5IGNlbnRzIHBlciB1bml0Ki4gTWFya2V0aW5nIFNwZW5kIGFkZHMgdmFsdWUgb2YgMyBjZW50cyB1bml0cyB0byBwcm9maXQuCgoKRW5kIG9mIFBhcnQgMiAtLSBSZWdyZXNzaW9uCjxocj4KCiMgKipDbGFzc2lmaWNhdGlvbioqCgojIyBMb2dpc3RpYyBSZWdyZXNzaW9uCmFnZSBhbmQgZW1haWwgYWN0aW9uIHRha2VuIChZL04gfCAxLzApIGNvcnJlbGF0aW9uLCB7bGluZWFyIHJlZ3Jlc3Npb24gaXMgbm90IHJpZ2h0IG1vZGVsIGZvciB0aGlzIGJ1dCBzaG93cyBhIHRyZW5kIGJldHdlZW4gdmFyaWFibGVzfS4gU2lnbW9pZCBmdW5jdGlvbiBmb3JjZXMgdmFsdWVzIDAgdG8gMSwgUy1zaGFwZWQgY3VydmUgb24gcGxvdCB3aGljaCBtYWtlcyB0aGUgYmVzdCBmaXR0aW5nIGxpbmUgZm9yIHZhcmlhYmxlcy4gKipVc2VkIGZvciBwcmVkaWN0aW5nIHByb2JhYmlsaXR5ICgkcF4tJCkgKHBfaGF0KSoqCgpMb2dpc3RpYyBSZWdyZXNzaW9uIGZvcm11bGEgICRsbiQocCAvIDEgLSBwKSA9ICRiX3swfSQgKyAkYl97MX0qeCQgCgpFeGFtcGxlOiBvbiB4LWF4aXMsIDQgYWdlIGNvbHVtbiB2YWx1ZXMgYXQgcmFuZG9tLCB5LWF4aXMgaXMgcF9oYXQsIHRoZSBzLWN1cnZlIG9uIHRoZSBwbG90LiBQZXJzb24gb2YgYWdlIDIwIGhhcyBhIHBfaGF0IHByb2JhYmlsaXR5IHZhbHVlIG9mIDAuNyUgKHBeLSA9IDAuNykgb2YgdGFraW5nIGFjdGlvbiB3aXRoIGFuIGVtYWlsLiBQZXJzb24gYWdlIDQwIGhhcyBwXi0gPSA4NSUgb2YgdGFraW5nIGFjdGlvbiB3aXRoIGFuIGVtYWlsLiBBbnkgdmFsdWVzIGJlbG93IDUwJSB0aGUgeV9oYXQgdmFsdWUgaXMgcHVzaGVkIGRvd24gdG93YXJkcyAwLCBhbnkgdmFsdWUgYWJvdmUgNTAlIGlzIHB1c2hlZCB1cCB0b3dhcmRzIDEsIHNvIHRoZSByZXN1bHRzIGluIGEgYmluYXJ5IDAvMSBvdXRjb21lLiAKCmBgYHtyIExvZ2lzdGljIFJlZ3Jlc3Npb259CgojIEltcG9ydGluZyB0aGUgZGF0YXNldApkYXRhc2V0ID0gcmVhZC5jc3YoJy4vUGFydCAzIC0gQ2xhc3NpZmljYXRpb24vU2VjdGlvbiAxNCAtIExvZ2lzdGljIFJlZ3Jlc3Npb24vUi9Tb2NpYWxfTmV0d29ya19BZHMuY3N2JykKCmRhdGFzZXQgPSBkYXRhc2V0WzM6NV0KCiMgRW5jb2RpbmcgdGhlIHRhcmdldCBmZWF0dXJlIGFzIGZhY3RvcgpkYXRhc2V0JFB1cmNoYXNlZCA9IGZhY3RvcihkYXRhc2V0JFB1cmNoYXNlZCwgbGV2ZWxzID0gYygwLCAxKSkKCiMgU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdGhlIFRyYWluaW5nIHNldCBhbmQgVGVzdCBzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKbGlicmFyeShjYVRvb2xzKQpzZXQuc2VlZCgxMjMpCnNwbGl0ID0gc2FtcGxlLnNwbGl0KGRhdGFzZXQkUHVyY2hhc2VkLCBTcGxpdFJhdGlvID0gMC43NSkKdHJhaW5pbmdfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IFRSVUUpCnRlc3Rfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IEZBTFNFKQoKIyBGZWF0dXJlIFNjYWxpbmcgaXMgYmVzdCBwcmFjdGljZSBmb3IgTG9naXN0aWMgUmVncmVzc2lvbgojICBbLTNdIGlzIHRoZSBsYXN0IGNvbHVtbiBvZiBkYXRhc2V0CnRyYWluaW5nX3NldFstM10gPSBzY2FsZSh0cmFpbmluZ19zZXRbLTNdKQp0ZXN0X3NldFstM10gPSBzY2FsZSh0ZXN0X3NldFstM10pCgojIEZpdHRpbmcgTG9naXN0aWMgUmVncmVzc2lvbiB0byB0aGUgVHJhaW5pbmcgc2V0CiMgZ2xtIChnZW5lcmFsIGxpbmVhciBtb2RlbCkgYnVpbGRzIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uCiMgcHJlZGljdCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIG9mIFB1cmNoYXNlZCBiYXNlZCBvbiBpbmRlcC4gdmFyaWFibGVzOiBhZ2UsIGVzdGltYXRlZCBzYWxhcnkKY2xhc3NpZmllciA9IGdsbShmb3JtdWxhID0gUHVyY2hhc2VkIH4gLiwgCiAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwsCiAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX3NldCkKCiMgUHJlZGljdGluZyB0aGUgVGVzdCBzZXQgcmVzdWx0cwpwcm9iX3ByZWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIsIAogICAgICAgICAgICAgICAgICAgIHR5cGUgPSAncmVzcG9uc2UnLCAKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9zZXRbLTNdKQoKeV9wcmVkLmxvZ3IgPSBpZmVsc2UocHJvYl9wcmVkID4gMC41LCAxLCAwKQoKIyBNYWtpbmcgdGhlIENvbmZ1c2lvbiBNYXRyaXgKY20gPSB0YWJsZSh0ZXN0X3NldFssIDNdLCB5X3ByZWQubG9nciA+IDAuNSkKY20KCnByb2JfcHJlZFsxXQp5X3ByZWQubG9nclsxXQpgYGAKdGhlIDFzdCBwcm9iYWJpbGl0eSBwcmVkaWN0ZWQgPSAwLjE2MiBhbmQgdGhlIHRlc3Rfc2V0IDFzdCB2YWx1ZSBpcyAwLCB0aGlzIG1lYW4gdXNlciAjMiBpcyB1bmxpa2VseSB0byBwdXJjaGFzZS4gVXNpbmcgdGhlIHlfcHJlZC5sb2dyIHZhcmlhYmxlIHRoZSBtb2RlbCBwcmVkaWN0ZWQgdGhhdCB1c2VyICMyIHdpbGwgbm90IHB1cmNoYXNlIGFuIGl0ZW0uCgoKdGhlIG1vZGVsIGNvcnJlY3RseSBwcmVkaWN0ZWQgYSBwdXJjaGFzZSAoNTcrMjYpIDgzIHRpbWVzIGFuZCAxNyBpbmNvcnJlY3QgcHJlZGljdGlvbnMKCgpgYGB7cn0KIyBWaXN1YWxpemluZyB0aGUgVHJhaW5pbmcgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQoKc2V0ID0gdHJhaW5pbmdfc2V0CgojIHRoZSByYW5nZXMgb2Ygb2JzZXJ2YXRpb25zICwgLTEgdG8gYXZvaWQgc3F1ZWV6aW5nClgxID0gc2VxKG1pbihzZXRbLCAxXSkgLSAxLCBtYXgoc2V0WywgMV0pICsgMSwgYnkgPSAwLjAxKSAjIGFnZQpYMiA9IHNlcShtaW4oc2V0WywgMl0pIC0gMSwgbWF4KHNldFssIDJdKSArIDEsIGJ5ID0gMC4wMSkgIyBzYWxhcnkKCiMgbWFrZSBncmlkIG1hdHJpeCB1c2luZyB0aGUgdmFyaWFibGVzIGFib3ZlCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQoKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCgpwcm9iX3NldCA9IHByZWRpY3QoY2xhc3NpZmllciwgCiAgICAgICAgICAgICAgICAgICB0eXBlID0gJ3Jlc3BvbnNlJywgCiAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9zZXQpCgp5X2dyaWQgPSBpZmVsc2UocHJvYl9zZXQgPiAwLjUsIDEsIDApCgoKCiM9PT09PT09PT09PT09PSBWaXN1YWxpemluZyB0aGUgVHJhaW5pbmcgc2V0CnBsb3Qoc2V0WywgLTNdLAogICAgIG1haW4gPSAnTG9naXN0aWMgUmVncmVzc2lvbiBDbGFzc2lmaWVyIChUcmFpbmluZyBzZXQpJywKICAgICB4bGFiID0gJ0FnZScsIAogICAgIHlsYWIgPSAnRXN0aW1hdGVkIFNhbGFyeScsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgCiAgICAgeWxpbSA9IHJhbmdlKFgyKSkKCmNvbnRvdXIoWDEsIFgyLCBtYXRyaXgoYXMubnVtZXJpYyh5X2dyaWQpLCBsZW5ndGgoWDEpLCBsZW5ndGgoWDIpKSwgYWRkID0gVFJVRSkKcG9pbnRzKGdyaWRfc2V0LCBwY2ggPSAnLicsIGNvbCA9IGlmZWxzZSh5X2dyaWQgPT0gMSwgJ3NwcmluZ2dyZWVuMycsICd0b21hdG8nKSkKcG9pbnRzKHNldCwgcGNoID0gMjEsIGJnID0gaWZlbHNlKHNldFssIDNdID09IDEsICdncmVlbjQnLCAncmVkMycpKQoKIz09PT09PT09PT09PT09IFZpc3VhbGl6aW5nIHRoZSBUZXN0IHNldCByZXN1bHRzCmxpYnJhcnkoRWxlbVN0YXRMZWFybikKCnNldCA9IHRlc3Rfc2V0CgpYMSA9IHNlcShtaW4oc2V0WywgMV0pIC0gMSwgbWF4KHNldFssIDFdKSArIDEsIGJ5ID0gMC4wMSkKWDIgPSBzZXEobWluKHNldFssIDJdKSAtIDEsIG1heChzZXRbLCAyXSkgKyAxLCBieSA9IDAuMDEpCgpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKCmNvbG5hbWVzKGdyaWRfc2V0KSA9IGMoJ0FnZScsICdFc3RpbWF0ZWRTYWxhcnknKQoKcHJvYl9zZXQgPSBwcmVkaWN0KGNsYXNzaWZpZXIsIHR5cGUgPSAncmVzcG9uc2UnLCBuZXdkYXRhID0gZ3JpZF9zZXQpCgp5X2dyaWQgPSBpZmVsc2UocHJvYl9zZXQgPiAwLjUsIDEsIDApCgpwbG90KHNldFssIC0zXSwKICAgICBtYWluID0gJ0xvZ2lzdGljIFJlZ3Jlc3Npb24gQ2xhc3NpZmllciAoVGVzdCBzZXQpJywKICAgICB4bGFiID0gJ0FnZScsIAogICAgIHlsYWIgPSAnRXN0aW1hdGVkIFNhbGFyeScsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgCiAgICAgeWxpbSA9IHJhbmdlKFgyKSkKCmNvbnRvdXIoWDEsIFgyLCBtYXRyaXgoYXMubnVtZXJpYyh5X2dyaWQpLCBsZW5ndGgoWDEpLCBsZW5ndGgoWDIpKSwgYWRkID0gVFJVRSkKcG9pbnRzKGdyaWRfc2V0LCBwY2ggPSAnLicsIGNvbCA9IGlmZWxzZSh5X2dyaWQgPT0gMSwgJ3NwcmluZ2dyZWVuMycsICd0b21hdG8nKSkKcG9pbnRzKHNldCwgcGNoID0gMjEsIGJnID0gaWZlbHNlKHNldFssIDNdID09IDEsICdncmVlbjQnLCAncmVkMycpKQoKCgpgYGAKClRyYWluaW5nIHNldCBvYnNlcnZhdGlvbiBkYXRhcG9pbnRzLCByZWQgcG9pbnRzIGFyZSB0cmFpbmluZyBvYnNlcnZhdGlvbiB3aGVyZSAoZGVwZW5kZW50IHZhcmlhYmxlKSBQdXJjaGFzZWQ9MCBhbmQgdGhlIGdyZWVuIHBvaW50cyBhcmUgdHJhaW5pbmcgc2V0IG9ic2VydmF0aW9uIHdoZXJlIHB1cmNoYXNlZD0xLiByZWQgem9uZSBpcyBwcmVkaWN0aW9uIHJlZ2lvbiBub24tcHVyY2hhc2UuIGNsYXNzaWZpZXIgcHJlZGljdGVkIHRoYXQgdGhlIGhpZ2hlciBhZ2UgdGhlIG1vcmUgZXN0aW1hdGVkIHNhbGFyeSBhbmQgdG8gIHB1cmNoYXNlZCBpdGVtLiBDbGFzc2lmaWVyIGlzIHN0cmFpZ2h0IGxpbmUgZm9yIGxpbmVhciBtb2RlbHMuIEZvY3VzIG9uIHRoZSBkb3QgY29sb3IgYW5kIHRoZSB6b25lIHRoZXkgZmFsbC4gCgoKCiMjIEstTmVhcmVzdCBOZWlnaGJvciAKCktOTiBwcm9jZXNzOgoKMS4gY2hvb3NlIHRoZSBudW1iZXIgayBvZiBuZWlnaGJvcnMKMi4gdGFrZSB0aGUgS05OcyBvZiB0aGUgbmV3IGRhdGEgcG9pbnQsIGFjY29yZGluZyB0byBFdWNsaWRlYW4gZGlzdGFuY2UKMy4gYW1vbmcgdGhlc2UgS05OcywgY291bnQgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBpbiBlYWNoIGNhdGVnb3J5CjQuIGFzc2lnbiB0aGUgbmV3IGRhdGEgcG9pbnQgdG8gdGhlIGNhdGVnb3J5IHdoZXJlIHlvdSBjb3VudGVkIHRoZSBtb3N0IG5laWdoYm9ycwo1LiBtb2RlbCBpcyBkb25lCgpgYGB7ciBLTk4gQ2xhc3NpZmllcn0KCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDMgLSBDbGFzc2lmaWNhdGlvbi9TZWN0aW9uIDE1IC0gSy1OZWFyZXN0IE5laWdoYm9ycyAoSy1OTikvUi9Tb2NpYWxfTmV0d29ya19BZHMuY3N2JykKCmRhdGFzZXQgPSBkYXRhc2V0WzM6NV0KCiMgRW5jb2RpbmcgdGhlIHRhcmdldCBmZWF0dXJlIGFzIGZhY3RvcgpkYXRhc2V0JFB1cmNoYXNlZCA9IGZhY3RvcihkYXRhc2V0JFB1cmNoYXNlZCwgbGV2ZWxzID0gYygwLCAxKSkKCiMgU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdGhlIFRyYWluaW5nIHNldCBhbmQgVGVzdCBzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKbGlicmFyeShjYVRvb2xzKQpzZXQuc2VlZCgxMjMpCnNwbGl0ID0gc2FtcGxlLnNwbGl0KGRhdGFzZXQkUHVyY2hhc2VkLCBTcGxpdFJhdGlvID0gMC43NSkKdHJhaW5pbmdfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IFRSVUUpCnRlc3Rfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IEZBTFNFKQoKIyBGZWF0dXJlIFNjYWxpbmcKdHJhaW5pbmdfc2V0Wy0zXSA9IHNjYWxlKHRyYWluaW5nX3NldFstM10pCnRlc3Rfc2V0Wy0zXSA9IHNjYWxlKHRlc3Rfc2V0Wy0zXSkKCiMgRml0dGluZyBjbGFzc2lmaWVyIHRvIHRoZSBUcmFpbmluZyBzZXQKIyBDcmVhdGUgeW91ciBjbGFzc2lmaWVyIGhlcmUKCiMgYnVpbGQgYSBLTk4gY2xhc3NpZmllcgpsaWJyYXJ5KGNsYXNzKQojIGZpdCBhIEtOTiB0byBUcmFpbmluZyBzZXQgYW5kIFByZWRpY3QgdGhlIFRlc3Qgc2V0CiMgcmVtb3ZlIGxhc3QgY29sdW1uIG9mIHRyYWluaW5nIHNldAp5X3ByZWQuS05OID0ga25uKHRyYWluPSB0cmFpbmluZ19zZXRbLC0zXSwKICAgICAgICAgICAgICAgICB0ZXN0PSB0ZXN0X3NldFssIC0zXSwKICAgICAgICAgICAgICAgICBjbD0gdHJhaW5pbmdfc2V0WywgM10sCiAgICAgICAgICAgICAgICAgaz0gNSkgCgojIHlfcHJlZC5LTk5bMTo1XQoKCiMgUHJlZGljdGluZyB0aGUgVGVzdCBzZXQgcmVzdWx0cwojIGZvciBLTk4gY29tbWVudCBvdXQgCiMgeV9wcmVkID0gcHJlZGljdChjbGFzc2lmaWVyLCBuZXdkYXRhID0gdGVzdF9zZXRbLTNdKQoKIyBNYWtpbmcgdGhlIENvbmZ1c2lvbiBNYXRyaXgKY20gPSB0YWJsZSh0ZXN0X3NldFssIDNdLCB5X3ByZWQuS05OKQpjbQoKIz09PT09PT09PT09PSBWaXN1YWxpemluZyB0aGUgVHJhaW5pbmcgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0cmFpbmluZ19zZXQKClgxID0gc2VxKG1pbihzZXRbLCAxXSkgLSAxLCBtYXgoc2V0WywgMV0pICsgMSwgYnkgPSAwLjAxKQpYMiA9IHNlcShtaW4oc2V0WywgMl0pIC0gMSwgbWF4KHNldFssIDJdKSArIDEsIGJ5ID0gMC4wMSkKCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQoKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCgojIHlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllciwgbmV3ZGF0YSA9IGdyaWRfc2V0KQoKIyBmb3IgS05OIHJlcGxhY2UgdGhlIHByZWRpY3QgYW5kIGl0cyBhcmd1bWVudHMgd2l0aCBLTk4gYXJndW1lbnRzCnlfZ3JpZCA9IGtubih0cmFpbj0gdHJhaW5pbmdfc2V0WywtM10sCiAgICAgICAgICAgICAgICAgdGVzdD0gZ3JpZF9zZXQsICMgcmVwbGFjZSB0ZXN0X3NldAogICAgICAgICAgICAgICAgIGNsPSB0cmFpbmluZ19zZXRbLCAzXSwKICAgICAgICAgICAgICAgICBrPSA1KQoKcGxvdChzZXRbLCAtM10sCiAgICAgbWFpbiA9ICdLTk4gQ2xhc3NpZmllciAoVHJhaW5pbmcgc2V0KScsCiAgICAgeGxhYiA9ICdBZ2UnLCB5bGFiID0gJ0VzdGltYXRlZCBTYWxhcnknLAogICAgIHhsaW0gPSByYW5nZShYMSksIHlsaW0gPSByYW5nZShYMikpCgpjb250b3VyKFgxLCBYMiwgbWF0cml4KGFzLm51bWVyaWMoeV9ncmlkKSwgbGVuZ3RoKFgxKSwgbGVuZ3RoKFgyKSksIGFkZCA9IFRSVUUpCnBvaW50cyhncmlkX3NldCwgcGNoID0gJy4nLCBjb2wgPSBpZmVsc2UoeV9ncmlkID09IDEsICdzcHJpbmdncmVlbjMnLCAndG9tYXRvJykpCnBvaW50cyhzZXQsIHBjaCA9IDIxLCBiZyA9IGlmZWxzZShzZXRbLCAzXSA9PSAxLCAnZ3JlZW40JywgJ3JlZDMnKSkKCiMgVmlzdWFsaXNpbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0ZXN0X3NldApYMSA9IHNlcShtaW4oc2V0WywgMV0pIC0gMSwgbWF4KHNldFssIDFdKSArIDEsIGJ5ID0gMC4wMSkKWDIgPSBzZXEobWluKHNldFssIDJdKSAtIDEsIG1heChzZXRbLCAyXSkgKyAxLCBieSA9IDAuMDEpCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQpjb2xuYW1lcyhncmlkX3NldCkgPSBjKCdBZ2UnLCAnRXN0aW1hdGVkU2FsYXJ5JykKCiMgZm9yIEtOTiByZXBsYWNlIHRoZSBwcmVkaWN0IGFuZCBpdHMgYXJndW1lbnRzIHdpdGggS05OIGFyZ3VtZW50cwp5X2dyaWQgPSBrbm4odHJhaW49IHRyYWluaW5nX3NldFssLTNdLAogICAgICAgICAgICAgICAgIHRlc3Q9IGdyaWRfc2V0LCAjIHJlcGxhY2UgdGVzdF9zZXQKICAgICAgICAgICAgICAgICBjbD0gdHJhaW5pbmdfc2V0WywgM10sCiAgICAgICAgICAgICAgICAgaz0gNSkKIyB5X2dyaWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIsIG5ld2RhdGEgPSBncmlkX3NldCkKCnBsb3Qoc2V0WywgLTNdLCBtYWluID0gJ0tOTiBDbGFzc2lmaWVyIChUZXN0IHNldCknLAogICAgIHhsYWIgPSAnQWdlJywgeWxhYiA9ICdFc3RpbWF0ZWQgU2FsYXJ5JywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQpjb250b3VyKFgxLCBYMiwgbWF0cml4KGFzLm51bWVyaWMoeV9ncmlkKSwgbGVuZ3RoKFgxKSwgbGVuZ3RoKFgyKSksIGFkZCA9IFRSVUUpCnBvaW50cyhncmlkX3NldCwgcGNoID0gJy4nLCBjb2wgPSBpZmVsc2UoeV9ncmlkID09IDEsICdzcHJpbmdncmVlbjMnLCAndG9tYXRvJykpCnBvaW50cyhzZXQsIHBjaCA9IDIxLCBiZyA9IGlmZWxzZShzZXRbLCAzXSA9PSAxLCAnZ3JlZW40JywgJ3JlZDMnKSkKCgoKYGBgCgoKIyMgU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMKU3RhcnRlZCBpbiAxOTYwcyBhbmQgMTk5MHMuIFNlcGFyYXRlIGRhdGFwb2ludHMgb24gYSBwbG90IGFuZCBjbGFzc2lmeSB0aGVtLiBHb2FsOiBmaW5kIGJlc3QgZGVjaXNpb24gYm91bmRhcnkgdXNpbmcgYSBtYXggbWFyZ2luIGh5cGVycGxhbmUgbGluZSB0aGF0IGhhcyBhIG1heCBtYXJnaW4gKGRpc3RhbmNlIGF3YXkgZnJvbSBsaW5lIGFuZCBkaXN0YW5jZSBiZXR3ZWVuIG1heCBtYXJnaW4gbGluZXMpIGFuZCBkYXRhcG9pbnRzIG91dHNpZGUgdGhlIG1heCBtYXJnaW4gbGluZXMgYXJlIHBvc2l0aXZlIG9yIG5lZ2F0aXZlIGh5cGVycGxhbmUuIAoKY2xhc3NpZnkgYXBwbGVzIGFuZCBvcmFuZ2VzLCB0cmFpbmluZyBvbiBiZXN0IGV4YW1wbGVzIG9mIGVhY2ggZnJ1aXQKCmBgYHtyIFNWTSBDbGFzc2lmaWNhdGlvbn0KCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDMgLSBDbGFzc2lmaWNhdGlvbi9TZWN0aW9uIDE2IC0gU3VwcG9ydCBWZWN0b3IgTWFjaGluZSAoU1ZNKS9SL1NvY2lhbF9OZXR3b3JrX0Fkcy5jc3YnKQoKZGF0YXNldCA9IGRhdGFzZXRbMzo1XQoKIyBFbmNvZGluZyB0aGUgdGFyZ2V0IGZlYXR1cmUgYXMgZmFjdG9yCmRhdGFzZXQkUHVyY2hhc2VkID0gZmFjdG9yKGRhdGFzZXQkUHVyY2hhc2VkLCBsZXZlbHMgPSBjKDAsIDEpKQoKIyBTcGxpdHRpbmcgdGhlIGRhdGFzZXQgaW50byB0aGUgVHJhaW5pbmcgc2V0IGFuZCBUZXN0IHNldAojIGluc3RhbGwucGFja2FnZXMoJ2NhVG9vbHMnKQpsaWJyYXJ5KGNhVG9vbHMpCgpzZXQuc2VlZCgxMjMpCgpzcGxpdCA9IHNhbXBsZS5zcGxpdChkYXRhc2V0JFB1cmNoYXNlZCwgU3BsaXRSYXRpbyA9IDAuNzUpCnRyYWluaW5nX3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBUUlVFKQp0ZXN0X3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBGQUxTRSkKCiMgRmVhdHVyZSBTY2FsaW5nCnRyYWluaW5nX3NldFstM10gPSBzY2FsZSh0cmFpbmluZ19zZXRbLTNdKQp0ZXN0X3NldFstM10gPSBzY2FsZSh0ZXN0X3NldFstM10pCgojIEZpdHRpbmcgY2xhc3NpZmllciB0byB0aGUgVHJhaW5pbmcgc2V0CiMgQ3JlYXRlIHlvdXIgY2xhc3NpZmllciBoZXJlCgojIGxpYnJhcnkgZTEwNzEgZm9yIFNWTQpsaWJyYXJ5KGUxMDcxKQoKIyByZWFkIHRoZSBkb2N1bWVudGF0aW9uCmNsYXNzaWZpZXIuU1ZNID0gc3ZtKGZvcm11bGE9IFB1cmNoYXNlZCB+IC4sCiAgICAgICAgICAgICAgICAgICAgIGRhdGE9IHRyYWluaW5nX3NldCwKICAgICAgICAgICAgICAgICAgICAgdHlwZT0gJ0MtY2xhc3NpZmljYXRpb24nLCAjIGNsYXNzaWZpY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgIGtlcm5lbD0gJ2xpbmVhcicKICAgICAgICAgICAgICAgICAgICAgKQoKCiMgUHJlZGljdGluZyB0aGUgVGVzdCBzZXQgcmVzdWx0cwp5X3ByZWQuU1ZNID0gcHJlZGljdChjbGFzc2lmaWVyLlNWTSwgbmV3ZGF0YSA9IHRlc3Rfc2V0Wy0zXSkKCiMgTWFraW5nIHRoZSBDb25mdXNpb24gTWF0cml4CmNtID0gdGFibGUodGVzdF9zZXRbLCAzXSwgeV9wcmVkLlNWTSkKY20KCgoKIz09PT09PT09PT09PT0gVmlzdWFsaXppbmcgdGhlIFRyYWluaW5nIHNldCByZXN1bHRzCmxpYnJhcnkoRWxlbVN0YXRMZWFybikKc2V0ID0gdHJhaW5pbmdfc2V0ClgxID0gc2VxKG1pbihzZXRbLCAxXSkgLSAxLCBtYXgoc2V0WywgMV0pICsgMSwgYnkgPSAwLjAxKQpYMiA9IHNlcShtaW4oc2V0WywgMl0pIC0gMSwgbWF4KHNldFssIDJdKSArIDEsIGJ5ID0gMC4wMSkKCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQoKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCgp5X2dyaWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIuU1ZNLCBuZXdkYXRhID0gZ3JpZF9zZXQpCnBsb3Qoc2V0WywgLTNdLAogICAgIG1haW4gPSAnU1ZNIENsYXNzaWZpZXIgKFRyYWluaW5nIHNldCknLAogICAgIHhsYWIgPSAnQWdlJywgCiAgICAgeWxhYiA9ICdFc3RpbWF0ZWQgU2FsYXJ5JywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQoKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAxLCAnc3ByaW5nZ3JlZW4zJywgJ3RvbWF0bycpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpCgoKIz09PT09PT09PT09PSBWaXN1YWxpemluZyB0aGUgVGVzdCBzZXQgcmVzdWx0cwoKbGlicmFyeShFbGVtU3RhdExlYXJuKQoKc2V0ID0gdGVzdF9zZXQKClgxID0gc2VxKG1pbihzZXRbLCAxXSkgLSAxLCBtYXgoc2V0WywgMV0pICsgMSwgYnkgPSAwLjAxKQpYMiA9IHNlcShtaW4oc2V0WywgMl0pIC0gMSwgbWF4KHNldFssIDJdKSArIDEsIGJ5ID0gMC4wMSkKCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQoKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCgp5X2dyaWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIuU1ZNLCAKICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9zZXQpCnBsb3Qoc2V0WywgLTNdLCBtYWluID0gJ1NWTSBDbGFzc2lmaWVyIChUZXN0IHNldCknLAogICAgIHhsYWIgPSAnQWdlJywgeWxhYiA9ICdFc3RpbWF0ZWQgU2FsYXJ5JywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQoKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAxLCAnc3ByaW5nZ3JlZW4zJywgJ3RvbWF0bycpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpCgoKYGBgICAgICAgICAgICAgIAogICAgICAgICAgICAgIAptYXBwaW5nIGRhdGFwb2ludHMgdG8gYSBIaWdoZXIgZGltZW5zaW9uYWwgc2hhcGUgc2VwYXJhdGVzIHBvaW50cyBieSBtYXBwaW5nIHRoZSBwb2ludHMgdG8gYSBhbGdlYnJhaWMgZnVuY3Rpb24gdGhlbiB0byBoYXZlIGEgaHlwZXJwbGFuZSBhIHNlcGFyYXRvciwgYnV0IHRoaXMgaXMgdmVyeSBjb21wdXRlciBpbnRlbnNpdmUgYW5kIG5vdCBwcmFjdGljYWwuCiAgICAgICAgICAgICAKICAgICAgICAgICAgCiMjICBLZXJuZWwgU1ZNCkRlY2lzaW9uIGJvdW5kYXJpZXMgZm9yIHdoZW4gZGF0YSBwb2ludHMgYXJlIGNsdXN0ZXJlZCBpbiBjaXJjbGVzIGFuZCBpcyBub3QgbGluZWFyLiAKR2F1c3NpYW4gUkJGIEtlcm5lbC4gCgpgYGB7ciBLZXJuZWwgU1ZNfQogICAgICAgICAgICAgIAogIyBJbXBvcnRpbmcgdGhlIGRhdGFzZXQKZGF0YXNldCA9IHJlYWQuY3N2KCcuL1BhcnQgMyAtIENsYXNzaWZpY2F0aW9uL1NlY3Rpb24gMTcgLSBLZXJuZWwgU1ZNL1IvU29jaWFsX05ldHdvcmtfQWRzLmNzdicpCmRhdGFzZXQgPSBkYXRhc2V0WzM6NV0KCiMgRW5jb2RpbmcgdGhlIHRhcmdldCBmZWF0dXJlIGFzIGZhY3RvcgpkYXRhc2V0JFB1cmNoYXNlZCA9IGZhY3RvcihkYXRhc2V0JFB1cmNoYXNlZCwgbGV2ZWxzID0gYygwLCAxKSkKCiMgU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdGhlIFRyYWluaW5nIHNldCBhbmQgVGVzdCBzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKbGlicmFyeShjYVRvb2xzKQpzZXQuc2VlZCgxMjMpCnNwbGl0ID0gc2FtcGxlLnNwbGl0KGRhdGFzZXQkUHVyY2hhc2VkLCBTcGxpdFJhdGlvID0gMC43NSkKdHJhaW5pbmdfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IFRSVUUpCnRlc3Rfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IEZBTFNFKQoKIyBGZWF0dXJlIFNjYWxpbmcKdHJhaW5pbmdfc2V0Wy0zXSA9IHNjYWxlKHRyYWluaW5nX3NldFstM10pCnRlc3Rfc2V0Wy0zXSA9IHNjYWxlKHRlc3Rfc2V0Wy0zXSkKCgojIEZpdHRpbmcgY2xhc3NpZmllciB0byB0aGUgVHJhaW5pbmcgc2V0CiMgQ3JlYXRlIHlvdXIgY2xhc3NpZmllciBoZXJlCgojPT09PT09PT09PT0gS2VybmVsIFNWTQpsaWJyYXJ5KGUxMDcxKQoKIyBHYXVzc2lhbiBjbGFzc2lmaWVyLCByYWRpYWwKY2xhc3NpZmllci5TVk0gPSBzdm0oZm9ybXVsYT0gUHVyY2hhc2VkIH4gLiwKICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfc2V0LAogICAgICAgICAgICAgICAgIHR5cGUgPSAiQy1jbGFzc2lmaWNhdGlvbiIsCiAgICAgICAgICAgICAgICAga2VybmVsPSAncmFkaWFsJykKCgojIFByZWRpY3RpbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKeV9wcmVkID0gcHJlZGljdChjbGFzc2lmaWVyLlNWTSwgbmV3ZGF0YSA9IHRlc3Rfc2V0Wy0zXSkKeV9wcmVkWzE6NV0KCiMgTWFraW5nIHRoZSBDb25mdXNpb24gTWF0cml4CmNtID0gdGFibGUodGVzdF9zZXRbLCAzXSwgeV9wcmVkKQpjbQoKCiM9PT09PT09PT09PT09PSBWaXN1YWxpemluZyB0aGUgVHJhaW5pbmcgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0cmFpbmluZ19zZXQKWDEgPSBzZXEobWluKHNldFssIDFdKSAtIDEsIG1heChzZXRbLCAxXSkgKyAxLCBieSA9IDAuMDEpClgyID0gc2VxKG1pbihzZXRbLCAyXSkgLSAxLCBtYXgoc2V0WywgMl0pICsgMSwgYnkgPSAwLjAxKQpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCnlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllci5TVk0sIG5ld2RhdGEgPSBncmlkX3NldCkKcGxvdChzZXRbLCAtM10sCiAgICAgbWFpbiA9ICdLZXJuZWwgU1ZNIENsYXNzaWZpZXIgKFRyYWluaW5nIHNldCknLAogICAgIHhsYWIgPSAnQWdlJywgeWxhYiA9ICdFc3RpbWF0ZWQgU2FsYXJ5JywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQpjb250b3VyKFgxLCBYMiwgbWF0cml4KGFzLm51bWVyaWMoeV9ncmlkKSwgbGVuZ3RoKFgxKSwgbGVuZ3RoKFgyKSksIGFkZCA9IFRSVUUpCnBvaW50cyhncmlkX3NldCwgcGNoID0gJy4nLCBjb2wgPSBpZmVsc2UoeV9ncmlkID09IDEsICdzcHJpbmdncmVlbjMnLCAndG9tYXRvJykpCnBvaW50cyhzZXQsIHBjaCA9IDIxLCBiZyA9IGlmZWxzZShzZXRbLCAzXSA9PSAxLCAnZ3JlZW40JywgJ3JlZDMnKSkKCiM9PT09PT09PT09PT09PT09PT0gVmlzdWFsaXppbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0ZXN0X3NldApYMSA9IHNlcShtaW4oc2V0WywgMV0pIC0gMSwgbWF4KHNldFssIDFdKSArIDEsIGJ5ID0gMC4wMSkKWDIgPSBzZXEobWluKHNldFssIDJdKSAtIDEsIG1heChzZXRbLCAyXSkgKyAxLCBieSA9IDAuMDEpCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQpjb2xuYW1lcyhncmlkX3NldCkgPSBjKCdBZ2UnLCAnRXN0aW1hdGVkU2FsYXJ5JykKeV9ncmlkID0gcHJlZGljdChjbGFzc2lmaWVyLlNWTSwgbmV3ZGF0YSA9IGdyaWRfc2V0KQpwbG90KHNldFssIC0zXSwgbWFpbiA9ICdLZXJuZWwgU1ZNIENsYXNzaWZpZXIgKFRlc3Qgc2V0KScsCiAgICAgeGxhYiA9ICdBZ2UnLCB5bGFiID0gJ0VzdGltYXRlZCBTYWxhcnknLAogICAgIHhsaW0gPSByYW5nZShYMSksIHlsaW0gPSByYW5nZShYMikpCmNvbnRvdXIoWDEsIFgyLCBtYXRyaXgoYXMubnVtZXJpYyh5X2dyaWQpLCBsZW5ndGgoWDEpLCBsZW5ndGgoWDIpKSwgYWRkID0gVFJVRSkKcG9pbnRzKGdyaWRfc2V0LCBwY2ggPSAnLicsIGNvbCA9IGlmZWxzZSh5X2dyaWQgPT0gMSwgJ3NwcmluZ2dyZWVuMycsICd0b21hdG8nKSkKcG9pbnRzKHNldCwgcGNoID0gMjEsIGJnID0gaWZlbHNlKHNldFssIDNdID09IDEsICdncmVlbjQnLCAncmVkMycpKQoKYGBgCgpDb25mdXNpb24gbWF0cml4IHNob3dzIDEwIGluY29ycmVjdCByZXN1bHRzIGFuZCA5MCBjb3JyZWN0IHJlc3VsdHMgICAgICAgICAKICAgICAgICAgICAgICAKIFRoZSBTVk0gbWFwcGVkIGRhdGEgdG8gYSAzRCBwbGFuZSwgZ3JlZW4gem9uZSBpcyB1c2VycyB3aG8gcHVyY2hhc2VkIGl0ZW0gYW5kIHJlZCB6b25lIGlzIG5vIHB1cmNoYXNlZCBpdGVtLiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCiMjIE5haXZlIEJheWVzIFRoZW9yZW0gQ2xhc3NpZmljYXRpb24KQmF5ZXMgVGhlb3JlbSBvZiBwcm9iYWJpbGl0eSBmb3JtdWxhICRQKEF8QikkID0gJFAoQnxBKSAqIFAoQSkgXGRpdiBQKEIpJCAKICAgICAgICAgICAKRXhhbXBsZTogbWFjaGluZS4xIG1ha2VzIDMwIGl0ZW1zL2hyIGFuZCBtYWNoaW5lLjIgbWFrZXMgMjAgaXRlbXMvaHIuIE91dCBvZiBhbGwgaXRlbXMgbWFkZSwgMSAlIGlzIGRlZmVjdGl2ZSwgYW5kIG91dCBvZiBhbGwgZGVmZWN0aXZlIGl0ZW1zIDUwJSBjYW1lIGZyb20gbWFjaGluZS4xIGFuZCA1MCUgZnJvbSBtYWNoaW5lLjIuICpXaGF0IGlzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgaXRlbSBtYWRlIGJ5IG1hY2hpbmUuMiBpcyBkZWZlY3RpdmU/KiAgICAgICAgIAoKLSBQKE1hY2hpbmUuMikgPSAyMC81MAotIFAoRGVmZWN0KSA9IDElCi0gUChNYWNoaW5lLjIgfCBEZWZlY3QpPSA1MCUKLSBQKERlZmVjdCB8IE1hY2hpbmUuMikgPSA/CgpQKERlZmVjdCB8IE1hY2hpbmUuMikgPSBQKE1hY2hpbmUuMiB8IERlZmVjdCkgKiBQKERlZmVjdCkgIC8gUChNYWNoaW5lLjIpClAoRGVmZWN0IHwgTWFjaGluZS4yKSA9IDAuNSAqIDAuMDEgLyAwLjQgPT0gMC4wMTI1ICgxLjI1JSkgICAgICAgICAgICAKCkV4YW1wbGUgMiBOYWl2ZSBCYXllcyAoYXNzdW1lZCBpbmRlcGVuZGVuY2Ugb2YgdmFyaWFibGVzOgp4PSBhZ2UsIHk9IHNhbGFyeSwgZGF0YXBvaW50cyBncm91cGVkOiB3YWxrcyB0byB3b3JrIG9yIGRyaXZlcyB0byB3b3JrLiBYPSBmZWF0dXJlcwpQKFdhbGtzIHwgWCkgPSBQKFggfCBXYWxrcykgKiBQKFdhbGtzKSAvIFAoWCkgICAgICAgICAKUChEcml2ZXMgfCBYKSA9IFAoWCB8IERyaXZlcykgKiBQKERyaXZlcykgLyBQKFgpICAgICAKCnJlcGVhdCBzdGVwcyBmb3IgYm90aCBXYWxrcyBhbmQgRHJpdmVzIGNsYXNzOgoKMS4gcHJpb3IgcHJvYmFiaWxpdHk6IFAoV2Fsa3MpCjIuIG1hcmdpbmFsIGxpa2VsaWhvb2Q6IFAoWCkKMy4gbGlrZWxpaG9vZDogUChYIHwgV2Fsa3MpCjQuIHBvc3RlcmlvciBwcm9iYWJpbGl0eTogUChXYWxrcyB8IFgpCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCnN0ZXAgMTogd2UgaGF2ZSBubyBkYXRhLCBzbyBjYWxjdWxhdGUgdGhlIHBvaW50cyBpbiB0aGUgd2Fsa3MgZ3JvdXAgZnJvbSBhIHBsb3QuIAotIFAoV2Fsa3MpICA9IG51bWJlciBvZiB3YWxrZXJzIC8gdG90YWwgZGF0YXBvaW50cwogIApzdGVwIDI6IHNlbGVjdCBhIHJhZGl1cyBvbiBhIHBsb3QsIGEgY2lyY2xlIHRvIGNvbnRhaW4gZGF0YXBvaW50cywgbG9vayBhdCBmZWF0dXJlcyAoYWdlIGFuZCBzYWxhcnkpIGFuZCB0aGVzZSBwb2ludHMgd2lsbCBiZSBzaW1pbGFyLiB0aGUgcHJvYmFiaWxpdHkgb2YgYSBuZXcgZGF0YXBvaW50IHdvdWxkIGZhbGwgaW50byB0aGlzIHJhZGl1cy4gQ291bnQgdGhlIG51bWJlciBvZiBwb2ludHMgaW5zaWRlIGNpcmNsZS4KLSBQKFgpID0gbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAvIHRvdGFsIG9ic2VydmF0aW9ucyAKICAKc3RlcCAzOiB1c2UgdGhlIHJhZGl1cyBjaXJjbGUgZm9yIHNpbWlsYXIgZmVhdHVyZXMgb2YgZGF0YXBvaW50cywgd2hhdCBpcyB0aGUgbGlrZWxpaG9vZCBvZiBmZWF0dXJlcyBvZiB3YWxrZXJzIGdpdmVuIHRoYXQgcGVyc29uIHdobyB3YWxrcyAoaWdub3JlIHRoZSBkcml2ZXJzKS4gQ291bnQgdGhlIG51bWJlciBvZiBkYXRhcG9pbnRzIGZvciB3YWxrZXJzIGluc2lkZSB0aGUgY2lyY2xlLiAKLSBQKFggfCBXYWxrZXJzKSA9IG51bWJlciBvZiBzaW1pbGFyIHBvaW50cyBmb3Igd2Fsa2VycyAvIHRvdGFsIG51bWJlciBvZiB3YWxrZXJzCiAgCnN0ZXAgNDogUChXYWxrcyB8IFgpID0gKDMvMTApICogKDEwLzMwKSAvICg0LzMwKSA9PSAwLjc1ICAgICg3NSUgbGlrZWxpaG9vZCBvZiBkYXRhcG9pbnQgYmVpbmcgYSBXYWxrZXIpICAgICAgIAogICAgICAgICAgICAgIApyZXBlYXQgZm9yIGRyaXZlciAgICAgICAgICAgCnN0ZXAgNDogUChEcml2ZXMgfCBYKT0gKDEvMjApICogKDIwLzMwKSAvICg0LzMwKSA9IDAuMjUgICgyNSUpICAgICAgICAgICAgIAogICAgICAgICAgICAgIApgYGB7ciBCYXllcyBUaGVvcmVtfSAgICAgICAgICAgIAogICAgICAgICAgICAgIAojIEltcG9ydGluZyB0aGUgZGF0YXNldApkYXRhc2V0ID0gcmVhZC5jc3YoJy4vUGFydCAzIC0gQ2xhc3NpZmljYXRpb24vU2VjdGlvbiAxOCAtIE5haXZlIEJheWVzL1IvU29jaWFsX05ldHdvcmtfQWRzLmNzdicpCmRhdGFzZXQgPSBkYXRhc2V0WzM6NV0KCiMgRW5jb2RpbmcgdGhlIHRhcmdldCBmZWF0dXJlIGFzIGZhY3RvcgpkYXRhc2V0JFB1cmNoYXNlZCA9IGZhY3RvcihkYXRhc2V0JFB1cmNoYXNlZCwgbGV2ZWxzID0gYygwLCAxKSkKCiMgU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdGhlIFRyYWluaW5nIHNldCBhbmQgVGVzdCBzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKbGlicmFyeShjYVRvb2xzKQpzZXQuc2VlZCgxMjMpCnNwbGl0ID0gc2FtcGxlLnNwbGl0KGRhdGFzZXQkUHVyY2hhc2VkLCBTcGxpdFJhdGlvID0gMC43NSkKdHJhaW5pbmdfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IFRSVUUpCnRlc3Rfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IEZBTFNFKQoKIyBGZWF0dXJlIFNjYWxpbmcKdHJhaW5pbmdfc2V0Wy0zXSA9IHNjYWxlKHRyYWluaW5nX3NldFstM10pCnRlc3Rfc2V0Wy0zXSA9IHNjYWxlKHRlc3Rfc2V0Wy0zXSkKCiMgRml0dGluZyBjbGFzc2lmaWVyIHRvIHRoZSBUcmFpbmluZyBzZXQKIyBDcmVhdGUgeW91ciBjbGFzc2lmaWVyIGhlcmUKCiMgPT09IEJheWVzIENsYXNzaWZpZXIKbGlicmFyeShlMTA3MSkKCiMgcHJlc3MgRjEgZm9yIGRvY3VtZW50YXRpb24gd2hlbiBtb3VzZSBpcyBvbiBmdW5jdGlvbiBuYW1lCmNsYXNzaWZpZXIuQmF5ZXMgPSBuYWl2ZUJheWVzKHg9IHRyYWluaW5nX3NldFstM10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9IHRyYWluaW5nX3NldCRQdXJjaGFzZWQpCgoKCiMgUHJlZGljdGluZyB0aGUgVGVzdCBzZXQgcmVzdWx0cwp5X3ByZWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIuQmF5ZXMsIG5ld2RhdGEgPSB0ZXN0X3NldFstM10pCgojIE1ha2luZyB0aGUgQ29uZnVzaW9uIE1hdHJpeApjbSA9IHRhYmxlKHRlc3Rfc2V0WywgM10sIHlfcHJlZCkKCiM9PT09PT09PT09PT09PSBWaXN1YWxpemluZyB0aGUgVHJhaW5pbmcgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0cmFpbmluZ19zZXQKWDEgPSBzZXEobWluKHNldFssIDFdKSAtIDEsIG1heChzZXRbLCAxXSkgKyAxLCBieSA9IDAuMDEpClgyID0gc2VxKG1pbihzZXRbLCAyXSkgLSAxLCBtYXgoc2V0WywgMl0pICsgMSwgYnkgPSAwLjAxKQpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCnlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllci5CYXllcywgbmV3ZGF0YSA9IGdyaWRfc2V0KQpwbG90KHNldFssIC0zXSwKICAgICBtYWluID0gJ05haXZlIEJheWVzIENsYXNzaWZpZXIgKFRyYWluaW5nIHNldCknLAogICAgIHhsYWIgPSAnQWdlJywgeWxhYiA9ICdFc3RpbWF0ZWQgU2FsYXJ5JywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQpjb250b3VyKFgxLCBYMiwgbWF0cml4KGFzLm51bWVyaWMoeV9ncmlkKSwgbGVuZ3RoKFgxKSwgbGVuZ3RoKFgyKSksIGFkZCA9IFRSVUUpCnBvaW50cyhncmlkX3NldCwgcGNoID0gJy4nLCBjb2wgPSBpZmVsc2UoeV9ncmlkID09IDEsICdzcHJpbmdncmVlbjMnLCAndG9tYXRvJykpCnBvaW50cyhzZXQsIHBjaCA9IDIxLCBiZyA9IGlmZWxzZShzZXRbLCAzXSA9PSAxLCAnZ3JlZW40JywgJ3JlZDMnKSkKCiM9PT09PT09PT09IFZpc3VhbGl6aW5nIHRoZSBUZXN0IHNldCByZXN1bHRzCmxpYnJhcnkoRWxlbVN0YXRMZWFybikKc2V0ID0gdGVzdF9zZXQKWDEgPSBzZXEobWluKHNldFssIDFdKSAtIDEsIG1heChzZXRbLCAxXSkgKyAxLCBieSA9IDAuMDEpClgyID0gc2VxKG1pbihzZXRbLCAyXSkgLSAxLCBtYXgoc2V0WywgMl0pICsgMSwgYnkgPSAwLjAxKQpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCnlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllci5CYXllcywgbmV3ZGF0YSA9IGdyaWRfc2V0KQpwbG90KHNldFssIC0zXSwgbWFpbiA9ICdOYWl2ZSBCYXllcyBDbGFzc2lmaWVyIChUZXN0IHNldCknLAogICAgIHhsYWIgPSAnQWdlJywgeWxhYiA9ICdFc3RpbWF0ZWQgU2FsYXJ5JywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQpjb250b3VyKFgxLCBYMiwgbWF0cml4KGFzLm51bWVyaWMoeV9ncmlkKSwgbGVuZ3RoKFgxKSwgbGVuZ3RoKFgyKSksIGFkZCA9IFRSVUUpCnBvaW50cyhncmlkX3NldCwgcGNoID0gJy4nLCBjb2wgPSBpZmVsc2UoeV9ncmlkID09IDEsICdzcHJpbmdncmVlbjMnLCAndG9tYXRvJykpCnBvaW50cyhzZXQsIHBjaCA9IDIxLCBiZyA9IGlmZWxzZShzZXRbLCAzXSA9PSAxLCAnZ3JlZW40JywgJ3JlZDMnKSkgICAgICAgICAgICAKICAgICAgICAgICAgICAKICAgICAgICAgICAgICAKYGBgICAgICAgICAgCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCiMjIERlY2lzaW9uIFRyZWUgQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgIApgYGB7ciBEZWNpc2lvbiBUcmVlfSAgICAgICAgICAKCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDMgLSBDbGFzc2lmaWNhdGlvbi9TZWN0aW9uIDE5IC0gRGVjaXNpb24gVHJlZSBDbGFzc2lmaWNhdGlvbi9SL1NvY2lhbF9OZXR3b3JrX0Fkcy5jc3YnKQoKZGF0YXNldCA9IGRhdGFzZXRbMzo1XQoKIyBFbmNvZGluZyB0aGUgdGFyZ2V0IGZlYXR1cmUgYXMgZmFjdG9yCmRhdGFzZXQkUHVyY2hhc2VkID0gZmFjdG9yKGRhdGFzZXQkUHVyY2hhc2VkLCBsZXZlbHMgPSBjKDAsIDEpKQoKIyBTcGxpdHRpbmcgdGhlIGRhdGFzZXQgaW50byB0aGUgVHJhaW5pbmcgc2V0IGFuZCBUZXN0IHNldAojIGluc3RhbGwucGFja2FnZXMoJ2NhVG9vbHMnKQpsaWJyYXJ5KGNhVG9vbHMpCnNldC5zZWVkKDEyMykKc3BsaXQgPSBzYW1wbGUuc3BsaXQoZGF0YXNldCRQdXJjaGFzZWQsIFNwbGl0UmF0aW8gPSAwLjc1KQp0cmFpbmluZ19zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gVFJVRSkKdGVzdF9zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gRkFMU0UpCgojIEZlYXR1cmUgU2NhbGluZwp0cmFpbmluZ19zZXRbLTNdID0gc2NhbGUodHJhaW5pbmdfc2V0Wy0zXSkKdGVzdF9zZXRbLTNdID0gc2NhbGUodGVzdF9zZXRbLTNdKQoKIz09PT09PT09PT09IEZpdHRpbmcgRGVjaXNpb24gVHJlZSBDbGFzc2lmaWNhdGlvbiB0byB0aGUgVHJhaW5pbmcgc2V0CiMgaW5zdGFsbC5wYWNrYWdlcygncnBhcnQnKQpsaWJyYXJ5KHJwYXJ0KQpjbGFzc2lmaWVyID0gcnBhcnQoZm9ybXVsYSA9IFB1cmNoYXNlZCB+IC4sCiAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfc2V0KQoKIyBQcmVkaWN0aW5nIHRoZSBUZXN0IHNldCByZXN1bHRzCnlfcHJlZCA9IHByZWRpY3QoY2xhc3NpZmllciwgbmV3ZGF0YSA9IHRlc3Rfc2V0Wy0zXSwgdHlwZSA9ICdjbGFzcycpCgojIE1ha2luZyB0aGUgQ29uZnVzaW9uIE1hdHJpeApjbSA9IHRhYmxlKHRlc3Rfc2V0WywgM10sIHlfcHJlZCkKCiMgPT09PT09PT09PSBWaXN1YWxpemluZyB0aGUgVHJhaW5pbmcgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0cmFpbmluZ19zZXQKWDEgPSBzZXEobWluKHNldFssIDFdKSAtIDEsIG1heChzZXRbLCAxXSkgKyAxLCBieSA9IDAuMDEpClgyID0gc2VxKG1pbihzZXRbLCAyXSkgLSAxLCBtYXgoc2V0WywgMl0pICsgMSwgYnkgPSAwLjAxKQpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCnlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllciwgbmV3ZGF0YSA9IGdyaWRfc2V0LCB0eXBlID0gJ2NsYXNzJykKcGxvdChzZXRbLCAtM10sCiAgICAgbWFpbiA9ICdEZWNpc2lvbiBUcmVlIENsYXNzaWZpY2F0aW9uIChUcmFpbmluZyBzZXQpJywKICAgICB4bGFiID0gJ0FnZScsIHlsYWIgPSAnRXN0aW1hdGVkIFNhbGFyeScsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgeWxpbSA9IHJhbmdlKFgyKSkKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAxLCAnc3ByaW5nZ3JlZW4zJywgJ3RvbWF0bycpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpCgojPT09PT09PT09PT09IFZpc3VhbGl6aW5nIHRoZSBUZXN0IHNldCByZXN1bHRzCmxpYnJhcnkoRWxlbVN0YXRMZWFybikKc2V0ID0gdGVzdF9zZXQKWDEgPSBzZXEobWluKHNldFssIDFdKSAtIDEsIG1heChzZXRbLCAxXSkgKyAxLCBieSA9IDAuMDEpClgyID0gc2VxKG1pbihzZXRbLCAyXSkgLSAxLCBtYXgoc2V0WywgMl0pICsgMSwgYnkgPSAwLjAxKQpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnQWdlJywgJ0VzdGltYXRlZFNhbGFyeScpCnlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllciwgbmV3ZGF0YSA9IGdyaWRfc2V0LCB0eXBlID0gJ2NsYXNzJykKcGxvdChzZXRbLCAtM10sIG1haW4gPSAnRGVjaXNpb24gVHJlZSBDbGFzc2lmaWNhdGlvbiAoVGVzdCBzZXQpJywKICAgICB4bGFiID0gJ0FnZScsIHlsYWIgPSAnRXN0aW1hdGVkIFNhbGFyeScsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgeWxpbSA9IHJhbmdlKFgyKSkKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAxLCAnc3ByaW5nZ3JlZW4zJywgJ3RvbWF0bycpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpCgojIFBsb3R0aW5nIHRoZSB0cmVlCnBsb3QoY2xhc3NpZmllcikKdGV4dChjbGFzc2lmaWVyKQoKYGBgCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgClRoZSBEZWNpc2lvbiBUcmVlIENsYXNzaWZpZXIgZmlyc3QgY29uZGl0aW9uIGlzIGFnZSA8IDQ0LjUgYW5kIHNhbGFyeSA8ICQ5MCwwMDAsIG91ciBtb2RlbCBjbGFzc2lmaWVzIHBlcnNvbiBhcyB3aWxsIG5vdCBidXkgdGhlIGl0ZW0sIGlmIHNhbGFyeSBpcyA+ICQ5MCwwMDAgcGVyc29uIHdpbGwgYnV5IHRoZSBpdGVtIC4gKlRoaXMgd2FzIG1hZGUgYnkgbm90IHJ1bm5pbmcgdGhlIGNvZGUgb2YgZmVhdHVyZSBzY2FsaW5nIGFuZCB2aXN1YWxpemF0aW9ucywgYnV0IHRoZSBjbGFzc2lmaWVyIGFuZCBwbG90dGluZyBmdW5jdGlvbi4qICAgICAgICAgIAogICAgICAgICAgICAgIAojIyBSYW5kb20gRm9yZXN0IENsYXNzaWZpY2F0aW9uCgpFbnNlbWJsZSBMZWFybmluZyA9IHRha2UgbXVsdGlwbGUgYWxnb3JpdGhtcyB0byBtYWtlIHBvd2VyZnVsIGFsZ29yaXRobQoKc3RlcCAxOiBwaWNrIGF0IHJhbmRvbSBrIGRhdGEgcG9pbnRzIGZyb20gdHJhaW5pbmcgc2V0CgpzdGVwIDI6IGJ1aWxkIGRlY2lzaW9uIHRyZWUgYXNzb2NpYXRlZCB0byB0aGVzZSBrIGRhdGEgcG9pbnRzCgpzdGVwIDM6IGNob29zZSB0aGUgbnVtYmVyIE50cmVlIG9mIHRyZWVzIHlvdSB3YW50IHRvIGJ1aWxkIGFuZCByZXBlYXQgc3RlcHMgMSAmIDIKCnN0ZXAgNDogZm9yIGEgbmV3IGRhdGEgcG9pbnQsIG1ha2UgZWFjaCBvbmUgb2YgeW91ciBOdHJlZSB0cmVlcyBwcmVkaWN0IHRoZSB2YWx1ZSBvZiBZIGZvciB0aGUgZGF0YSBwb2ludCBpbiBxdWVzdGlvbiwgYW5kIGFzc2lnbiB0aGUgbmV3IGRhdGEgcG9pbnQgdGhlIGF2ZXJhZ2UgYWNyb3NzIGFsbCBvZiB0aGUgcHJlZGljdGVkIFkgdmFsdWVzCiAgICAgICAgICAgICAgClRoaXMgbW9kZWwgaXMgdXNlZCBmb3IgcmVtb3RlIGNvbnRyb2xsZXIgZnJlZSBnYW1pbmcgY29uc29sZXMgKE1pY3Jvc29mdCBjb25uZWN0KSAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCmBgYHtyIFJhbmRvbSBGb3Jlc3R9ICAgICAgICAgICAgICAKIyBJbXBvcnRpbmcgdGhlIGRhdGFzZXQKZGF0YXNldCA9IHJlYWQuY3N2KCcuL1BhcnQgMyAtIENsYXNzaWZpY2F0aW9uL1NlY3Rpb24gMjAgLSBSYW5kb20gRm9yZXN0IENsYXNzaWZpY2F0aW9uL1IvU29jaWFsX05ldHdvcmtfQWRzLmNzdicpCgpkYXRhc2V0ID0gZGF0YXNldFszOjVdCgojIEVuY29kaW5nIHRoZSB0YXJnZXQgZmVhdHVyZSBhcyBmYWN0b3IKZGF0YXNldCRQdXJjaGFzZWQgPSBmYWN0b3IoZGF0YXNldCRQdXJjaGFzZWQsIGxldmVscyA9IGMoMCwgMSkpCgojIFNwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIHRoZSBUcmFpbmluZyBzZXQgYW5kIFRlc3Qgc2V0CiMgaW5zdGFsbC5wYWNrYWdlcygnY2FUb29scycpCmxpYnJhcnkoY2FUb29scykKc2V0LnNlZWQoMTIzKQpzcGxpdCA9IHNhbXBsZS5zcGxpdChkYXRhc2V0JFB1cmNoYXNlZCwgU3BsaXRSYXRpbyA9IDAuNzUpCnRyYWluaW5nX3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBUUlVFKQp0ZXN0X3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBGQUxTRSkKCiMgRmVhdHVyZSBTY2FsaW5nCnRyYWluaW5nX3NldFstM10gPSBzY2FsZSh0cmFpbmluZ19zZXRbLTNdKQp0ZXN0X3NldFstM10gPSBzY2FsZSh0ZXN0X3NldFstM10pCgojIEZpdHRpbmcgUmFuZG9tIEZvcmVzdCBDbGFzc2lmaWNhdGlvbiB0byB0aGUgVHJhaW5pbmcgc2V0CiMgaW5zdGFsbC5wYWNrYWdlcygncmFuZG9tRm9yZXN0JykKbGlicmFyeShyYW5kb21Gb3Jlc3QpCnNldC5zZWVkKDEyMykKY2xhc3NpZmllciA9IHJhbmRvbUZvcmVzdCh4ID0gdHJhaW5pbmdfc2V0Wy0zXSwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gdHJhaW5pbmdfc2V0JFB1cmNoYXNlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMCkKCiMgUHJlZGljdGluZyB0aGUgVGVzdCBzZXQgcmVzdWx0cwp5X3ByZWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIsIG5ld2RhdGEgPSB0ZXN0X3NldFstM10pCgojIE1ha2luZyB0aGUgQ29uZnVzaW9uIE1hdHJpeApjbSA9IHRhYmxlKHRlc3Rfc2V0WywgM10sIHlfcHJlZCkKCgoKIz09PT09PT09PT0gVmlzdWFsaXppbmcgdGhlIFRyYWluaW5nIHNldCByZXN1bHRzCmxpYnJhcnkoRWxlbVN0YXRMZWFybikKc2V0ID0gdHJhaW5pbmdfc2V0ClgxID0gc2VxKG1pbihzZXRbLCAxXSkgLSAxLCBtYXgoc2V0WywgMV0pICsgMSwgYnkgPSAwLjAxKQpYMiA9IHNlcShtaW4oc2V0WywgMl0pIC0gMSwgbWF4KHNldFssIDJdKSArIDEsIGJ5ID0gMC4wMSkKZ3JpZF9zZXQgPSBleHBhbmQuZ3JpZChYMSwgWDIpCmNvbG5hbWVzKGdyaWRfc2V0KSA9IGMoJ0FnZScsICdFc3RpbWF0ZWRTYWxhcnknKQp5X2dyaWQgPSBwcmVkaWN0KGNsYXNzaWZpZXIsIGdyaWRfc2V0KQoKcGxvdChzZXRbLCAtM10sCiAgICAgbWFpbiA9ICdSYW5kb20gRm9yZXN0IENsYXNzaWZpY2F0aW9uIChUcmFpbmluZyBzZXQpJywKICAgICB4bGFiID0gJ0FnZScsIHlsYWIgPSAnRXN0aW1hdGVkIFNhbGFyeScsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgeWxpbSA9IHJhbmdlKFgyKSkKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAxLCAnc3ByaW5nZ3JlZW4zJywgJ3RvbWF0bycpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpCgojPT09PT09PT0gVmlzdWFsaXppbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0ZXN0X3NldApYMSA9IHNlcShtaW4oc2V0WywgMV0pIC0gMSwgbWF4KHNldFssIDFdKSArIDEsIGJ5ID0gMC4wMSkKWDIgPSBzZXEobWluKHNldFssIDJdKSAtIDEsIG1heChzZXRbLCAyXSkgKyAxLCBieSA9IDAuMDEpCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQpjb2xuYW1lcyhncmlkX3NldCkgPSBjKCdBZ2UnLCAnRXN0aW1hdGVkU2FsYXJ5JykKeV9ncmlkID0gcHJlZGljdChjbGFzc2lmaWVyLCBncmlkX3NldCkKcGxvdChzZXRbLCAtM10sIG1haW4gPSAnUmFuZG9tIEZvcmVzdCBDbGFzc2lmaWNhdGlvbiAoVGVzdCBzZXQpJywKICAgICB4bGFiID0gJ0FnZScsIHlsYWIgPSAnRXN0aW1hdGVkIFNhbGFyeScsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgeWxpbSA9IHJhbmdlKFgyKSkKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAxLCAnc3ByaW5nZ3JlZW4zJywgJ3RvbWF0bycpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpCgoKCiMgQ2hvb3NpbmcgdGhlIG51bWJlciBvZiB0cmVlcwpwbG90KGNsYXNzaWZpZXIpCgpgYGAKICAgICAgICAgICAgICAKICAgICAgICAgICAgICAKICAgICAgICAgICAgICAKIyBDbGFzc2lmaWNhdGlvbiBNb2RlbCBFdmFsdWF0aW9ucyAgICAgICAgICAgIAoKIyMgRmFsc2UgUG9zaXRpdmVzICYgRmFsc2UgTmVnYXRpdmVzCkZhbHNlIFBvc2l0aXZlIChUeXBlIDEgZXJyb3IpIHt3YXJuaW5nfSwgRmFsc2UgTmVnYXRpdmUgKHR5cGUgMiBlcnJvcikge25vdGhpbmcgdG8gc2VlIGJ1dCBkaXNhc3RlciBoYXBwZW5zfQogICAgICAgICAgICAgIAojIyBDb25mdXNpb24gTWF0cml4Cgp5X3ByZWQKYGBge30KfCAgICAgUHJlZC4gfCAgMCAgfCAxICB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IGFjdHVhbCAgMCB8ICA1NiB8ICA4IHwKfCBhY3R1YWwgIDEgfCAgNyAgfCAyOSB8CmBgYCAgICAgICAgICAgIApBY2N1cmFjeSBSYXRlID0gY29ycmVjdCAvIHRvdGFsIC4gNTYrMjk9IDg1ICA4NS8xMDA9PSA4NSUgICAgICAgICAKRXJyb3IgUmF0ZSA9IHdyb25nL3RvdGFsIC4gNys4PTE1ICAxNS8xMDA9IDE1JSAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCiMjIEN1bXVsYXRpdmUgQWNjdXJhY3kgUHJvZmlsZSAoQ0FQKQpUaGUgY3VydmVkIGxpbmUgYWJvdmUgdGhlIGxpbmVhciBsaW5lIG9uIGEgcGxvdCwgQUtBIEdhaW4gQ2hhcnQuICAgICAgICAgIApDQVAgQW5hbHlzaXM6IGFyZWEgdW5kZXIgcGVyZmVjdCBtb2RlbCBsaW5lIGFuZCB1bmRlciByZWQgbGluZS4gCiAgICAgICAgICAgICAgClJPQyBpcyBSZWNlaXZlciBPcGVyYXRpbmcgQ2hhcmFjdGVyaXN0aWMgKG5vdCB0aGUgc2FtZSkgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCkVuZCBvZiBQYXJ0IDMgLSBDbGFzc2lmaWNhdGlvbgoKPGhyPgoKIyAqKkNsdXN0ZXJpbmcqKgpJbiBDbHVzdGVyaW5nIHlvdSBkb27igJl0IGtub3cgd2hhdCB5b3UgYXJlIGxvb2tpbmcgZm9yLCBhbmQgeW91IGFyZSB0cnlpbmcgdG8gaWRlbnRpZnkgc29tZSBzZWdtZW50cyBvciBjbHVzdGVycyBpbiB5b3VyIGRhdGEuIFdoZW4geW91IHVzZSBjbHVzdGVyaW5nIGFsZ29yaXRobXMgb24geW91ciBkYXRhc2V0LCB1bmV4cGVjdGVkIHRoaW5ncyBjYW4gc3VkZGVubHkgcG9wIHVwIGxpa2Ugc3RydWN0dXJlcywgY2x1c3RlcnMgYW5kIGdyb3VwaW5ncyB5b3Ugd291bGQgaGF2ZSBuZXZlciB0aG91Z2h0IG9mIG90aGVyd2lzZQoKICAgICAgICAgICAgICAKIyMgSy1NZWFucyBDbHVzdGVyaW5nClRoaXMgZmluZHMgdGhlIGNsdXN0ZXJzIGZvciB5b3UuCgpQcm9jZXNzOgoKLSBzdGVwIDEuIGNob29zZSB0aGUgbnVtYmVyIG9mIEsgb2YgY2x1c3RlcnMKLSBzdGVwIDIuIHNlbGVjdCBhdCByYW5kb20gayBwb2ludHMsIHRoZSBjZW50cm9pZHMKLSBzdGVwIDMuIGFzc2lnbiBlYWNoIGRhdGEgcG9pbnQgdG8gdGhlIGNsb3Nlc3QgY2VudHJvaWQgPT4gZm9ybXMgY2x1c3RlcnMKLSBzdGVwIDQuIGNvbXB1dGUgYW5kIHBsYWNlIHRoZSBuZXcgY2VudHJvaWQgb2YgZWFjaCBjbHVzdGVyCi0gc3RlcCA1LiByZWFzc2lnbiBlYWNoIGRhdGFwb2ludCB0byB0aGUgbmV3IGNsb3Nlc3QgY2VudHJvaWQsIHN0ZXAgNCBhbmQgYmFjaywgdGhlbiBmaW5pc2hlZAoKCnNlbGVjdGluZyBrIHVzaW5nIHRoZSAiZWxib3cgbWV0aG9kIiBrPTMgb3IgNCBiYXNlZCBvbiBhIHBsb3QgbGluZQoKYGBge3IgSyBNZWFuc30KCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDQgLSBDbHVzdGVyaW5nL1NlY3Rpb24gMjQgLSBLLU1lYW5zIENsdXN0ZXJpbmcvUi9NYWxsX0N1c3RvbWVycy5jc3YnKQpkYXRhc2V0ID0gZGF0YXNldFs0OjVdCgojIFNwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIHRoZSBUcmFpbmluZyBzZXQgYW5kIFRlc3Qgc2V0CiMgaW5zdGFsbC5wYWNrYWdlcygnY2FUb29scycpCiMgbGlicmFyeShjYVRvb2xzKQojIHNldC5zZWVkKDEyMykKIyBzcGxpdCA9IHNhbXBsZS5zcGxpdChkYXRhc2V0JERlcGVuZGVudFZhcmlhYmxlLCBTcGxpdFJhdGlvID0gMC44KQojIHRyYWluaW5nX3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBUUlVFKQojIHRlc3Rfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IEZBTFNFKQoKIyBGZWF0dXJlIFNjYWxpbmcKIyB0cmFpbmluZ19zZXQgPSBzY2FsZSh0cmFpbmluZ19zZXQpCiMgdGVzdF9zZXQgPSBzY2FsZSh0ZXN0X3NldCkKCiMgVXNpbmcgdGhlIGVsYm93IG1ldGhvZCB0byBmaW5kIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycwpzZXQuc2VlZCg2KQp3Y3NzID0gdmVjdG9yKCkKZm9yIChpIGluIDE6MTApIHdjc3NbaV0gPSBzdW0oa21lYW5zKGRhdGFzZXQsIGkpJHdpdGhpbnNzKQpwbG90KDE6MTAsCiAgICAgd2NzcywKICAgICB0eXBlID0gJ2InLAogICAgIG1haW4gPSBwYXN0ZSgnVGhlIEVsYm93IE1ldGhvZCcpLAogICAgIHhsYWIgPSAnTnVtYmVyIG9mIGNsdXN0ZXJzJywKICAgICB5bGFiID0gJ1dDU1MnKQoKIyBGaXR0aW5nIEstTWVhbnMgdG8gdGhlIGRhdGFzZXQKc2V0LnNlZWQoMjkpCmttZWFucyA9IGttZWFucyh4ID0gZGF0YXNldCwgY2VudGVycyA9IDUpICMgY2x1c3RlciA9PSBjZW50ZXJzCnlfa21lYW5zID0ga21lYW5zJGNsdXN0ZXIKCiMgVmlzdWFsaXNpbmcgdGhlIGNsdXN0ZXJzCmxpYnJhcnkoY2x1c3RlcikKY2x1c3Bsb3QoZGF0YXNldCwgICAKICAgICAgICAgeV9rbWVhbnMsCiAgICAgICAgIGxpbmVzID0gMCwKICAgICAgICAgc2hhZGUgPSBUUlVFLAogICAgICAgICBjb2xvciA9IFRSVUUsCiAgICAgICAgIGxhYmVscyA9IDIsCiAgICAgICAgIHBsb3RjaGFyID0gRkFMU0UsCiAgICAgICAgIHNwYW4gPSBUUlVFLAogICAgICAgICBtYWluID0gcGFzdGUoJ0NsdXN0ZXJzIG9mIGN1c3RvbWVycycpLAogICAgICAgICB4bGFiID0gJ0FubnVhbCBJbmNvbWUnLAogICAgICAgICB5bGFiID0gJ1NwZW5kaW5nIFNjb3JlJykKCmBgYAoKCiMjIEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIApBZ2dsb21lcmF0aXZlIChib3R0b20tdXApIGFuZCBEaXZpc2l2ZSAodG9wLWRvd24pCgpEZW5kcm9ncmFtcyAoc2VjdGlvbiAyNzogdmlkZW8gMTc4LCAxNzkpCgpgYGB7ciBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZ30KCiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDQgLSBDbHVzdGVyaW5nL1NlY3Rpb24gMjUgLSBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZy9SL01hbGxfQ3VzdG9tZXJzLmNzdicpCmRhdGFzZXQgPSBkYXRhc2V0WzQ6NV0KCiMgU3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdGhlIFRyYWluaW5nIHNldCBhbmQgVGVzdCBzZXQKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYVRvb2xzJykKIyBsaWJyYXJ5KGNhVG9vbHMpCiMgc2V0LnNlZWQoMTIzKQojIHNwbGl0ID0gc2FtcGxlLnNwbGl0KGRhdGFzZXQkRGVwZW5kZW50VmFyaWFibGUsIFNwbGl0UmF0aW8gPSAwLjgpCiMgdHJhaW5pbmdfc2V0ID0gc3Vic2V0KGRhdGFzZXQsIHNwbGl0ID09IFRSVUUpCiMgdGVzdF9zZXQgPSBzdWJzZXQoZGF0YXNldCwgc3BsaXQgPT0gRkFMU0UpCgojIEZlYXR1cmUgU2NhbGluZwojIHRyYWluaW5nX3NldCA9IHNjYWxlKHRyYWluaW5nX3NldCkKIyB0ZXN0X3NldCA9IHNjYWxlKHRlc3Rfc2V0KQoKIyBVc2luZyB0aGUgZGVuZHJvZ3JhbSB0byBmaW5kIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycwpkZW5kcm9ncmFtID0gaGNsdXN0KGQgPSBkaXN0KGRhdGFzZXQsIG1ldGhvZCA9ICdldWNsaWRlYW4nKSwgbWV0aG9kID0gJ3dhcmQuRCcpCnBsb3QoZGVuZHJvZ3JhbSwKICAgICBtYWluID0gcGFzdGUoJ0RlbmRyb2dyYW0nKSwKICAgICB4bGFiID0gJ0N1c3RvbWVycycsCiAgICAgeWxhYiA9ICdFdWNsaWRlYW4gZGlzdGFuY2VzJykKCiMgRml0dGluZyBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyB0byB0aGUgZGF0YXNldApoYyA9IGhjbHVzdChkID0gZGlzdChkYXRhc2V0LCBtZXRob2QgPSAnZXVjbGlkZWFuJyksIG1ldGhvZCA9ICd3YXJkLkQnKQp5X2hjID0gY3V0cmVlKGhjLCA1KQoKIyBWaXN1YWxpc2luZyB0aGUgY2x1c3RlcnMKbGlicmFyeShjbHVzdGVyKQpjbHVzcGxvdChkYXRhc2V0LAogICAgICAgICB5X2hjLAogICAgICAgICBsaW5lcyA9IDAsCiAgICAgICAgIHNoYWRlID0gVFJVRSwKICAgICAgICAgY29sb3IgPSBUUlVFLAogICAgICAgICBsYWJlbHM9IDIsCiAgICAgICAgIHBsb3RjaGFyID0gRkFMU0UsCiAgICAgICAgIHNwYW4gPSBUUlVFLAogICAgICAgICBtYWluID0gcGFzdGUoJ0NsdXN0ZXJzIG9mIGN1c3RvbWVycycpLAogICAgICAgICB4bGFiID0gJ0FubnVhbCBJbmNvbWUnLAogICAgICAgICB5bGFiID0gJ1NwZW5kaW5nIFNjb3JlJykKCgpgYGAKCgoKCgoKCgoKIyBBc3NvY2lhdGlvbiBSdWxlCiJwZW9wbGUgd2hvIGJvdWdodCBYIGFsc28gYm91Z2h0IFkiCgpBcHJpb3JpIGFsZ29yaXRobQpgYGB7fQoKbW92aWUgcmVjb21tZW5kYXRpb246IAogIHN1cHBvcnQoTSkgPSAjIHVzZXIgd2F0Y2hsaXN0IGNvbnRhaW5pbmcgTSAvICMgdXNlciB3YXRjaGxpc3RzCgptb3ZpZSByZWNvbW1lbmRhdGlvbjogCiAgY29uZmlkZW5jZShNMSAtPiBNMikgPSAjIHVzZXIgd2F0Y2hsaXN0IGNvbnRhaW5pbmcgTTEsIE0yIC8gIyB1c2VyIHdhdGNobGlzdHMgY29udGFpbmluZyBNMQoKbW92aWUgcmVjb21tZW5kYXRpb246IAogIGxpZnQoTTEgLT4gTTIpID0gY29uZmlkZW5jZSBNMSwgTTIgLyBzdXBwb3J0IE0yCiAgCiAgCm1hcmtldCBiYXNrZXQgb3B0aW1pemF0aW9uOiAKICBzdXBwb3J0KEopID0gIyB0cmFuc2FjdGlvbnMgY29udGFpbmluZyBKIC8gIyB0cmFuc2FjdGlvbnMKICAKbWFya2V0IGJhc2tldCBvcHRpbWl6YXRpb246IAogIGNvbmZpZGVuY2UoSjEgLT4gSjIpID0gIyB0cmFuc2FjdGlvbnMgY29udGFpbmluZyBKMSwgSjIgLyAjIHRyYW5zYWN0aW9ucyBjb250YWluaW5nIEoxCiAgCm1hcmtldCBiYXNrZXQgb3B0aW1pemF0aW9uOiAKICB7d2hhdCBpcyB0aGUgcmFuZG9tIGxpa2VsaWhvb2QgdGhhdCBwZXJzb24gbGlrZXMgdGhpcyBpdGVtPyBMaWZ0IGlzIHRoZSBpbXByb3ZlbWVudCByZWNvbW1lbmRhdGlvbn0KICBsaWZ0KEoxIC0+IEoyKSA9IGNvbmZpZGVuY2UgSjEsIEoyIC8gc3VwcG9ydCBKMgoKYGBgCgpQcm9jZXNzOgoKMS4gc2V0IGEgbWluIHN1cHBvcnQgYW5kIGNvbmZpZGVuY2UKMi4gdGFrZSBhbGwgdGhlIHN1YnNldHMgaW4gdHJhbnNhY3Rpb25zIGhhdmluZyBoaWdoZXIgc3VwcG9ydCB0aGFuIG1pbiBzdXBwb3J0CjMuIHRha2UgYWxsIHRoZSBydWxlcyBvZiB0aGVzZSBzdWJzZXRzIGhhdmluZyBoaWdoZXIgY29uZmlkZW5jZSB0aGFuIG1pbiBjb25maWRlbmNlCjQuIHNvcnQgdGhlIHJ1bGVzIGJ5IGRlY3JlYXNpbmcgbGlmdCwgaGlnaGVzdCBsaWZ0IGlzIHRoZSB2YWx1ZSB5b3Ugd2FudCAKCmBgYHtyIEFzc29jaWF0aW9uIFJ1bGV9CiMgQXByaW9yaQoKIyBEYXRhIFByZXByb2Nlc3NpbmcKIyBpbnN0YWxsLnBhY2thZ2VzKCdhcnVsZXMnKQpsaWJyYXJ5KGFydWxlcykKCmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDUgLSBBc3NvY2lhdGlvbiBSdWxlIExlYXJuaW5nL1NlY3Rpb24gMjggLSBBcHJpb3JpL1IvTWFya2V0X0Jhc2tldF9PcHRpbWlzYXRpb24uY3N2JywgaGVhZGVyID0gRkFMU0UpCgojIHNwYXJzZSBtYXRyaXgKZGF0YXNldCA9IHJlYWQudHJhbnNhY3Rpb25zKCcuL1BhcnQgNSAtIEFzc29jaWF0aW9uIFJ1bGUgTGVhcm5pbmcvU2VjdGlvbiAyOCAtIEFwcmlvcmkvUi9NYXJrZXRfQmFza2V0X09wdGltaXNhdGlvbi5jc3YnLCBzZXAgPSAnLCcsIHJtLmR1cGxpY2F0ZXMgPSBUUlVFKQoKc3VtbWFyeShkYXRhc2V0KQppdGVtRnJlcXVlbmN5UGxvdChkYXRhc2V0LCB0b3BOID0gMzApCgojIFRyYWluaW5nIEFwcmlvcmkgb24gdGhlIGRhdGFzZXQKCiMgaXRlbXMgYm91Z2h0IDN4J3MgYSBkYXkgZGl2aWRlZCBieSB0b3RhbCBwcm9kdWN0cyA9IHszKjcvNzUwMH0gPSAwLjAyOCA9PiAwLjAwMwojIGl0ZW1zIGJvdWdodCA0eCdzIGEgZGF5IGRpdmlkZWQgYnkgdG90YWwgcHJvZHVjdHMgPSB7NCo3Lzc1MDB9ID0gMC4wMDM3ID0+IDAuMDA0CiMgY29uZmlkZW5jZSB2YWx1ZSBpcyBhcmJpdHJhcnkgY2hvaWNlIAojIApydWxlcyA9IGFwcmlvcmkoZGF0YSA9IGRhdGFzZXQsIAogICAgICAgICAgICAgICAgcGFyYW1ldGVyID0gbGlzdChzdXBwb3J0ID0gMC4wMDQsICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlkZW5jZSA9IDAuMikpICMgdXNlIHNtYWxsIHZhbHVlcyBmb3IgbW9yZSBydWxlcwoKIyBWaXN1YWxpemluZyB0aGUgcmVzdWx0cwojIGdldCB0aGUgaGlnaGVzdCBydWxlcyBieSBsaWZ0Cmluc3BlY3Qoc29ydChydWxlcywgYnkgPSAnbGlmdCcpWzE6MTBdKQoKYGBgCgp0aGUgcnVsZXMgc2hvdyB0aGF0IHBlb3BsZSB3aG8gYm91Z2h0IHtsaWdodCBjcmVhbX0gd2lsbCBidXkge2NoaWNrZW59IDI5JSBbQ29uZmlkZW5jZV0gb2YgdGhlIGNhc2VzIHdpdGggYSBsaWZ0IHZhbHVlIG9mIDQuODQKCgojIyBFY2xhdAp0aGlzIGFsZ29yaXRobSBpcyBzaW1pbGFyIGFzIGFib3ZlLCBidXQgaXQgb25seSBoYXMgdGhlIHN1cHBvcnQgdmFyaWFibGUgdXNpbmcgc2V0cwoKc2ltcGxlIHJlc3VsdHMgb2YgaXRlbXMgY29tbW9ubHkgcHVyY2hhc2VkIHRvZ2V0aGVyIHVzaW5nIHRoZSBzdXBwb3J0IHBhcmFtZXRlcgpgYGB7ciBFY2xhdH0KCiMgRGF0YSBQcmVwcm9jZXNzaW5nCiMgaW5zdGFsbC5wYWNrYWdlcygnYXJ1bGVzJykKbGlicmFyeShhcnVsZXMpCmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDUgLSBBc3NvY2lhdGlvbiBSdWxlIExlYXJuaW5nL1NlY3Rpb24gMjkgLSBFY2xhdC9SL01hcmtldF9CYXNrZXRfT3B0aW1pc2F0aW9uLmNzdicpCgojIHNwYXJzZSBtYXRyaXgKZGF0YXNldCA9IHJlYWQudHJhbnNhY3Rpb25zKCcuL1BhcnQgNSAtIEFzc29jaWF0aW9uIFJ1bGUgTGVhcm5pbmcvU2VjdGlvbiAyOSAtIEVjbGF0L1IvTWFya2V0X0Jhc2tldF9PcHRpbWlzYXRpb24uY3N2Jywgc2VwID0gJywnLCBybS5kdXBsaWNhdGVzID0gVFJVRSkKCnN1bW1hcnkoZGF0YXNldCkKCml0ZW1GcmVxdWVuY3lQbG90KGRhdGFzZXQsIHRvcE4gPSAxMCkKCiMgVHJhaW5pbmcgRWNsYXQgb24gdGhlIGRhdGFzZXQKcnVsZXMgPSBlY2xhdChkYXRhID0gZGF0YXNldCwgcGFyYW1ldGVyID0gbGlzdChzdXBwb3J0ID0gMC4wMDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmxlbiA9IDIpKSAjIG1pbiBudW1iZXIgb2YgcHVyY2hhc2VkIGl0ZW1zIGJvdWdodCB0b2dldGhlcgoKIyBWaXN1YWxpc2luZyB0aGUgcmVzdWx0cwppbnNwZWN0KHNvcnQocnVsZXMsIGJ5ID0gJ3N1cHBvcnQnKVsxOjEwXSkKCgpgYGAKICAgICAgICAgICAgICAKICAgICAgCkVuZCBvZiBzZWN0aW9uCjxocj4KICAgICAgCgojICoqUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKSoqClVuc3VwZXJ2aXNlZCBhbGdvcml0aG0gZm9yIDogbm9pc2UgZmlsdGVyaW5nLCB2aXN1YWxpemF0aW9uLCBmZWF0dXJlIGV4dHJhY3Rpb24sIHRpbWUgc2VyaWVzIHByZWRpY3Rpb25zLCBnZW5lIGRhdGEgYW5hbHlzaXMuCiAgCkdvYWw6IGlkZW50aWZ5IHBhdHRlcm5zIGluIGRhdGEsIGRldGVjdCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMgIGJ5IHJlZHVjaW5nIHRoZSBkaW1lbnNpb25zCgpgYGB7ciBQQ0F9CiMgSW1wb3J0aW5nIHRoZSBkYXRhc2V0CmRhdGFzZXQgPSByZWFkLmNzdignLi9QYXJ0IDkgLSBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24vU2VjdGlvbiA0MyAtIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkvUi9XaW5lLmNzdicpCgojIFNwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIHRoZSBUcmFpbmluZyBzZXQgYW5kIFRlc3Qgc2V0CiMgaW5zdGFsbC5wYWNrYWdlcygnY2FUb29scycpCmxpYnJhcnkoY2FUb29scykKc2V0LnNlZWQoMTIzKQpzcGxpdCA9IHNhbXBsZS5zcGxpdChkYXRhc2V0JEN1c3RvbWVyX1NlZ21lbnQsIFNwbGl0UmF0aW8gPSAwLjgpCnRyYWluaW5nX3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBUUlVFKQp0ZXN0X3NldCA9IHN1YnNldChkYXRhc2V0LCBzcGxpdCA9PSBGQUxTRSkKCiMgRmVhdHVyZSBTY2FsaW5nCnRyYWluaW5nX3NldFstMTRdID0gc2NhbGUodHJhaW5pbmdfc2V0Wy0xNF0pCnRlc3Rfc2V0Wy0xNF0gPSBzY2FsZSh0ZXN0X3NldFstMTRdKQoKIyBBcHBseWluZyBQQ0EKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYXJldCcpCmxpYnJhcnkoY2FyZXQpCiMgaW5zdGFsbC5wYWNrYWdlcygnZTEwNzEnKQpsaWJyYXJ5KGUxMDcxKQpwY2EgPSBwcmVQcm9jZXNzKHggPSB0cmFpbmluZ19zZXRbLTE0XSwgbWV0aG9kID0gJ3BjYScsIHBjYUNvbXAgPSAyKQp0cmFpbmluZ19zZXQgPSBwcmVkaWN0KHBjYSwgdHJhaW5pbmdfc2V0KQp0cmFpbmluZ19zZXQgPSB0cmFpbmluZ19zZXRbYygyLCAzLCAxKV0KdGVzdF9zZXQgPSBwcmVkaWN0KHBjYSwgdGVzdF9zZXQpCnRlc3Rfc2V0ID0gdGVzdF9zZXRbYygyLCAzLCAxKV0KCiMgRml0dGluZyBTVk0gdG8gdGhlIFRyYWluaW5nIHNldAojIGluc3RhbGwucGFja2FnZXMoJ2UxMDcxJykKbGlicmFyeShlMTA3MSkKY2xhc3NpZmllciA9IHN2bShmb3JtdWxhID0gQ3VzdG9tZXJfU2VnbWVudCB+IC4sCiAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX3NldCwKICAgICAgICAgICAgICAgICB0eXBlID0gJ0MtY2xhc3NpZmljYXRpb24nLAogICAgICAgICAgICAgICAgIGtlcm5lbCA9ICdsaW5lYXInKQoKIyBQcmVkaWN0aW5nIHRoZSBUZXN0IHNldCByZXN1bHRzCnlfcHJlZCA9IHByZWRpY3QoY2xhc3NpZmllciwgbmV3ZGF0YSA9IHRlc3Rfc2V0Wy0zXSkKCiMgTWFraW5nIHRoZSBDb25mdXNpb24gTWF0cml4CmNtID0gdGFibGUodGVzdF9zZXRbLCAzXSwgeV9wcmVkKQoKIyBWaXN1YWxpc2luZyB0aGUgVHJhaW5pbmcgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0cmFpbmluZ19zZXQKWDEgPSBzZXEobWluKHNldFssIDFdKSAtIDEsIG1heChzZXRbLCAxXSkgKyAxLCBieSA9IDAuMDEpClgyID0gc2VxKG1pbihzZXRbLCAyXSkgLSAxLCBtYXgoc2V0WywgMl0pICsgMSwgYnkgPSAwLjAxKQpncmlkX3NldCA9IGV4cGFuZC5ncmlkKFgxLCBYMikKY29sbmFtZXMoZ3JpZF9zZXQpID0gYygnUEMxJywgJ1BDMicpCnlfZ3JpZCA9IHByZWRpY3QoY2xhc3NpZmllciwgbmV3ZGF0YSA9IGdyaWRfc2V0KQpwbG90KHNldFssIC0zXSwKICAgICBtYWluID0gJ1NWTSAoVHJhaW5pbmcgc2V0KScsCiAgICAgeGxhYiA9ICdQQzEnLCB5bGFiID0gJ1BDMicsCiAgICAgeGxpbSA9IHJhbmdlKFgxKSwgeWxpbSA9IHJhbmdlKFgyKSkKY29udG91cihYMSwgWDIsIG1hdHJpeChhcy5udW1lcmljKHlfZ3JpZCksIGxlbmd0aChYMSksIGxlbmd0aChYMikpLCBhZGQgPSBUUlVFKQpwb2ludHMoZ3JpZF9zZXQsIHBjaCA9ICcuJywgY29sID0gaWZlbHNlKHlfZ3JpZCA9PSAyLCAnZGVlcHNreWJsdWUnLCBpZmVsc2UoeV9ncmlkID09IDEsICdzcHJpbmdncmVlbjMnLCAndG9tYXRvJykpKQpwb2ludHMoc2V0LCBwY2ggPSAyMSwgYmcgPSBpZmVsc2Uoc2V0WywgM10gPT0gMiwgJ2JsdWUzJywgaWZlbHNlKHNldFssIDNdID09IDEsICdncmVlbjQnLCAncmVkMycpKSkKCiMgVmlzdWFsaXNpbmcgdGhlIFRlc3Qgc2V0IHJlc3VsdHMKbGlicmFyeShFbGVtU3RhdExlYXJuKQpzZXQgPSB0ZXN0X3NldApYMSA9IHNlcShtaW4oc2V0WywgMV0pIC0gMSwgbWF4KHNldFssIDFdKSArIDEsIGJ5ID0gMC4wMSkKWDIgPSBzZXEobWluKHNldFssIDJdKSAtIDEsIG1heChzZXRbLCAyXSkgKyAxLCBieSA9IDAuMDEpCmdyaWRfc2V0ID0gZXhwYW5kLmdyaWQoWDEsIFgyKQpjb2xuYW1lcyhncmlkX3NldCkgPSBjKCdQQzEnLCAnUEMyJykKeV9ncmlkID0gcHJlZGljdChjbGFzc2lmaWVyLCBuZXdkYXRhID0gZ3JpZF9zZXQpCnBsb3Qoc2V0WywgLTNdLCBtYWluID0gJ1NWTSAoVGVzdCBzZXQpJywKICAgICB4bGFiID0gJ1BDMScsIHlsYWIgPSAnUEMyJywKICAgICB4bGltID0gcmFuZ2UoWDEpLCB5bGltID0gcmFuZ2UoWDIpKQpjb250b3VyKFgxLCBYMiwgbWF0cml4KGFzLm51bWVyaWMoeV9ncmlkKSwgbGVuZ3RoKFgxKSwgbGVuZ3RoKFgyKSksIGFkZCA9IFRSVUUpCnBvaW50cyhncmlkX3NldCwgcGNoID0gJy4nLCBjb2wgPSBpZmVsc2UoeV9ncmlkID09IDIsICdkZWVwc2t5Ymx1ZScsIGlmZWxzZSh5X2dyaWQgPT0gMSwgJ3NwcmluZ2dyZWVuMycsICd0b21hdG8nKSkpCnBvaW50cyhzZXQsIHBjaCA9IDIxLCBiZyA9IGlmZWxzZShzZXRbLCAzXSA9PSAyLCAnYmx1ZTMnLCBpZmVsc2Uoc2V0WywgM10gPT0gMSwgJ2dyZWVuNCcsICdyZWQzJykpKSAgICAgCiAgICAgIAogICAgICAKYGBgICAKICAgICAgCiAgICAgIAogICAgICAKICAgICAgCgogICAgICAKICAgICAgCiAgICAgIAogICAgICAKICAgICAgCiAgICAgIAogICAgICAKICAgICAgCiAgICAgIAogICAgICAKICAgICAgCiAgICAgIAogICAgICAKICAgICAgCiAgICAgIAogICAgICAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKIAogCiAKICAgICAgCiAgICAgICAgICAgICAg