library(tidyverse)
library(kableExtra)
library(ggplot2)
library(plotly)
library(plot3D)



Design of experiment (DoE) is a method of planning and conducting experiments in a systematic and efficient way. It allows you to explore the effects of multiple factors on a response variable, and to find the optimal settings for the factors. DoE can help you to improve the quality and performance of a product or a process, and to reduce the costs and time of experimentation. DoE can be applied to various fields, such as engineering, food science, business, and education. On this page there are some examples on how to set some of the most popular DoE models.



Factorial design


Two factor three levels

We have \(3^2\) experimental conditions for two factors \(x_1\) and \(x_2\)


factors = 2
levels = c(-1,0,1)
x1 = c()
x2 = c()

for (j in 1:length(levels)) {
    for (i in 1:length(levels)) {
    x1 = c(x1, levels[i])
    x2 = c(x2, levels[j])
  }
}

twodf = data.frame(row.names =  c(1:length(x1)), x1,x2)

kable(twodf, row.names = T)  %>% 
  kable_styling('striped',fixed_thead = T, full_width = F) %>%

    column_spec(2,color= 'white',
              background = spec_color(twodf$x1,end = 0.7, option = 'A' )) %>%
  column_spec(3,color= 'white',
              background = spec_color(twodf$x2 ,end = 0.7, option = 'A' ))
x1 x2
1 -1 -1
2 0 -1
3 1 -1
4 -1 0
5 0 0
6 1 0
7 -1 1
8 0 1
9 1 1


ggplot(twodf, aes(x1,x2))+geom_point(color = 'cornflowerblue')+
  theme_bw()+
  geom_text(label = rownames(twodf), nudge_x = 0.05)+ 
  theme(aspect.ratio = 1)


To calculate the effects you can use the following postulated model:

\[ Y = b_0+b_1x_1+b_2x_2+b_{11}x_1^2+b_{22}x_2^2+b_{12}x_1x_2 \]


Three factors three levels


factors = 3
levels = c(-1,0,1)
x1 = c()
x2 = c()
x3 = c()

for (k in 1:length(levels))
  for (j in 1:length(levels)) {
      for (i in 1:length(levels)) {
      x1 = c(x1, levels[i])
      x2 = c(x2, levels[j])
      x3 = c(x3, levels[k])
    }
  }

threedf = data.frame(row.names = c(1:length(x1)), x1,x2, x3)

kable(threedf, row.names = T)  %>% 
  kable_styling('striped',fixed_thead = T, full_width = F) %>%
  

  column_spec(2,color= 'white',
              background = spec_color(threedf$x1,end = 0.7, option = 'A' )) %>%
  column_spec(3,color= 'white',
              background = spec_color(threedf$x2 ,end = 0.7, option = 'A' )) %>%
  column_spec(4,color= 'white',
              background = spec_color(threedf$x3 ,end = 0.7, option = 'A' )) %>%
  scroll_box(threedf, height = '400px')
x1 x2 x3
1 -1 -1 -1
2 0 -1 -1
3 1 -1 -1
4 -1 0 -1
5 0 0 -1
6 1 0 -1
7 -1 1 -1
8 0 1 -1
9 1 1 -1
10 -1 -1 0
11 0 -1 0
12 1 -1 0
13 -1 0 0
14 0 0 0
15 1 0 0
16 -1 1 0
17 0 1 0
18 1 1 0
19 -1 -1 1
20 0 -1 1
21 1 -1 1
22 -1 0 1
23 0 0 1
24 1 0 1
25 -1 1 1
26 0 1 1
27 1 1 1


plot_ly(threedf, x = ~x1, y = ~x2, z = ~x3) %>%
  add_markers(size= 0.5)  


The postulated model will be:

\[ Y = b_0+b_1x_1+b_2x_2+b_3x_3+b_{11}x_1^2+b_{22}x_2^2+b_{33}x_3^2+b_{12}x_1x_2+b_{13}x_1x_3+b_{23}x_2x_3+b_{123}x_1x_2x_3 \]

Central composite design (CCD)


To decrease the number of experiments with respect to a \(3^n\) full FD, a Central Composite Design (CCD) can be adopted, which consists in three parts:

  • A \(2^n\) FD
  • A star design
  • The central point

The total number of design points needed \((N)\) is determined by the formula \(N = 2^k + 2k + C_0\), where \(k\) is the number of variables (factors) and \(C_0\) is the number of center points.


CCD for two factors

The distance from the center of the points of the star design, \(\alpha\), can be for \(f\) factors:

  • \(\alpha = 2^{f/4}\)

So, for example, for \(2\) factors \(\alpha\) will be \(2^{2/4} = 2^{1/2} = \sqrt{2}\)

and the postulted model:

\[ Y = b_0+b_1x_1+b_2x_2+b_{11}x_1^2+b_{22}x_2^2+b_{12}x_1x_2 \]


Model example


Suppose we want to find the optimal yield of a generic reaction to the change of time and temperature

We can collect the data following the central composite design then we apply the postulated model.


factor = 2
leveltime = c(81.5,88.5)
leveltemp = c(171.5,178.5)
  
# initialize two vectors

time = c()
temp = c()

# 2^2 factorial design

for (j in 1:length(leveltime)) {
    for (i in 1:length(leveltemp)) {
    time = c(time, leveltime[i])
    temp = c(temp, leveltemp[j])
  }
}

# star design

time = c(time, 80,90,85,85)
temp = c(temp,175,175,170,180)

# adding 5 replicas of point 0

time = c(time,85,85,85,85,85)
temp = c(temp,175,175,175,175,175)

# experimental response

Y = c(76,78,77,79.5,75.6,78.4,77,78.5,79.9,80.3,80,79.7,79.8)


# printing dataframe

ccddf = data.frame(row.names =  c(1:length(time)),time,temp, Y)

kable(ccddf, row.names = T)  %>% 
  kable_styling('striped',fixed_thead = T, full_width = F) %>%
    column_spec(2,color= 'white',
              background = spec_color(ccddf$time,end = 0.7, option = 'A' )) %>%
  column_spec(3,color= 'white',
              background = spec_color(ccddf$temp ,end = 0.7, option = 'A' )) %>%
  column_spec(4,color= 'black', bold = T)
time temp Y
1 81.5 171.5 76.0
2 88.5 171.5 78.0
3 81.5 178.5 77.0
4 88.5 178.5 79.5
5 80.0 175.0 75.6
6 90.0 175.0 78.4
7 85.0 170.0 77.0
8 85.0 180.0 78.5
9 85.0 175.0 79.9
10 85.0 175.0 80.3
11 85.0 175.0 80.0
12 85.0 175.0 79.7
13 85.0 175.0 79.8


factor = 2
levels = c(-1,1)

# initialize two vectors

x1= c()
x2= c()

# 2^2 factorial design

for (j in 1:length(levels)) {
    for (i in 1:length(levels)) {
    x1 = c(x1, levels[i])
    x2 = c(x2, levels[j])
  }
}

# star design

x1 = c(x1, -sqrt(factor), sqrt(factor), 0,0) %>% round(digits = 2)
x2 = c(x2,0,0, -sqrt(factor), sqrt(factor)) %>% round(digits = 2)

# adding 5 replicas of point 0

x1 = c(x1,numeric(5))
x2 = c(x2,numeric(5))

# printing dataframe

ccddf = data.frame(row.names =  c(1:length(x1)), x1,x2, Y)

kable(ccddf, row.names = T)  %>% 
  kable_styling('striped',fixed_thead = T, full_width = F) %>%
    column_spec(2,color= 'white',
              background = spec_color(ccddf$x1,end = 0.7, option = 'A' )) %>%
  column_spec(3,color= 'white',
              background = spec_color(ccddf$x2 ,end = 0.7, option = 'A' ))
x1 x2 Y
1 -1.00 -1.00 76.0
2 1.00 -1.00 78.0
3 -1.00 1.00 77.0
4 1.00 1.00 79.5
5 -1.41 0.00 75.6
6 1.41 0.00 78.4
7 0.00 -1.41 77.0
8 0.00 1.41 78.5
9 0.00 0.00 79.9
10 0.00 0.00 80.3
11 0.00 0.00 80.0
12 0.00 0.00 79.7
13 0.00 0.00 79.8
ccddf1 = ccddf[-3]

names(ccddf1)[1] = '$x_1$'
names(ccddf1)[2] = '$x_2$'
ccddf1['$x_1^2$'] = x1^2 %>% round(digits = 2)
ccddf1['$x_2^2$'] = x2^2 %>% round(digits = 2)
ccddf1['$x_1x_2$'] = x1*x2
ccddf1['$Y$'] = Y

kable(ccddf1, row.names = T)  %>% 
  kable_styling('striped',fixed_thead = T, full_width = T) %>%
  
  column_spec(2,color= 'white',
              background = spec_color(ccddf1$`$x_1$`,end = 0.7, option = 'A' )) %>%
  column_spec(3,color= 'white',
              background = spec_color(ccddf1$`$x_2$` ,end = 0.7, option = 'A' )) %>%
  column_spec(4,color= 'white',
              background = spec_color(ccddf1$`$x_1^2$` ,end = 0.7, option = 'A' )) %>%
  column_spec(5,color= 'white',
              background = spec_color(ccddf1$`$x_2^2$` ,end = 0.7, option = 'A' )) %>%
  column_spec(6,color= 'white',
              background = spec_color(ccddf1$`$x_1x_2$` ,end = 0.7, option = 'A' )) %>%
  column_spec(7,color= 'black', bold = T)
\(x_1\) \(x_2\) \(x_1^2\) \(x_2^2\) \(x_1x_2\) \(Y\)
1 -1.00 -1.00 1.00 1.00 1 76.0
2 1.00 -1.00 1.00 1.00 -1 78.0
3 -1.00 1.00 1.00 1.00 -1 77.0
4 1.00 1.00 1.00 1.00 1 79.5
5 -1.41 0.00 1.99 0.00 0 75.6
6 1.41 0.00 1.99 0.00 0 78.4
7 0.00 -1.41 0.00 1.99 0 77.0
8 0.00 1.41 0.00 1.99 0 78.5
9 0.00 0.00 0.00 0.00 0 79.9
10 0.00 0.00 0.00 0.00 0 80.3
11 0.00 0.00 0.00 0.00 0 80.0
12 0.00 0.00 0.00 0.00 0 79.7
13 0.00 0.00 0.00 0.00 0 79.8


Now perform a regression

model = lm(`$Y$` ~ `$x_1$`+ `$x_2$` + `$x_1^2$` + `$x_2^2$`+ `$x_1x_2$`, ccddf1)
model %>% summary()
## 
## Call:
## lm(formula = `$Y$` ~ `$x_1$` + `$x_2$` + `$x_1^2$` + `$x_2^2$` + 
##     `$x_1x_2$`, data = ccddf1)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.23947 -0.13947 -0.03804  0.11134  0.36053 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 79.93947    0.10712 746.280  < 2e-16 ***
## `$x_1$`      1.05915    0.08481  12.488 4.86e-06 ***
## `$x_2$`      0.57860    0.08481   6.822 0.000248 ***
## `$x_1^2$`   -1.41107    0.09114 -15.482 1.13e-06 ***
## `$x_2^2$`   -1.03419    0.09114 -11.347 9.25e-06 ***
## `$x_1x_2$`   0.12500    0.11976   1.044 0.331304    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2395 on 7 degrees of freedom
## Multiple R-squared:  0.987,  Adjusted R-squared:  0.9778 
## F-statistic: 106.5 on 5 and 7 DF,  p-value: 1.902e-06


The significant coefficients of which are:

modelcoeff = coefficients(model)
modelcoeff[-6]
## (Intercept)     `$x_1$`     `$x_2$`   `$x_1^2$`   `$x_2^2$` 
##  79.9394742   1.0591510   0.5785963  -1.4110716  -1.0341872


Surface plot

x = seq(from = -1.4,  to = 1.4, by = 0.1)
y = seq(from = -1.4,  to = 1.4, by = 0.1)
z = c()

for (j in 1:length(x)){
  for (i in 1:length(y)){
  z = c(z, modelcoeff[1]+modelcoeff[2]*x[i]+modelcoeff[3]*y[j]^2+modelcoeff[4]*x[i]^2+modelcoeff[5]*y[j]^2 )
  }
}

time = seq(from = min(time), to = max(time), by= 0.1)
temp = seq(from = min(temp), to = max(temp), by= 0.1)

z = matrix(z, nrow = length(x), byrow = T)

plot_ly(x=time, y=temp, z=z) %>% add_surface() %>%
  layout(scene = list(
                 xaxis = list(title = 'Time'),
                 yaxis = list(title = 'Temperature'),
                 zaxis = list(title = 'Yield'),
                 aspectmode = 'cube'))


Contour plot


plot_ly(x=time, y=temp, z=z,contours = list(showlabels = TRUE)) %>% add_contour() %>%
  layout( xaxis = list(title = 'Time'),
                 yaxis = list(title = 'Temperature')) %>%
          config(modeBarButtonsToAdd = list('drawline', 
                                 'drawopenpath', 
                                 'drawclosedpath', 
                                 'drawcircle', 
                                 'drawrect', 
                                 'eraseshape')) %>%
  colorbar(title = "Yield")


Doehlert design


An alternative and very useful experimental design for second-order models is the uniform shell design proposed by Doehlert in 1970 (Doehlert 1970). Doehlert designs are easily applied to optimize variables and offer advantages in relation to central composite designs. They need fewer experiments, which are more efficient and can move through the experimental domain.

The Doehlert design describes a spherical experimental domain and it stresses uniformity in space filling. Although this matrix is neither orthogonal nor rotatable, it does not significantly diverge from the required quality for effective use .

For two variables, the Doehlert design consists of one central point and six points forming a regular hexagon, and therefore situated on a circle.

The number of experiments required is given by \(N = k^2 + k + C_0\)

A generic Doehlert matrix for two variables will be:


X1 = c(0,1,0.5,-0.5,-1,-0.5,0.5)
X2 = c(0,0,0.866,0.866,0,-0.866,-0.866)

doedf = data.frame(X1, X2, row.names = c(1:7))

doedf %>% kable(row.names = T) %>% kable_styling('striped',fixed_thead = T, full_width = F)
X1 X2
1 0.0 0.000
2 1.0 0.000
3 0.5 0.866
4 -0.5 0.866
5 -1.0 0.000
6 -0.5 -0.866
7 0.5 -0.866


Doehlert design for two factors
Doehlert design for two factors


Fractional factorial design (FFD)


Fractional Factorial Design (FFD) is based on the assumption that [significance of interaction terms decreases with increasing the order of interaction]

Take for example a \(2^3\) factorial design (3 factors and 2 levels)

factors = 3
levels = c(-1,1)
x1 = c()
x2 = c()
x3 = c()

for (k in 1:length(levels)) {
    for (j in 1:length(levels)) {
      for (i in 1:length(levels)) { 
        x1 = c(x1, levels[i])
        x2 = c(x2, levels[j])
        x3 = c(x3, levels[k])
      }
    }
}

twotwodf = data.frame(row.names =  c(1:length(x1)), x1,x2, x3)

p = kable(twotwodf, row.names = T)  %>% 
    kable_styling('striped',fixed_thead = T, full_width = F)

for (m in 1:ncol(twotwodf)) {
    p = column_spec(p, m+1,color= 'white',
              background = spec_color(twotwodf[,m],end = 0.7, option = 'A' )
                    )
}

p
x1 x2 x3
1 -1 -1 -1
2 1 -1 -1
3 -1 1 -1
4 1 1 -1
5 -1 -1 1
6 1 -1 1
7 -1 1 1
8 1 1 1


And expand it according to the postulated model


twotwodf$x1x2   = x1*x2
twotwodf$x1x3   = x1*x3
twotwodf$x2x3   = x2*x3
twotwodf$x1x2x3 = x1*x2*x3

p = kable(twotwodf, row.names = T)  %>% 
    kable_styling('striped',fixed_thead = T, full_width = T)

for (m in 1:ncol(twotwodf)) {
    p = column_spec(p, m+1,color= 'white',
              background = spec_color(twotwodf[,m],end = 0.7, option = 'A' )
                    )
}

p
x1 x2 x3 x1x2 x1x3 x2x3 x1x2x3
1 -1 -1 -1 1 1 1 -1
2 1 -1 -1 -1 -1 1 1
3 -1 1 -1 -1 1 -1 1
4 1 1 -1 1 -1 -1 -1
5 -1 -1 1 1 -1 -1 1
6 1 -1 1 -1 1 -1 -1
7 -1 1 1 -1 -1 1 -1
8 1 1 1 1 1 1 1


Now eliminate all the rows of the column x1x2x3 (maximum order of interaction) with the value -1

twotwodf2 = twotwodf[!(twotwodf$x1x2x3 == -1),]

p = kable(twotwodf2, row.names = T)  %>% 
    kable_styling('striped',fixed_thead = T, full_width = T)

for (m in 1:ncol(twotwodf2)) {
    p = column_spec(p, m+1,color= 'white',
              background = spec_color(twotwodf2[,m],end = 0.7, option = 'A' )
                    )
}

p
x1 x2 x3 x1x2 x1x3 x2x3 x1x2x3
2 1 -1 -1 -1 -1 1 1
3 -1 1 -1 -1 1 -1 1
5 -1 -1 1 1 -1 -1 1
8 1 1 1 1 1 1 1


Plackett-Burman design


Plackett–Burman designs are experimental designs presented in 1946 by Robin L. Plackett and J. P. Burman while working in the British Ministry of Supply. (PLACKETT and BURMAN 1946)

When the number of factors is fairly large, the constraint that the number of experiments must equal a power of 2 can be rather restrictive. For example, a 10 factors and 2 levels factorial design it would require \(2^{10}\) experiments.

With Plackett-Burman design the number of experiments required is the first multiple of 4 greater than the number of factors. In this case therefore you would have only 12 experiments!

The number of total factors will be \(N-1\), with \(N\) the number of experiments.

The additional factors are considered as dummy variables, useful to estimate the significance of the real factors.


12 runs PB design
12 runs PB design


In numerical values:


# -----  First row from literature ------------

r = c(1,    1,  -1, 1,  1,  1,  -1, -1, -1, 1,  -1)

plackdf = data.frame(t(r))

for (i in 0:9) {
  plackdf[2+i,] = c(r[(11-i):11],r[-c((11-i):11)])
}

plackdf[12,] = rep(-1,11)


fig = plackdf %>% kable(row.names = T) %>% kable_styling('striped',fixed_thead = T, full_width = F)

for (m in 1:ncol(plackdf)) {
    fig = column_spec(fig, m+1,color= 'white',
              background = spec_color(plackdf[,m],begin = 0.15,  end = 0.6, option = 'B' )
                    )
}
fig
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11
1 1 1 -1 1 1 1 -1 -1 -1 1 -1
2 -1 1 1 -1 1 1 1 -1 -1 -1 1
3 1 -1 1 1 -1 1 1 1 -1 -1 -1
4 -1 1 -1 1 1 -1 1 1 1 -1 -1
5 -1 -1 1 -1 1 1 -1 1 1 1 -1
6 -1 -1 -1 1 -1 1 1 -1 1 1 1
7 1 -1 -1 -1 1 -1 1 1 -1 1 1
8 1 1 -1 -1 -1 1 -1 1 1 -1 1
9 1 1 1 -1 -1 -1 1 -1 1 1 -1
10 -1 1 1 1 -1 -1 -1 1 -1 1 1
11 1 -1 1 1 1 -1 -1 -1 1 -1 1
12 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1


Example


Suppose you have measured an experimental response for all the experiments  


y = c(51,   31, 44, 23, 80, 45, 31, 55, 23, 26, 29, 28)

plackdf2 = plackdf
plackdf2 = cbind(plackdf2, 'Y' = y)

fig = plackdf2 %>% kable(row.names = T) %>% kable_styling('striped',fixed_thead = T, full_width = F)

for (m in 1:ncol(plackdf)) {
    fig = column_spec(fig, m+1,color= 'white',
              background = spec_color(plackdf[,m],begin = 0.15,  end = 0.6, option = 'B' )
                    )
}

fig %>% column_spec(13, color= 'white', background = 'darkgreen')
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 Y
1 1 1 -1 1 1 1 -1 -1 -1 1 -1 51
2 -1 1 1 -1 1 1 1 -1 -1 -1 1 31
3 1 -1 1 1 -1 1 1 1 -1 -1 -1 44
4 -1 1 -1 1 1 -1 1 1 1 -1 -1 23
5 -1 -1 1 -1 1 1 -1 1 1 1 -1 80
6 -1 -1 -1 1 -1 1 1 -1 1 1 1 45
7 1 -1 -1 -1 1 -1 1 1 -1 1 1 31
8 1 1 -1 -1 -1 1 -1 1 1 -1 1 55
9 1 1 1 -1 -1 -1 1 -1 1 1 -1 23
10 -1 1 1 1 -1 -1 -1 1 -1 1 1 26
11 1 -1 1 1 1 -1 -1 -1 1 -1 1 29
12 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 28


The postulated model will be


\[ Y = b_0 + b_1x_1+b_2x_2+ b_{3}x_{3}+ b_{4}x_{4}+ b_{5}x_{5}+ b_{6}x_{6}+ b_{7}x_{7}+ b_{8}x_{8}+ b_{9}x_{9}+ b_{10}x_{10}+b_{11}x_{11} \]

Now we’re able to perform a regression  

colnames(plackdf2)[11] = 'Dummy'
model = lm(Y ~ X1+X2+X3+X4+X5+X6+X7+X8+X9+X10+Dummy, plackdf2)
model %>% summary()
## 
## Call:
## lm(formula = Y ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + X8 + X9 + 
##     X10 + Dummy, data = plackdf2)
## 
## Residuals:
## ALL 12 residuals are 0: no residual degrees of freedom!
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)
## (Intercept)  3.883e+01        NaN     NaN      NaN
## X1           2.324e-15        NaN     NaN      NaN
## X2          -4.000e+00        NaN     NaN      NaN
## X3          -8.547e-16        NaN     NaN      NaN
## X4          -2.500e+00        NaN     NaN      NaN
## X5           2.000e+00        NaN     NaN      NaN
## X6           1.217e+01        NaN     NaN      NaN
## X7          -6.000e+00        NaN     NaN      NaN
## X8           4.333e+00        NaN     NaN      NaN
## X9           3.667e+00        NaN     NaN      NaN
## X10          3.833e+00        NaN     NaN      NaN
## Dummy       -2.667e+00        NaN     NaN      NaN
## 
## Residual standard error: NaN on 0 degrees of freedom
## Multiple R-squared:      1,  Adjusted R-squared:    NaN 
## F-statistic:   NaN on 11 and 0 DF,  p-value: NA


Since there are no residual degrees of freedom we cannot estimate the significance of the coefficients as usual

We need another approach to understand which of them are actually significant.


df = coefficients(model)[-1] %>% data.frame()
colnames(df) = 'coeff'
df$X = rownames(df)
df$X = factor(df$X, levels = df$X)

ggplot(df, aes(x= X, coeff, fill =ifelse(X == 'Dummy', 'Dummy', 'normal')))+
  geom_col(show.legend = F)+
  scale_fill_manual( values = c( "Dummy"="darkred", "normal"="cornflowerblue" ))+
  theme_bw()+
  xlab('')+
  ylab('')+
  geom_hline(yintercept = coefficients(model)[12], linetype = 'longdash', color = 'red')+
  geom_hline(yintercept = -coefficients(model)[12], linetype = 'longdash', color = 'red')+
  geom_hline(yintercept = 0,alpha = 0.5)  


Coefficients of similar size to the Dummy are no particularly significant!

In this case only factors X6 and X7 are relevant and maybe used later for further experimental design.








References


Doehlert, David H. 1970. “Uniform Shell Designs.” Applied Statistics 19 (3): 231. https://doi.org/10.2307/2346327.
PLACKETT, R. L., and J. P. BURMAN. 1946. “THE DESIGN OF OPTIMUM MULTIFACTORIAL EXPERIMENTS.” Biometrika 33 (4): 305–25. https://doi.org/10.1093/biomet/33.4.305.
LS0tDQp0aXRsZTogIkRlc2lnbiBvZiBFeHBlcmltZW50Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IFRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiBUcnVlICAgIA0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCi0tLQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocGxvdDNEKQ0KDQoNCmBgYA0KDQo8YnIvPg0KPGJyLz4NCg0KRGVzaWduIG9mIGV4cGVyaW1lbnQgKERvRSkgaXMgYSBtZXRob2Qgb2YgcGxhbm5pbmcgYW5kIGNvbmR1Y3RpbmcgZXhwZXJpbWVudHMgaW4gYSBzeXN0ZW1hdGljIGFuZCBlZmZpY2llbnQgd2F5LiBJdCBhbGxvd3MgeW91IHRvIGV4cGxvcmUgdGhlIGVmZmVjdHMgb2YgbXVsdGlwbGUgZmFjdG9ycyBvbiBhIHJlc3BvbnNlIHZhcmlhYmxlLCBhbmQgdG8gZmluZCB0aGUgb3B0aW1hbCBzZXR0aW5ncyBmb3IgdGhlIGZhY3RvcnMuIERvRSBjYW4gaGVscCB5b3UgdG8gaW1wcm92ZSB0aGUgcXVhbGl0eSBhbmQgcGVyZm9ybWFuY2Ugb2YgYSBwcm9kdWN0IG9yIGEgcHJvY2VzcywgYW5kIHRvIHJlZHVjZSB0aGUgY29zdHMgYW5kIHRpbWUgb2YgZXhwZXJpbWVudGF0aW9uLiBEb0UgY2FuIGJlIGFwcGxpZWQgdG8gdmFyaW91cyBmaWVsZHMsIHN1Y2ggYXMgZW5naW5lZXJpbmcsIGZvb2Qgc2NpZW5jZSwgYnVzaW5lc3MsIGFuZCBlZHVjYXRpb24uIE9uIHRoaXMgcGFnZSB0aGVyZSBhcmUgc29tZSBleGFtcGxlcyBvbiBob3cgdG8gc2V0IHNvbWUgb2YgdGhlIG1vc3QgcG9wdWxhciBEb0UgbW9kZWxzLg0KDQo8YnIvPg0KPGJyLz4NCg0KIyBGYWN0b3JpYWwgZGVzaWduDQoNCjxici8+DQoNCg0KDQojIyBUd28gZmFjdG9yIHRocmVlIGxldmVscw0KDQpXZSBoYXZlICQzXjIkIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zIGZvciB0d28gZmFjdG9ycyAkeF8xJCBhbmQgJHhfMiQNCg0KPGJyLz4NCg0KYGBge3J9DQoNCmZhY3RvcnMgPSAyDQpsZXZlbHMgPSBjKC0xLDAsMSkNCngxID0gYygpDQp4MiA9IGMoKQ0KDQpmb3IgKGogaW4gMTpsZW5ndGgobGV2ZWxzKSkgew0KICAgIGZvciAoaSBpbiAxOmxlbmd0aChsZXZlbHMpKSB7DQogICAgeDEgPSBjKHgxLCBsZXZlbHNbaV0pDQogICAgeDIgPSBjKHgyLCBsZXZlbHNbal0pDQogIH0NCn0NCg0KdHdvZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9ICBjKDE6bGVuZ3RoKHgxKSksIHgxLHgyKQ0KDQprYWJsZSh0d29kZiwgcm93Lm5hbWVzID0gVCkgICU+JSANCiAga2FibGVfc3R5bGluZygnc3RyaXBlZCcsZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRikgJT4lDQoNCiAgICBjb2x1bW5fc3BlYygyLGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcih0d29kZiR4MSxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKSAlPiUNCiAgY29sdW1uX3NwZWMoMyxjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IodHdvZGYkeDIgLGVuZCA9IDAuNywgb3B0aW9uID0gJ0EnICkpDQoNCmBgYA0KDQo8YnIvPg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KDQpnZ3Bsb3QodHdvZGYsIGFlcyh4MSx4MikpK2dlb21fcG9pbnQoY29sb3IgPSAnY29ybmZsb3dlcmJsdWUnKSsNCiAgdGhlbWVfYncoKSsNCiAgZ2VvbV90ZXh0KGxhYmVsID0gcm93bmFtZXModHdvZGYpLCBudWRnZV94ID0gMC4wNSkrIA0KICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQ0KDQpgYGANCg0KPGJyLz4NCg0KVG8gY2FsY3VsYXRlIHRoZSBlZmZlY3RzIHlvdSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgcG9zdHVsYXRlZCBtb2RlbDoNCg0KJCQNCiAgICBZID0gYl8wK2JfMXhfMStiXzJ4XzIrYl97MTF9eF8xXjIrYl97MjJ9eF8yXjIrYl97MTJ9eF8xeF8yDQokJA0KDQo8YnIvPg0KDQojIyBUaHJlZSBmYWN0b3JzIHRocmVlIGxldmVscw0KDQo8YnIvPg0KDQpgYGB7cn0NCg0KZmFjdG9ycyA9IDMNCmxldmVscyA9IGMoLTEsMCwxKQ0KeDEgPSBjKCkNCngyID0gYygpDQp4MyA9IGMoKQ0KDQpmb3IgKGsgaW4gMTpsZW5ndGgobGV2ZWxzKSkNCiAgZm9yIChqIGluIDE6bGVuZ3RoKGxldmVscykpIHsNCiAgICAgIGZvciAoaSBpbiAxOmxlbmd0aChsZXZlbHMpKSB7DQogICAgICB4MSA9IGMoeDEsIGxldmVsc1tpXSkNCiAgICAgIHgyID0gYyh4MiwgbGV2ZWxzW2pdKQ0KICAgICAgeDMgPSBjKHgzLCBsZXZlbHNba10pDQogICAgfQ0KICB9DQoNCnRocmVlZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGMoMTpsZW5ndGgoeDEpKSwgeDEseDIsIHgzKQ0KDQprYWJsZSh0aHJlZWRmLCByb3cubmFtZXMgPSBUKSAgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCdzdHJpcGVkJyxmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSAlPiUNCiAgDQoNCiAgY29sdW1uX3NwZWMoMixjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IodGhyZWVkZiR4MSxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKSAlPiUNCiAgY29sdW1uX3NwZWMoMyxjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IodGhyZWVkZiR4MiAsZW5kID0gMC43LCBvcHRpb24gPSAnQScgKSkgJT4lDQogIGNvbHVtbl9zcGVjKDQsY29sb3I9ICd3aGl0ZScsDQogICAgICAgICAgICAgIGJhY2tncm91bmQgPSBzcGVjX2NvbG9yKHRocmVlZGYkeDMgLGVuZCA9IDAuNywgb3B0aW9uID0gJ0EnICkpICU+JQ0KICBzY3JvbGxfYm94KHRocmVlZGYsIGhlaWdodCA9ICc0MDBweCcpDQoNCg0KYGBgDQoNCjxici8+DQoNCmBgYHtyfQ0KcGxvdF9seSh0aHJlZWRmLCB4ID0gfngxLCB5ID0gfngyLCB6ID0gfngzKSAlPiUNCiAgYWRkX21hcmtlcnMoc2l6ZT0gMC41KSAgDQoNCg0KYGBgDQoNCjxici8+DQoNClRoZSBwb3N0dWxhdGVkIG1vZGVsIHdpbGwgYmU6IDxici8+DQoNCiQkDQogICBZID0gYl8wK2JfMXhfMStiXzJ4XzIrYl8zeF8zK2JfezExfXhfMV4yK2JfezIyfXhfMl4yK2JfezMzfXhfM14yK2JfezEyfXhfMXhfMitiX3sxM314XzF4XzMrYl97MjN9eF8yeF8zK2JfezEyM314XzF4XzJ4XzMNCiQkIDxici8+DQoNCiMgQ2VudHJhbCBjb21wb3NpdGUgZGVzaWduIChDQ0QpDQoNCjxici8+DQoNClRvIGRlY3JlYXNlIHRoZSBudW1iZXIgb2YgZXhwZXJpbWVudHMgd2l0aCByZXNwZWN0IHRvIGEgJDNebiQgZnVsbCBGRCwgYQ0KQ2VudHJhbCBDb21wb3NpdGUgRGVzaWduIChDQ0QpIGNhbiBiZSBhZG9wdGVkLCB3aGljaCBjb25zaXN0cyBpbiB0aHJlZQ0KcGFydHM6DQoNCi0gICBBICQyXm4kIEZEDQotICAgQSBzdGFyIGRlc2lnbg0KLSAgIFRoZSBjZW50cmFsIHBvaW50DQoNClRoZSB0b3RhbCBudW1iZXIgb2YgZGVzaWduIHBvaW50cyBuZWVkZWQgJChOKSQgaXMgZGV0ZXJtaW5lZCBieSB0aGUNCmZvcm11bGEgJE4gPSAyXmsgKyAyayArIENfMCQsIHdoZXJlICRrJCBpcyB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcw0KKGZhY3RvcnMpIGFuZCAkQ18wJCBpcyB0aGUgbnVtYmVyIG9mIGNlbnRlciBwb2ludHMuXA0KDQo8YnIvPg0KDQojIyBDQ0QgZm9yIHR3byBmYWN0b3JzDQoNClRoZSBkaXN0YW5jZSBmcm9tIHRoZSBjZW50ZXIgb2YgdGhlIHBvaW50cyBvZiB0aGUgc3RhciBkZXNpZ24sICRcYWxwaGEkLA0KY2FuIGJlIGZvciAkZiQgZmFjdG9yczoNCg0KLSAgICRcYWxwaGEgPSAyXntmLzR9JFwNCg0KU28sIGZvciBleGFtcGxlLCBmb3IgJDIkIGZhY3RvcnMgJFxhbHBoYSQgd2lsbCBiZQ0KJDJeezIvNH0gPSAyXnsxLzJ9ID0gXHNxcnR7Mn0kXA0KDQphbmQgdGhlIHBvc3R1bHRlZCBtb2RlbDogPGJyLz4NCg0KJCQNCiAgICBZID0gYl8wK2JfMXhfMStiXzJ4XzIrYl97MTF9eF8xXjIrYl97MjJ9eF8yXjIrYl97MTJ9eF8xeF8yDQokJCA8YnIvPg0KDQo8YnIvPg0KDQojIyBNb2RlbCBleGFtcGxlDQoNCjxici8+DQoNClN1cHBvc2Ugd2Ugd2FudCB0byBmaW5kIHRoZSBvcHRpbWFsIHlpZWxkIG9mIGEgZ2VuZXJpYyByZWFjdGlvbiB0byB0aGUNCmNoYW5nZSBvZiB0aW1lIGFuZCB0ZW1wZXJhdHVyZVwNCg0KV2UgY2FuIGNvbGxlY3QgdGhlIGRhdGEgZm9sbG93aW5nIHRoZSBjZW50cmFsIGNvbXBvc2l0ZSBkZXNpZ24gdGhlbiB3ZQ0KYXBwbHkgdGhlIHBvc3R1bGF0ZWQgbW9kZWwuDQoNCjxici8+DQoNCmBgYHtyfQ0KDQpmYWN0b3IgPSAyDQpsZXZlbHRpbWUgPSBjKDgxLjUsODguNSkNCmxldmVsdGVtcCA9IGMoMTcxLjUsMTc4LjUpDQogIA0KIyBpbml0aWFsaXplIHR3byB2ZWN0b3JzDQoNCnRpbWUgPSBjKCkNCnRlbXAgPSBjKCkNCg0KIyAyXjIgZmFjdG9yaWFsIGRlc2lnbg0KDQpmb3IgKGogaW4gMTpsZW5ndGgobGV2ZWx0aW1lKSkgew0KICAgIGZvciAoaSBpbiAxOmxlbmd0aChsZXZlbHRlbXApKSB7DQogICAgdGltZSA9IGModGltZSwgbGV2ZWx0aW1lW2ldKQ0KICAgIHRlbXAgPSBjKHRlbXAsIGxldmVsdGVtcFtqXSkNCiAgfQ0KfQ0KDQojIHN0YXIgZGVzaWduDQoNCnRpbWUgPSBjKHRpbWUsIDgwLDkwLDg1LDg1KQ0KdGVtcCA9IGModGVtcCwxNzUsMTc1LDE3MCwxODApDQoNCiMgYWRkaW5nIDUgcmVwbGljYXMgb2YgcG9pbnQgMA0KDQp0aW1lID0gYyh0aW1lLDg1LDg1LDg1LDg1LDg1KQ0KdGVtcCA9IGModGVtcCwxNzUsMTc1LDE3NSwxNzUsMTc1KQ0KDQojIGV4cGVyaW1lbnRhbCByZXNwb25zZQ0KDQpZID0gYyg3Niw3OCw3Nyw3OS41LDc1LjYsNzguNCw3Nyw3OC41LDc5LjksODAuMyw4MCw3OS43LDc5LjgpDQoNCg0KIyBwcmludGluZyBkYXRhZnJhbWUNCg0KY2NkZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9ICBjKDE6bGVuZ3RoKHRpbWUpKSx0aW1lLHRlbXAsIFkpDQoNCmthYmxlKGNjZGRmLCByb3cubmFtZXMgPSBUKSAgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCdzdHJpcGVkJyxmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSAlPiUNCiAgICBjb2x1bW5fc3BlYygyLGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcihjY2RkZiR0aW1lLGVuZCA9IDAuNywgb3B0aW9uID0gJ0EnICkpICU+JQ0KICBjb2x1bW5fc3BlYygzLGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcihjY2RkZiR0ZW1wICxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKSAlPiUNCiAgY29sdW1uX3NwZWMoNCxjb2xvcj0gJ2JsYWNrJywgYm9sZCA9IFQpDQoNCg0KYGBgDQoNCjxici8+DQoNCmBgYHtyfQ0KDQpmYWN0b3IgPSAyDQpsZXZlbHMgPSBjKC0xLDEpDQoNCiMgaW5pdGlhbGl6ZSB0d28gdmVjdG9ycw0KDQp4MT0gYygpDQp4Mj0gYygpDQoNCiMgMl4yIGZhY3RvcmlhbCBkZXNpZ24NCg0KZm9yIChqIGluIDE6bGVuZ3RoKGxldmVscykpIHsNCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgobGV2ZWxzKSkgew0KICAgIHgxID0gYyh4MSwgbGV2ZWxzW2ldKQ0KICAgIHgyID0gYyh4MiwgbGV2ZWxzW2pdKQ0KICB9DQp9DQoNCiMgc3RhciBkZXNpZ24NCg0KeDEgPSBjKHgxLCAtc3FydChmYWN0b3IpLCBzcXJ0KGZhY3RvciksIDAsMCkgJT4lIHJvdW5kKGRpZ2l0cyA9IDIpDQp4MiA9IGMoeDIsMCwwLCAtc3FydChmYWN0b3IpLCBzcXJ0KGZhY3RvcikpICU+JSByb3VuZChkaWdpdHMgPSAyKQ0KDQojIGFkZGluZyA1IHJlcGxpY2FzIG9mIHBvaW50IDANCg0KeDEgPSBjKHgxLG51bWVyaWMoNSkpDQp4MiA9IGMoeDIsbnVtZXJpYyg1KSkNCg0KIyBwcmludGluZyBkYXRhZnJhbWUNCg0KY2NkZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9ICBjKDE6bGVuZ3RoKHgxKSksIHgxLHgyLCBZKQ0KDQprYWJsZShjY2RkZiwgcm93Lm5hbWVzID0gVCkgICU+JSANCiAga2FibGVfc3R5bGluZygnc3RyaXBlZCcsZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRikgJT4lDQogICAgY29sdW1uX3NwZWMoMixjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IoY2NkZGYkeDEsZW5kID0gMC43LCBvcHRpb24gPSAnQScgKSkgJT4lDQogIGNvbHVtbl9zcGVjKDMsY29sb3I9ICd3aGl0ZScsDQogICAgICAgICAgICAgIGJhY2tncm91bmQgPSBzcGVjX2NvbG9yKGNjZGRmJHgyICxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKQ0KDQpgYGANCg0KYGBge3J9DQoNCg0KY2NkZGYxID0gY2NkZGZbLTNdDQoNCm5hbWVzKGNjZGRmMSlbMV0gPSAnJHhfMSQnDQpuYW1lcyhjY2RkZjEpWzJdID0gJyR4XzIkJw0KY2NkZGYxWyckeF8xXjIkJ10gPSB4MV4yICU+JSByb3VuZChkaWdpdHMgPSAyKQ0KY2NkZGYxWyckeF8yXjIkJ10gPSB4Ml4yICU+JSByb3VuZChkaWdpdHMgPSAyKQ0KY2NkZGYxWyckeF8xeF8yJCddID0geDEqeDINCmNjZGRmMVsnJFkkJ10gPSBZDQoNCmthYmxlKGNjZGRmMSwgcm93Lm5hbWVzID0gVCkgICU+JSANCiAga2FibGVfc3R5bGluZygnc3RyaXBlZCcsZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkgJT4lDQogIA0KICBjb2x1bW5fc3BlYygyLGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcihjY2RkZjEkYCR4XzEkYCxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKSAlPiUNCiAgY29sdW1uX3NwZWMoMyxjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IoY2NkZGYxJGAkeF8yJGAgLGVuZCA9IDAuNywgb3B0aW9uID0gJ0EnICkpICU+JQ0KICBjb2x1bW5fc3BlYyg0LGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcihjY2RkZjEkYCR4XzFeMiRgICxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKSAlPiUNCiAgY29sdW1uX3NwZWMoNSxjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IoY2NkZGYxJGAkeF8yXjIkYCAsZW5kID0gMC43LCBvcHRpb24gPSAnQScgKSkgJT4lDQogIGNvbHVtbl9zcGVjKDYsY29sb3I9ICd3aGl0ZScsDQogICAgICAgICAgICAgIGJhY2tncm91bmQgPSBzcGVjX2NvbG9yKGNjZGRmMSRgJHhfMXhfMiRgICxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApKSAlPiUNCiAgY29sdW1uX3NwZWMoNyxjb2xvcj0gJ2JsYWNrJywgYm9sZCA9IFQpDQoNCg0KYGBgDQoNCjxici8+DQoNCk5vdyBwZXJmb3JtIGEgcmVncmVzc2lvblwNCg0KYGBge3J9DQoNCm1vZGVsID0gbG0oYCRZJGAgfiBgJHhfMSRgKyBgJHhfMiRgICsgYCR4XzFeMiRgICsgYCR4XzJeMiRgKyBgJHhfMXhfMiRgLCBjY2RkZjEpDQptb2RlbCAlPiUgc3VtbWFyeSgpDQoNCmBgYA0KDQo8YnIvPg0KDQpUaGUgc2lnbmlmaWNhbnQgY29lZmZpY2llbnRzIG9mIHdoaWNoIGFyZTpcDQoNCmBgYHtyfQ0KbW9kZWxjb2VmZiA9IGNvZWZmaWNpZW50cyhtb2RlbCkNCm1vZGVsY29lZmZbLTZdDQpgYGANCg0KPGJyLz4NCg0KIyMgU3VyZmFjZSBwbG90DQoNCmBgYHtyfQ0KDQp4ID0gc2VxKGZyb20gPSAtMS40LCAgdG8gPSAxLjQsIGJ5ID0gMC4xKQ0KeSA9IHNlcShmcm9tID0gLTEuNCwgIHRvID0gMS40LCBieSA9IDAuMSkNCnogPSBjKCkNCg0KZm9yIChqIGluIDE6bGVuZ3RoKHgpKXsNCiAgZm9yIChpIGluIDE6bGVuZ3RoKHkpKXsNCiAgeiA9IGMoeiwgbW9kZWxjb2VmZlsxXSttb2RlbGNvZWZmWzJdKnhbaV0rbW9kZWxjb2VmZlszXSp5W2pdXjIrbW9kZWxjb2VmZls0XSp4W2ldXjIrbW9kZWxjb2VmZls1XSp5W2pdXjIgKQ0KICB9DQp9DQoNCnRpbWUgPSBzZXEoZnJvbSA9IG1pbih0aW1lKSwgdG8gPSBtYXgodGltZSksIGJ5PSAwLjEpDQp0ZW1wID0gc2VxKGZyb20gPSBtaW4odGVtcCksIHRvID0gbWF4KHRlbXApLCBieT0gMC4xKQ0KDQp6ID0gbWF0cml4KHosIG5yb3cgPSBsZW5ndGgoeCksIGJ5cm93ID0gVCkNCg0KcGxvdF9seSh4PXRpbWUsIHk9dGVtcCwgej16KSAlPiUgYWRkX3N1cmZhY2UoKSAlPiUNCiAgbGF5b3V0KHNjZW5lID0gbGlzdCgNCiAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1RpbWUnKSwNCiAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ1RlbXBlcmF0dXJlJyksDQogICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICdZaWVsZCcpLA0KICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlID0gJ2N1YmUnKSkNCg0KDQoNCg0KYGBgDQoNCjxici8+DQoNCiMjIENvbnRvdXIgcGxvdA0KDQo8YnIvPg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KDQpwbG90X2x5KHg9dGltZSwgeT10ZW1wLCB6PXosY29udG91cnMgPSBsaXN0KHNob3dsYWJlbHMgPSBUUlVFKSkgJT4lIGFkZF9jb250b3VyKCkgJT4lDQogIGxheW91dCggeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1RpbWUnKSwNCiAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ1RlbXBlcmF0dXJlJykpICU+JQ0KICAgICAgICAgIGNvbmZpZyhtb2RlQmFyQnV0dG9uc1RvQWRkID0gbGlzdCgnZHJhd2xpbmUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkcmF3b3BlbnBhdGgnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkcmF3Y2xvc2VkcGF0aCcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RyYXdjaXJjbGUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkcmF3cmVjdCcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2VyYXNlc2hhcGUnKSkgJT4lDQogIGNvbG9yYmFyKHRpdGxlID0gIllpZWxkIikNCg0KYGBgDQoNCjxici8+DQoNCiMgRG9laGxlcnQgZGVzaWduDQoNCjxici8+DQoNCkFuIGFsdGVybmF0aXZlIGFuZCB2ZXJ5IHVzZWZ1bCBleHBlcmltZW50YWwgZGVzaWduIGZvciBzZWNvbmQtb3JkZXINCm1vZGVscyBpcyB0aGUgdW5pZm9ybSBzaGVsbCBkZXNpZ24gcHJvcG9zZWQgYnkgRG9laGxlcnQgaW4gMTk3MA0KW0Bkb2VobGVydDE5NzBdLiBEb2VobGVydCBkZXNpZ25zIGFyZSBlYXNpbHkgYXBwbGllZCB0byBvcHRpbWl6ZQ0KdmFyaWFibGVzIGFuZCBvZmZlciBhZHZhbnRhZ2VzIGluIHJlbGF0aW9uIHRvIGNlbnRyYWwgY29tcG9zaXRlIGRlc2lnbnMuDQpUaGV5IG5lZWQgZmV3ZXIgZXhwZXJpbWVudHMsIHdoaWNoIGFyZSBtb3JlIGVmZmljaWVudCBhbmQgY2FuIG1vdmUNCnRocm91Z2ggdGhlIGV4cGVyaW1lbnRhbCBkb21haW4uXA0KDQpUaGUgRG9laGxlcnQgZGVzaWduIGRlc2NyaWJlcyBhIHNwaGVyaWNhbCBleHBlcmltZW50YWwgZG9tYWluIGFuZCBpdA0Kc3RyZXNzZXMgdW5pZm9ybWl0eSBpbiBzcGFjZSBmaWxsaW5nLiBBbHRob3VnaCB0aGlzIG1hdHJpeCBpcyBuZWl0aGVyDQpvcnRob2dvbmFsIG5vciByb3RhdGFibGUsIGl0IGRvZXMgbm90IHNpZ25pZmljYW50bHkgZGl2ZXJnZSBmcm9tIHRoZQ0KcmVxdWlyZWQgcXVhbGl0eSBmb3IgZWZmZWN0aXZlIHVzZSAuDQoNCkZvciB0d28gdmFyaWFibGVzLCB0aGUgRG9laGxlcnQgZGVzaWduIGNvbnNpc3RzIG9mIG9uZSBjZW50cmFsIHBvaW50IGFuZA0Kc2l4IHBvaW50cyBmb3JtaW5nIGEgcmVndWxhciBoZXhhZ29uLCBhbmQgdGhlcmVmb3JlIHNpdHVhdGVkIG9uIGENCmNpcmNsZS5cDQoNClRoZSBudW1iZXIgb2YgZXhwZXJpbWVudHMgcmVxdWlyZWQgaXMgZ2l2ZW4gYnkgJE4gPSBrXjIgKyBrICsgQ18wJFwNCg0KQSBnZW5lcmljIERvZWhsZXJ0IG1hdHJpeCBmb3IgdHdvIHZhcmlhYmxlcyB3aWxsIGJlOlwNCg0KPGJyLz4NCg0KYGBge3J9DQoNClgxID0gYygwLDEsMC41LC0wLjUsLTEsLTAuNSwwLjUpDQpYMiA9IGMoMCwwLDAuODY2LDAuODY2LDAsLTAuODY2LC0wLjg2NikNCg0KZG9lZGYgPSBkYXRhLmZyYW1lKFgxLCBYMiwgcm93Lm5hbWVzID0gYygxOjcpKQ0KDQpkb2VkZiAlPiUga2FibGUocm93Lm5hbWVzID0gVCkgJT4lIGthYmxlX3N0eWxpbmcoJ3N0cmlwZWQnLGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IEYpDQoNCmBgYA0KDQo8YnIvPg0KDQo8Y2VudGVyPiFbRG9laGxlcnQgZGVzaWduIGZvciB0d28NCmZhY3RvcnNdKGltYWdlcy9Eb2VobGVydC5KUEcpe3dpZHRoPSIzMDAifTwvY2VudGVyPg0KDQo8YnIvPg0KDQojIEZyYWN0aW9uYWwgZmFjdG9yaWFsIGRlc2lnbiAoRkZEKQ0KDQo8YnIvPg0KDQpGcmFjdGlvbmFsIEZhY3RvcmlhbCBEZXNpZ24gKEZGRCkgaXMgYmFzZWQgb24gdGhlIGFzc3VtcHRpb24gdGhhdA0KWyoqc2lnbmlmaWNhbmNlIG9mIGludGVyYWN0aW9uIHRlcm1zIGRlY3JlYXNlcyB3aXRoIGluY3JlYXNpbmcgdGhlIG9yZGVyDQpvZiBpbnRlcmFjdGlvbioqXQ0KDQpUYWtlIGZvciBleGFtcGxlIGEgJDJeMyQgZmFjdG9yaWFsIGRlc2lnbiAoMyBmYWN0b3JzIGFuZCAyIGxldmVscylcDQoNCmBgYHtyfQ0KDQpmYWN0b3JzID0gMw0KbGV2ZWxzID0gYygtMSwxKQ0KeDEgPSBjKCkNCngyID0gYygpDQp4MyA9IGMoKQ0KDQpmb3IgKGsgaW4gMTpsZW5ndGgobGV2ZWxzKSkgew0KICAgIGZvciAoaiBpbiAxOmxlbmd0aChsZXZlbHMpKSB7DQogICAgICBmb3IgKGkgaW4gMTpsZW5ndGgobGV2ZWxzKSkgeyANCiAgICAgICAgeDEgPSBjKHgxLCBsZXZlbHNbaV0pDQogICAgICAgIHgyID0gYyh4MiwgbGV2ZWxzW2pdKQ0KICAgICAgICB4MyA9IGMoeDMsIGxldmVsc1trXSkNCiAgICAgIH0NCiAgICB9DQp9DQoNCnR3b3R3b2RmID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSAgYygxOmxlbmd0aCh4MSkpLCB4MSx4MiwgeDMpDQoNCnAgPSBrYWJsZSh0d290d29kZiwgcm93Lm5hbWVzID0gVCkgICU+JSANCiAgICBrYWJsZV9zdHlsaW5nKCdzdHJpcGVkJyxmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKQ0KDQpmb3IgKG0gaW4gMTpuY29sKHR3b3R3b2RmKSkgew0KICAgIHAgPSBjb2x1bW5fc3BlYyhwLCBtKzEsY29sb3I9ICd3aGl0ZScsDQogICAgICAgICAgICAgIGJhY2tncm91bmQgPSBzcGVjX2NvbG9yKHR3b3R3b2RmWyxtXSxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApDQogICAgICAgICAgICAgICAgICAgICkNCn0NCg0KcA0KDQpgYGANCg0KPGJyLz4NCg0KQW5kIGV4cGFuZCBpdCBhY2NvcmRpbmcgdG8gdGhlIHBvc3R1bGF0ZWQgbW9kZWxcDQoNCjxici8+DQoNCmBgYHtyfQ0KDQp0d290d29kZiR4MXgyICAgPSB4MSp4Mg0KdHdvdHdvZGYkeDF4MyAgID0geDEqeDMNCnR3b3R3b2RmJHgyeDMgICA9IHgyKngzDQp0d290d29kZiR4MXgyeDMgPSB4MSp4Mip4Mw0KDQpwID0ga2FibGUodHdvdHdvZGYsIHJvdy5uYW1lcyA9IFQpICAlPiUgDQogICAga2FibGVfc3R5bGluZygnc3RyaXBlZCcsZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkNCg0KZm9yIChtIGluIDE6bmNvbCh0d290d29kZikpIHsNCiAgICBwID0gY29sdW1uX3NwZWMocCwgbSsxLGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcih0d290d29kZlssbV0sZW5kID0gMC43LCBvcHRpb24gPSAnQScgKQ0KICAgICAgICAgICAgICAgICAgICApDQp9DQoNCnANCg0KDQpgYGANCg0KPGJyLz4NCg0KTm93IGVsaW1pbmF0ZSBhbGwgdGhlIHJvd3Mgb2YgdGhlIGNvbHVtbiBgeDF4MngzYCAobWF4aW11bSBvcmRlciBvZg0KaW50ZXJhY3Rpb24pIHdpdGggdGhlIHZhbHVlIGAtMWANCg0KYGBge3J9DQoNCnR3b3R3b2RmMiA9IHR3b3R3b2RmWyEodHdvdHdvZGYkeDF4MngzID09IC0xKSxdDQoNCnAgPSBrYWJsZSh0d290d29kZjIsIHJvdy5uYW1lcyA9IFQpICAlPiUgDQogICAga2FibGVfc3R5bGluZygnc3RyaXBlZCcsZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkNCg0KZm9yIChtIGluIDE6bmNvbCh0d290d29kZjIpKSB7DQogICAgcCA9IGNvbHVtbl9zcGVjKHAsIG0rMSxjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IodHdvdHdvZGYyWyxtXSxlbmQgPSAwLjcsIG9wdGlvbiA9ICdBJyApDQogICAgICAgICAgICAgICAgICAgICkNCn0NCg0KcA0KDQoNCmBgYA0KDQo8YnIvPg0KDQojIFBsYWNrZXR0LUJ1cm1hbiBkZXNpZ24NCg0KPGJyLz4NCg0KUGxhY2tldHQtLUJ1cm1hbiBkZXNpZ25zIGFyZSBleHBlcmltZW50YWwgZGVzaWducyBwcmVzZW50ZWQgaW4gMTk0NiBieQ0KUm9iaW4gTC4gUGxhY2tldHQgYW5kIEouIFAuIEJ1cm1hbiB3aGlsZSB3b3JraW5nIGluIHRoZSBCcml0aXNoIE1pbmlzdHJ5DQpvZiBTdXBwbHkuIFtAcGxhY2tldHQxOTQ2XVwNCg0KV2hlbiB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgaXMgZmFpcmx5IGxhcmdlLCB0aGUgY29uc3RyYWludCB0aGF0IHRoZQ0KbnVtYmVyIG9mIGV4cGVyaW1lbnRzIG11c3QgZXF1YWwgYSBwb3dlciBvZiAyIGNhbiBiZSByYXRoZXIgcmVzdHJpY3RpdmUuDQpGb3IgZXhhbXBsZSwgYSAqKjEwIGZhY3RvcnMqKiBhbmQgKioyIGxldmVscyoqIGZhY3RvcmlhbCBkZXNpZ24gaXQgd291bGQNCnJlcXVpcmUgJDJeezEwfSQgZXhwZXJpbWVudHMuXA0KDQpXaXRoIFBsYWNrZXR0LUJ1cm1hbiBkZXNpZ24gdGhlIG51bWJlciBvZiBleHBlcmltZW50cyByZXF1aXJlZCBpcyB0aGUNCmZpcnN0IG11bHRpcGxlIG9mIDQgZ3JlYXRlciB0aGFuIHRoZSBudW1iZXIgb2YgZmFjdG9ycy4gSW4gdGhpcyBjYXNlDQp0aGVyZWZvcmUgeW91IHdvdWxkIGhhdmUgb25seSAqKjEyIGV4cGVyaW1lbnRzISoqXA0KDQpUaGUgbnVtYmVyIG9mIHRvdGFsIGZhY3RvcnMgd2lsbCBiZSAkTi0xJCwgd2l0aCAkTiQgdGhlIG51bWJlciBvZg0KZXhwZXJpbWVudHMuXA0KDQpUaGUgYWRkaXRpb25hbCBmYWN0b3JzIGFyZSBjb25zaWRlcmVkIGFzIGR1bW15IHZhcmlhYmxlcywgdXNlZnVsIHRvDQplc3RpbWF0ZSB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSByZWFsIGZhY3RvcnMuXA0KDQo8YnIvPg0KDQo8Y2VudGVyPiFbMTIgcnVucyBQQiBkZXNpZ25dKGltYWdlcy9CMTJydW5kZXNpZ24tMDEucG5nKXt3aWR0aD0iMzcwIn08L2NlbnRlcj4NCg0KPGJyLz4NCg0KSW4gbnVtZXJpY2FsIHZhbHVlczpcDQoNCjxici8+DQoNCmBgYHtyfQ0KDQojIC0tLS0tICBGaXJzdCByb3cgZnJvbSBsaXRlcmF0dXJlIC0tLS0tLS0tLS0tLQ0KDQpyID0gYygxLAkxLAktMSwJMSwJMSwJMSwJLTEsCS0xLAktMSwJMSwJLTEpDQoNCnBsYWNrZGYgPSBkYXRhLmZyYW1lKHQocikpDQoNCmZvciAoaSBpbiAwOjkpIHsNCiAgcGxhY2tkZlsyK2ksXSA9IGMoclsoMTEtaSk6MTFdLHJbLWMoKDExLWkpOjExKV0pDQp9DQoNCnBsYWNrZGZbMTIsXSA9IHJlcCgtMSwxMSkNCg0KDQpmaWcgPSBwbGFja2RmICU+JSBrYWJsZShyb3cubmFtZXMgPSBUKSAlPiUga2FibGVfc3R5bGluZygnc3RyaXBlZCcsZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRikNCg0KZm9yIChtIGluIDE6bmNvbChwbGFja2RmKSkgew0KICAgIGZpZyA9IGNvbHVtbl9zcGVjKGZpZywgbSsxLGNvbG9yPSAnd2hpdGUnLA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcihwbGFja2RmWyxtXSxiZWdpbiA9IDAuMTUsICBlbmQgPSAwLjYsIG9wdGlvbiA9ICdCJyApDQogICAgICAgICAgICAgICAgICAgICkNCn0NCmZpZw0KDQpgYGANCg0KPGJyLz4NCg0KIyMgRXhhbXBsZQ0KDQo8YnIvPg0KDQpTdXBwb3NlIHlvdSBoYXZlIG1lYXN1cmVkIGFuIGV4cGVyaW1lbnRhbCByZXNwb25zZSBmb3IgYWxsIHRoZSBleHBlcmltZW50cyBcIA0KDQo8YnIvPg0KDQpgYGB7cn0NCg0KeSA9IGMoNTEsCTMxLAk0NCwJMjMsCTgwLAk0NSwJMzEsCTU1LAkyMywJMjYsCTI5LAkyOCkNCg0KcGxhY2tkZjIgPSBwbGFja2RmDQpwbGFja2RmMiA9IGNiaW5kKHBsYWNrZGYyLCAnWScgPSB5KQ0KDQpmaWcgPSBwbGFja2RmMiAlPiUga2FibGUocm93Lm5hbWVzID0gVCkgJT4lIGthYmxlX3N0eWxpbmcoJ3N0cmlwZWQnLGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IEYpDQoNCmZvciAobSBpbiAxOm5jb2wocGxhY2tkZikpIHsNCiAgICBmaWcgPSBjb2x1bW5fc3BlYyhmaWcsIG0rMSxjb2xvcj0gJ3doaXRlJywNCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IocGxhY2tkZlssbV0sYmVnaW4gPSAwLjE1LCAgZW5kID0gMC42LCBvcHRpb24gPSAnQicgKQ0KICAgICAgICAgICAgICAgICAgICApDQp9DQoNCmZpZyAlPiUgY29sdW1uX3NwZWMoMTMsIGNvbG9yPSAnd2hpdGUnLCBiYWNrZ3JvdW5kID0gJ2RhcmtncmVlbicpDQogIA0KDQpgYGANCjxici8+DQoNClRoZSBwb3N0dWxhdGVkIG1vZGVsIHdpbGwgYmVcDQoNCjxici8+DQoNCiQkDQogIFkgPSBiXzAgKyBiXzF4XzErYl8yeF8yKyBiX3szfXhfezN9KyAgYl97NH14X3s0fSsgICBiX3s1fXhfezV9KyAgIGJfezZ9eF97Nn0rICAgYl97N314X3s3fSsgICBiX3s4fXhfezh9KyAgIGJfezl9eF97OX0rICBiX3sxMH14X3sxMH0rYl97MTF9eF97MTF9DQokJCANCjxici8+DQoNCk5vdyB3ZSdyZSBhYmxlIHRvIHBlcmZvcm0gYSByZWdyZXNzaW9uIFwgDQoNCmBgYHtyfQ0KY29sbmFtZXMocGxhY2tkZjIpWzExXSA9ICdEdW1teScNCm1vZGVsID0gbG0oWSB+IFgxK1gyK1gzK1g0K1g1K1g2K1g3K1g4K1g5K1gxMCtEdW1teSwgcGxhY2tkZjIpDQptb2RlbCAlPiUgc3VtbWFyeSgpDQoNCmBgYA0KPGJyLz4NCg0KU2luY2UgdGhlcmUgYXJlIG5vIHJlc2lkdWFsIGRlZ3JlZXMgb2YgZnJlZWRvbSB3ZSBjYW5ub3QgZXN0aW1hdGUgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgY29lZmZpY2llbnRzIGFzIHVzdWFsIFwNCg0KV2UgbmVlZCBhbm90aGVyIGFwcHJvYWNoIHRvIHVuZGVyc3RhbmQgd2hpY2ggb2YgdGhlbSBhcmUgYWN0dWFsbHkgc2lnbmlmaWNhbnQuIFwNCg0KPGJyLz4NCg0KYGBge3J9DQoNCmRmID0gY29lZmZpY2llbnRzKG1vZGVsKVstMV0gJT4lIGRhdGEuZnJhbWUoKQ0KY29sbmFtZXMoZGYpID0gJ2NvZWZmJw0KZGYkWCA9IHJvd25hbWVzKGRmKQ0KZGYkWCA9IGZhY3RvcihkZiRYLCBsZXZlbHMgPSBkZiRYKQ0KDQpnZ3Bsb3QoZGYsIGFlcyh4PSBYLCBjb2VmZiwgZmlsbCA9aWZlbHNlKFggPT0gJ0R1bW15JywgJ0R1bW15JywgJ25vcm1hbCcpKSkrDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKCB2YWx1ZXMgPSBjKCAiRHVtbXkiPSJkYXJrcmVkIiwgIm5vcm1hbCI9ImNvcm5mbG93ZXJibHVlIiApKSsNCiAgdGhlbWVfYncoKSsNCiAgeGxhYignJykrDQogIHlsYWIoJycpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjb2VmZmljaWVudHMobW9kZWwpWzEyXSwgbGluZXR5cGUgPSAnbG9uZ2Rhc2gnLCBjb2xvciA9ICdyZWQnKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWNvZWZmaWNpZW50cyhtb2RlbClbMTJdLCBsaW5ldHlwZSA9ICdsb25nZGFzaCcsIGNvbG9yID0gJ3JlZCcpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLGFscGhhID0gMC41KSAgDQoNCmBgYA0KDQo8YnIvPg0KDQpDb2VmZmljaWVudHMgb2Ygc2ltaWxhciBzaXplIHRvIHRoZSBEdW1teSBhcmUgbm8gcGFydGljdWxhcmx5IHNpZ25pZmljYW50ISBcDQoNCkluIHRoaXMgY2FzZSBvbmx5IGZhY3RvcnMgYFg2YCBhbmQgYFg3YCBhcmUgcmVsZXZhbnQgYW5kIG1heWJlIHVzZWQgbGF0ZXIgZm9yIGZ1cnRoZXIgZXhwZXJpbWVudGFsIGRlc2lnbi4gXA0KDQo8YnIvPg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KPGJyLz4NCg0KPGJyLz4NCg0KPGJyLz4NCg0KPGJyLz4NCg0KPGJyLz4NCg0KPGJyLz4NCg0KIyBSZWZlcmVuY2VzDQoNCjxici8+DQo=