Creative Commons License

1 Concepten

  • Experimentele eenheden zijn subjecten, planten, proefdieren of objecten die aan behandelingen worden onderworpen in een experimentele studie.
  • Observationele eenheden zijn de subjecten, planten proefdieren of objecten waarop we metingen doen. Meestal zijn de experimentele en observationele eenheden hetzelfde. Als dat niet het geval is heeft met vaak pseudoreplicatie.
  • Experimentele eenheden representatief voor populatie: Randomisatie
  • Replicatie: technisch vs biologisch, steekproefgrootte - power
  • Bronnen van variabiliteit: technisch, biologisch, binnen en tussen subjecten.

2 Replicatie

Paper over replicatie in Nature Methods (2 pagina’s) [PDF]

2.1 Voorbeeld

In een RNA-seq experiment willen de onderzoekers het effect nagaan van een medicijn op de gen expressie.

Mogelijkse onderzoeksvraag

  • Is er een verschil in gemiddelde gen expressie tussen behandelde en niet behandelde samples

2.2 Bronnen van variabiliteit

TABLE 1 NATURE METHODS | VOL.11 NO.9 | SEPTEMBER 2014 | 879 - 880

Replicate type Replicate category\(^\text{a}\)
Colonies B
Strains B
Animal study subjects Cohoused groups B
Gender B
Individuals B
Organs from sacrificed animals B
Methods for dissociating cells from tissue T
Sample preparation Dissociation runs from given tissue sample T
Individual cells B
RNA-seq library construction T
Runs from the library of a given cell T
Sequencing Reads from different transcript molecules V\(^\text{b}\)
Reads with unique molecular identifier (UMI) from a given transcript molecule T
  1. Replicates are categorized as biological (B), technical (T) or of variable type (V).
  2. Sequence reads serve diverse purposes depending on the application and how reads are used in analysis.

2.3 Op welk niveau moeten we repliceren?

  • \(\text{var}(X)=\sigma^2_A+\sigma^2_C+\sigma^2_M=\sigma^2_{TOT}\)
  • \(\text{var}(\bar{X})=\frac{\sigma^2_A}{n_A}+\frac{\sigma^2_C}{n_A n_C} + \frac{\sigma^2_M}{n_A n_C n_M}\)

Figure 1 NATURE METHODS | VOL.11 NO.9 | SEPTEMBER 2014 | 879 - 880

  1. Three levels of replication (two biological, one technical) with animal, cell and measurement replicates normally distributed with a mean across animals of 10 and ratio of variances 1:2:0.5. Solid green (biological) and blue (technical) dots show how a measurement of the expression (X = 12) samples from all three sources of variation. Distribution s.d. is shown as horizontal lines.
  2. Expression variance, Var(X), and variance of expression mean, Var(\(\bar X\)), computed across 10,000 simulations of nAnCnM = 48 measurements for unique combinations of the number of animals (nA = 1 to 48), cells per animal (nC = 1 to 48) and technical replicate measurements per cell (nM = 1 and 3). The ratio of Var(X) and Var(\(\bar X\)) is the effective sample size, n, which corresponds to the equivalent number of statistically independent measurements. Horizontal dashed lines correspond to biological and total variation. Error bars on Var(X) show s.d. from the 10,000 simulated samples (nM = 1).

Waar ligt interesse?

  • Karakteriseren van bronnen van variabiliteit in experiment ==> technische en biologische repeats zijn nodig

  • Voornamelijke interesse in schatten van effect van behandeling ==> focus op biologische repeats

  • Extra biologische repeats lumpen alle bronnen van variabiliteit: biologische + technische variabiliteit.

  • Goed experimenteel design vereist na denken over replicatie

    1. Identificeer onderzoeksvragen die men wenst te beantwoorden.
    2. Breng bronnen van variabiliteit in kaart in elke stap van het experiment
    3. Let op voor pseudoreplicatie, beoog zoveel mogelijk onafhankelijke repeats.

3 Power, steekproefgrootte en andere design aspecten.

Reading materials: Nature Methods (2013), 10(12), 1139–1140

3.1 Intermezzo lineare regression in matrix vorm

  • Lineaire regressie is een belangrijke methode in statistische data analyse.

3.1.1 Scalaire vorm

  • Stel dat men voor elke experimentele eenheid \(p\) predictoren meet \(\mathbf{x}=(x_1,\ldots,x_p)\) en
  • continue respons \(Y\)
  • het linear regressiemodel kan dan worden geschreven als: \[ Y=f(\mathbf{x}) +\epsilon=\beta_0+\sum\limits_{j=1}^p x_j\beta_j + \epsilon \] met i.i.d. \(\epsilon\sim N(0,\sigma^2)\)

3.1.2 Vector/Matrix vorm

  • \(n\) observatie \((\mathbf{x}_1,y_1) \ldots (\mathbf{x}_n,y_n)\) with \(\mathbf{x}_1^T=[1 x_1 \ldots x_p]\)
  • Regressie in matrix notatie \[\mathbf{Y}=\mathbf{X\beta} + \mathbf{\epsilon}\] met \(\mathbf{Y}=\left[\begin{array}{c}y_1\\ \vdots\\y_n\end{array}\right]\), \(\mathbf{X}=\left[\begin{array}{cccc} 1&x_{11}&\ldots&x_{1p}\\ \vdots&\vdots&&\vdots\\ 1&x_{n1}&\ldots&x_{np} \end{array}\right]\) or \(\mathbf{X}=\left[\begin{array}{c} \mathbf{x}_1^T\\\vdots\\\mathbf{x}_n^T\end{array}\right]\), \(\boldsymbol{\beta}=\left[\begin{array}{c}\beta_0\\ \vdots\\ \beta_p\end{array}\right]\) and \(\mathbf{\epsilon}=\left[\begin{array}{c} \epsilon_1 \\ \vdots \\ \epsilon_n\end{array}\right]\)

3.2 kleinste kwadraten techniek/Least Squares (LS)

  • Minimimaliseer de residuele kwadratensom \[\begin{eqnarray*} RSS(\boldsymbol{\beta})&=&\sum\limits_{i=1}^n e^2_i\\ &=&\sum\limits_{i=1}^n \left(y_i-\beta_0-\sum\limits_{j=1}^p x_{ij}\beta_j\right)^2 \end{eqnarray*}\]

  • of in matrix notatie

\[ \text{RSS}(\boldsymbol{\beta})=(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta}) \]

\[\rightarrow \hat{\boldsymbol{\beta}}=\text{argmin}_\beta \text{ RSS}(\boldsymbol{\beta})\]


3.2.1 Minimimaliseer RSS

\[ \begin{array}{ccc} \frac{\partial RSS}{\partial \boldsymbol{\beta}}&=&\mathbf{0}\\\\ \frac{(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}&=&\mathbf{0}\\\\ -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})&=&\mathbf{0}\\\\ \mathbf{X}^T\mathbf{X\beta}&=&\mathbf{X}^T\mathbf{Y}\\\\ \hat{\boldsymbol{\beta}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y} \end{array} \]


3.3 Variantie schatter?

\[ \begin{array}{ccl} \hat{\boldsymbol{\Sigma}}_{\hat{\boldsymbol{\beta}}} &=&\text{var}\left[(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}\right]\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\text{var}\left[\mathbf{Y}\right]\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T(\mathbf{I}\sigma^2)\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1} \\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{I}\quad\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ %\hat{\boldmath{\Sigma}}_{\hat{\boldsymbol{\beta}}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\text{var}\left[\mathbf{Y}\right](\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2 \end{array} \]

De onzekerheid op model parameters hangt dus af van de residuele variabiliteit en het de proefopzet!

  • Hoe groter \(\mathbf{X}^T\mathbf{X}\) hoe meer informatie het experiment zal hebben over de model parameters en hoe kleiner hun variantie en standaard errors!

  • Factoriële designs?

  • Designs with continue predictoren?


De effectgrootte van interesse is typisch een lineaire combinatie van de model parameters,
\[ l_0 \times \beta_0 + l_1 \times \beta_1 + ... + l_{p-1} \times \beta_{p-1} = \mathbf{L}^T\boldsymbol{\beta} \]

De nulhypothese van onze test kan dan worden geschreven als

\[ H_0: \mathbf{L}^T\boldsymbol{\beta} = 0 \]

vs de alternatieve hypothese

\[ H_1: \mathbf{L}^T\boldsymbol{\beta} \neq 0 \]

En het bewijs in het experiment tegen \(H_0\) kan worden gekwantificeerd met de t-test statistiek:

\[ t=\frac{\mathbf{L}^T\hat{\boldsymbol{\beta}} - 0}{\text{se}_{\mathbf{L}^T\hat{\boldsymbol{\beta}}}} \] die een t-distributie volgt met n-p vrijheidsgraden onder de nul hypothese als alle aannames van het model geldig zijn.

De kracht van de toets is dan

\[P(p < 0.05 | H_1)\] en hangt af van

  • de werkelijke effectgrootte in de populatie \(\mathbf{L}^T\boldsymbol{\beta}\).
  • Het aantal observaties: SE en df van t-test.
  • Keuze van de designpunten
  • Keuze van significantie-niveau \(\alpha\).

We kunnen dit evalueren aan de hand van simulaties.

3.4 Muis voorbeeld

  • In 2021 publiceerden Choa et al. dat cytokine Thymic stromal lymphopoietin (TSLP) vetverlies induceert bij muizen die een vet dieet krijgen door de secretie van talg. [html] [PDF]

  • Stel dat je een gelijkaardig experiment op wil zetten om na te gaan of cytokine interleukin 25 (IL) ook een effect heeft op het gewicht van muizen.

  • Je plant een studie met een controle groep muizen die een hoog vet dieet krijgen (HFD) en een behandelingsgroep die een HFD dieet met IL krijgt.

  • Wat is de vereiste steekproefgrootte om een bepaald effect op te pikken van de behandeling?

3.4.1 Hoe zal je de data van het experiment analyseren?


  • \(H_0\): Er is geen verschil in het gewicht van muizen die een dieet met IL krijgen en muizen die het controle dieet krijgen.

  • \(H_1\): Het gewicht is gemiddelde verschillend tussen muizen die het IL dieet krijgen en muizen die het controle dieet krijgen.

  • Two sample t-test of een t-test op de helling van het lineaire model met 1 dummy variabele

\[ Y_i = \beta_0 + \beta_1 X_\text{iIL} + \epsilon_i\]

\[ \text{met } X_\text{iIL}=\begin{cases}X_{iIL}=0 & \text{HFD}\\X_{iIL}=1 & \text{HFD + IL}\end{cases}. \]

  • Geschatte effectgrootte?

\[\hat\delta = \bar X_{IL} - \bar X_{c} = \hat \beta_1\]

  • Test statistiek \[ T = \frac{\bar{X}_{IL}-\bar{X}_{c}}{SD_\text{pooled} \times \sqrt{\frac{1}{n_1} + \frac{1}{n_2}}} = \frac{\hat\beta_1}{\text{SE}_{\hat\beta_1}} \]
  • \(\hat \beta_1\) is een onvertekende schatter van het werkelijk gewichtsverschil (\(\delta =\mu_{IL}-\mu_{c} = \beta_1\)) dat zou voorkomen in de populatie van muizen gevoed met HFD vs muizen gevoed met HFD + IL.

3.4.2 Power?

\[ P[p < \alpha \vert \beta_1 \neq 0] \]

De kracht (power) zal dus afhangen van

  • Het werkelijke gemiddelde gewichtsverschil \(\beta_1\) in de populatie.
  • De variabiliteit van de gewichtsmetingen
  • het significantie-niveau \(\alpha\)
  • steekproefgrootte \(n_{IL}\) en \(n_c\) in beide groepen

We kunnen de power schatten als

  1. voldaan is aan de aannamens van het model: gewicht in beide groepen is normaal verdeeld met gelijke variantie

en als we de

  1. standaard deviatie kennen van gewichtsmetingen rond hun groepsgemiddelde voor muizen die een HFD diet krijgen

  2. de echte effectgrootte in de populatie

  3. \(n_1\) en \(n_2\)

3.4.3 Gebruik data uit vorig experiment om inzicht te krijgen in de gewichtsdata van muizen

Stel dat we toegang hebben tot data van een vorig experiment (hier dat van Karen Svenson en Dan Gatti, gefinancierd door P50 GM070683 en beschikbaar op PH525x)

mice <- read.csv("https://raw.githubusercontent.com/genomicsclass/dagdata/master/inst/extdata/femaleMiceWeights.csv")

mice %>% 
  ggplot(aes(x=Diet,y=Bodyweight)) +
  geom_boxplot(outlier.shape=FALSE) +
  geom_jitter()

mice %>% 
  ggplot(aes(sample=Bodyweight)) +
  geom_qq() + 
  geom_qq_line() +
  facet_wrap(~Diet)

mice <- mice %>% mutate(Diet = as.factor(Diet))
miceSum <- mice %>% 
  group_by(Diet)  %>%
  summarize_at("Bodyweight",list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))
miceSum 

In het experiment werden twee diëten gebruikt:

  • gewoon diet o.b.v. granen (Chow)
  • Heel vet dieet (high fat: hf)

We kunnen de data van de hf muizen gebruiken als input voor onze power analyse.

  • De data van hf muizen lijken normaal verdeeld te zijn.
  • Het gemiddelde gewicht van een hf muis is 26.8g
  • De standaard deviatie (SD) van het gewicht van hf muizen is 4.1g

Effectgroote?

  • De alternative hypothese is complex.
  • Omvat alle mogelijke effecten!
  • Om een power analyse te doen kiezen we daarom een minimale effectgrootte die we wensen op te pikken in het nieuwe experiment.
  • Veronderstel dat we een gewichtsverschil willen oppikken van minimaal 10%.
delta <- - round(miceSum$mean[2] *.1,1)
delta
[1] -2.7

Merk op dat het gemiddeld gewicht dan dicht komt bij dat van muizen in het pilootexperiment die werden gevoed met het normale dieet.

We kunnen nu een simulatie studie opzetten om de power te berekenen voor een experiment met 8 muizen in elke groep.

3.4.3.1 Eén simulatie

set.seed(1423)
n1 <- n2 <- 8
b0 <- round(miceSum$mean[2],1)
b1 <- - delta
sd <- round(miceSum$sd[2],1)
alpha <- 0.05

x <- rep(0:1,c(n1,n2))
y <- b0 + b1 * x + rnorm(n1+n2,0, sd = sd)
fit <- lm(y~x)
bhat <- fit$coef 
stat <- summary(fit)
summary(fit)$coef[2,]
  Estimate Std. Error    t value   Pr(>|t|) 
 2.5972480  1.7919658  1.4493848  0.1692595 

Voor het gesimuleerde experiment konden we het effect van de behandeling niet oppikken bij het \(\alpha\)=0.05 (p = 0.17) significantie-niveau!

  • We moeten het experiment herhalen om de power te schatten.
  • We zullen de simulatie daarom coderen in een functie die we kunnen hergebruiken.

3.4.3.2 Simulatie van herhaalde experimenten (repeated experiments)

library(multcomp)
n1 <- n2 <- 8

b0 <- round(miceSum$mean[2],1)
b1 <- - delta

sd <- round(miceSum$sd[2],1)
predictorData <- data.frame(Diet = rep(c("c","hf"),c(n1,n2)) %>% as.factor)

alpha <- 0.05

simLm <- function(form, data, betas, sd, contrasts, simIndex = NA)
{
  dataSim <- data
  X <- model.matrix(form,dataSim)
  dataSim$ySim <- X%*%betas + rnorm(nrow(dataSim),0,sd)
  form <- formula(paste("ySim ~",form[[2]]))
  fitSim <- lm(form,dataSim)
  mcp <- glht(fitSim,linfct = contrasts)
  return(summary(mcp)$test[c("coefficients","sigma","tstat","pvalues")]%>% unlist)
}

simLm(
  form = ~Diet,
  data = predictorData,
  betas = c(b0,b1),
  sd = sd,
  contrasts = "Diethf = 0")
coefficients.Diethf        sigma.Diethf        tstat.Diethf             pvalues 
          1.9444623           2.0931302           0.9289734           0.3686439 

We hebben nu een generiek functie waarmee we data kunnen simuleren die normaal verdeeld zijn voor elk mogelijk design die we met het lineair model kunnen analyseren.

De functie heeft volgende argumenten:

  • form: One sided formula including the structure of the predictors in the model
  • data: Data frame with the predictor values for the design
  • betas: A vector with values for all mean model parameters
  • sd: The standard deviation of the error
  • contrasts: a scalar or a vector with the null hypotheses that we would like to assess.
  • simIndex: an arbitrary argument that is not used by the function but that will allow it to be used in an sapply loop that runs from 1 up to the number of simulations.

Simuleer nSim = 1000 herhaalde experimenten:

set.seed(1425)
nSim <- 1000

simResults  <- t(sapply(1:nSim,simLm,form = ~Diet,data = predictorData,betas = c(b0,b1),sd = sd,contrasts = "Diethf = 0"))
power <- mean(simResults[,4] < alpha)
power
[1] 0.221

We hebben een power van 22.1% om het behandelingseffect op te pikken met 8 biologische herhalingen in elke behandelingsgroep.

Merk op dat de power van een experiment ook het hoogste is als het aantal observaties gebalanceerd is, m.a.w. \(n_1=n_2\).

3.4.3.3 Power voor verschillende steekproefgroottes

We kunnen de power nu berekenen voor meerdere steekproefgrootes.

power <- data.frame(n=c(3,5,10,25,50,75,100),power=NA)

for (i in 1:nrow(power))
{
  n1 <- n2 <- power$n[i]
  predictorData <- data.frame(Diet = rep(c("c","hf"),c(n1,n2)) %>% as.factor)
  simResults  <- t(sapply(1:nSim,simLm,form = ~Diet,data = predictorData,betas = c(b0,b1),sd = sd,contrasts = "Diethf = 0"))
  power$power[i] <- mean(simResults[,"pvalues"] < alpha)
}
power
power %>% 
  ggplot(aes(x=n,y=power)) +
  geom_line()

3.4.3.4 Bestudeer power in functie van steekproefgrootte en effectgrootte

Merk op dat

  • het teken van delta arbitrair is omdat we tweezijdig testen.
  • het intercept arbitrair is omdat we alleen testen op \(\beta_1\)
  • We nemen daarom typisch \(\beta_0 = 0\)
nSim <- 1000
b0 <- 0
deltas <- c(1,2,3,5,10)
sd <- round(miceSum$sd[2],1)
ns <-  c(3,5,10,20,25,50,75,100)  

power <- data.frame(b1=rep(deltas,each=length(ns)),
                    n=rep(ns,length(deltas)),
                    power=NA)

for (i in 1:nrow(power))
{
  b1 <-power$b1[i]
  n1 <- n2 <-  power$n[i]
  predictorData <- data.frame(Diet = rep(c("c","hf"),c(n1,n2)) %>% as.factor)
  simResults  <- t(sapply(1:nSim,simLm,form = ~Diet,data = predictorData,betas = c(b0,b1),sd = sd,contrasts = "Diethf = 0"))
  power$power[i] <- mean(simResults[,"pvalues"] < alpha)
}
power %>% 
  ggplot(aes(x=n,y=power,col=b1%>%as.factor)) +
  geom_line()

Merk op dat de power curves wat schokkerig zijn omdat we maar een beperkt aantal steekproefgroottes en een beperkt aantal simulaties uitvoerden. Voor de lage powers is het aantal simulaties zeker onvoldoende om heel betrouwbare schattingen van de power te bekomen.

4 Opmerkingen

  • De code kan eenvoudig aangepast worden naar andere designs (zie oefeningen)

    • predictor data
    • formula
    • contrast
  • De simulaties kunnen lang duren wanneer je veel scenario’s evalueert.

    • Meer efficiënte code met matrices
    • Voor two group comparison bestaat een analytische oplossing.

4.1 Meer efficiënte code met matrices

Code is veel sneller. We simulaleren nu 20 keer meer experimenten in een veel kortere tijd!

nSim <- 20000
b0 <- 0
sd <- round(miceSum$sd[2],1)
ns <-  c(3,5,10,20,25,50,75,100)  
deltas <- c(1,2,3,5,10)


powerFast <- matrix(NA,nrow=length(ns)*length(deltas),ncol=3) %>% as.data.frame
names(powerFast) <- c("b1","n","power")

i <- 0

for (n in ns)
{
  n1 <- n2 <-  n
  
  ### Simulation
  predictorData <- data.frame(Diet = rep(c("c","hf"),c(n1,n2)) %>% as.factor)
  design <- model.matrix(~Diet,predictorData)
  L <- limma::makeContrasts("Diethf",levels = colnames(design))

  
  for (b1 in deltas)
  {
    ySim <- rnorm(nrow(predictorData)*nSim,sd=sd)
    dim(ySim) <-c(nrow(predictorData),nSim)
    ySim <- ySim + c(design %*%c(b0,b1))
    ySim <- t(ySim)
  
    ### Fitting
    fitAll <- limma::lmFit(ySim,design)
  
    ### Inference
    varUnscaled <- c(t(L)%*%fitAll$cov.coefficients%*%L)
    contrasts <- fitAll$coefficients %*%L
    seContrasts <- varUnscaled^.5*fitAll$sigma
    tstats <- contrasts/seContrasts
    pvals <- pt(abs(tstats),fitAll$df.residual,lower.tail = FALSE)*2
    
    i <- i+1
    powerFast[i,] <- c(b1,n,mean(pvals < alpha))
  }
}
powerFast %>% 
  ggplot(aes(x=n,y=power,col=b1%>%as.factor)) +
  geom_line()

4.2 Meer efficiënte code gebaseerd op de analytische oplossing voor een two group comparison

  • Voor de two sample t-test bestaat er een analytische oplossing om de power te berekenen.

  • In deze context maakt men typisch gebruik van de “Cohen’s effect size”:

\(D = \frac{\delta}{SD}\)

power.t.test(n=8, delta = delta, sd = sd, type='two.sample')

     Two-sample t test power calculation 

              n = 8
          delta = 2.7
             sd = 4.1
      sig.level = 0.05
          power = 0.2320512
    alternative = two.sided

NOTE: n is number in *each* group

Merk op dat de power overeenkomt met degene die we via simulaties hebben geschat!

b0 <- 0
sd <- round(miceSum$sd[2],1)
ns <-  c(3,5,10,20,25,50,75,100)  
deltas <- c(1,2,3,5,10)


powerTheo <- data.frame(deltas=rep(deltas,each=length(ns)),
                    n=rep(ns,length(deltas)),
                    power=NA)

powerTheo$power <- apply(powerTheo[,1:2],1,function(x) power.t.test(delta=x[1],n=x[2],sd=sd,type="two.sample")$power)
powerTheo %>% 
  ggplot(aes(x=n,y=power,col=deltas%>%as.factor)) +
  geom_line()

LS0tCnRpdGxlOiAiRXhwZXJpbWVudGVlbCBEZXNpZ24gSTogcmVwbGljYXRpZSBlbiBwb3dlciIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKLS0tCgoKPGEgcmVsPSJsaWNlbnNlIiBocmVmPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtc2EvNC4wIj48aW1nIGFsdD0iQ3JlYXRpdmUgQ29tbW9ucyBMaWNlbnNlIiBzdHlsZT0iYm9yZGVyLXdpZHRoOjAiIHNyYz0iaHR0cHM6Ly9pLmNyZWF0aXZlY29tbW9ucy5vcmcvbC9ieS1uYy1zYS80LjAvODh4MzEucG5nIiAvPjwvYT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoaW5jbHVkZSA9IFRSVUUsIGNvbW1lbnQgPSBOQSwgZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKCmBgYHtyIHBvcDJTYW1wMlBvcCxmaWcuYXNwPS44LCBmaWcuYWxpZ249J2NlbnRlcicsZWNobz1GQUxTRX0KaWYgKCJwaSIlaW4lbHMoKSkgcm0oInBpIikKa29wdm9ldGVyPC1mdW5jdGlvbih4LHksYW5nbGU9MCxsPS4yLGNleC5kb3Q9LjUscGNoPTE5LGNvbD0iYmxhY2siKQp7CmFuZ2xlPWFuZ2xlLzE4MCpwaQpwb2ludHMoeCx5LGNleD1jZXguZG90LHBjaD1wY2gsY29sPWNvbCkKbGluZXMoYyh4LHgrbCpjb3MoLXBpLzIrYW5nbGUpKSxjKHkseStsKnNpbigtcGkvMithbmdsZSkpLGNvbD1jb2wpCmxpbmVzKGMoeCtsLzIqY29zKC1waS8yK2FuZ2xlKSx4K2wvMipjb3MoLXBpLzIrYW5nbGUpK2wvNCpjb3MoYW5nbGUpKSxjKHkrbC8yKnNpbigtcGkvMithbmdsZSkseStsLzIqc2luKC1waS8yK2FuZ2xlKStsLzQqc2luKGFuZ2xlKSksY29sPWNvbCkKbGluZXMoYyh4K2wvMipjb3MoLXBpLzIrYW5nbGUpLHgrbC8yKmNvcygtcGkvMithbmdsZSkrbC80KmNvcyhwaSthbmdsZSkpLGMoeStsLzIqc2luKC1waS8yK2FuZ2xlKSx5K2wvMipzaW4oLXBpLzIrYW5nbGUpK2wvNCpzaW4ocGkrYW5nbGUpKSxjb2w9Y29sKQpsaW5lcyhjKHgrbCpjb3MoLXBpLzIrYW5nbGUpLHgrbCpjb3MoLXBpLzIrYW5nbGUpK2wvMipjb3MoLXBpLzIrcGkvNCthbmdsZSkpLGMoeStsKnNpbigtcGkvMithbmdsZSkseStsKnNpbigtcGkvMithbmdsZSkrbC8yKnNpbigtcGkvMitwaS80K2FuZ2xlKSksY29sPWNvbCkKbGluZXMoYyh4K2wqY29zKC1waS8yK2FuZ2xlKSx4K2wqY29zKC1waS8yK2FuZ2xlKStsLzIqY29zKC1waS8yLXBpLzQrYW5nbGUpKSxjKHkrbCpzaW4oLXBpLzIrYW5nbGUpLHkrbCpzaW4oLXBpLzIrYW5nbGUpK2wvMipzaW4oLXBpLzItcGkvNCthbmdsZSkpLGNvbD1jb2wpCn0KCnBhcihtYXI9YygwLDAsMCwwKSxtYWk9YygwLDAsMCwwKSkKcGxvdCgwLDAseGxhYj0iIix5bGFiPSIiLHhsaW09YygwLDEwKSx5bGltPWMoMCwxMCksY29sPTAseGF4dD0ibm9uZSIseWF4dD0ibm9uZSIsYXhlcz1GQUxTRSkKcmVjdCgwLDYsMTAsMTAsYm9yZGVyPSJyZWQiLGx3ZD0yKQp0ZXh0KC41LDgsInBvcHVsYXRpZSIsc3J0PTkwLGNvbD0icmVkIixjZXg9MikKc3ltYm9scyAoMywgOCwgY2lyY2xlcz0xLjUsIGNvbD0icmVkIixhZGQ9VFJVRSxmZz0icmVkIixpbmNoZXM9RkFMU0UsbHdkPTIpCmdyaWQ9c2VxKDAsMS4zLC4wMSkKCmZvciAoaSBpbiAxOjUwKQp7CglhbmdsZTE9cnVuaWYobj0xLG1pbj0wLG1heD0zNjApCglhbmdsZTI9cnVuaWYobj0xLG1pbj0wLG1heD0zNjApCglyYWRpdXM9c2FtcGxlKGdyaWQscHJvYj1ncmlkXjIqcGkvc3VtKGdyaWReMipwaSksc2l6ZT0xKQoJa29wdm9ldGVyKDMrcmFkaXVzKmNvcyhhbmdsZTEvMTgwKnBpKSw4K3JhZGl1cypzaW4oYW5nbGUxLzE4MCpwaSksYW5nbGU9YW5nbGUyKQp9CnRleHQoNy41LDgsIkVmZmVjdCB2YW4gcm9rZW4gaW4gcG9wdWxhdGllIixjb2w9InJlZCIsY2V4PTEuMikKCnJlY3QoMCwwLDEwLDQsYm9yZGVyPSJibHVlIixsd2Q9MikKdGV4dCguNSwyLCJzYW1wbGUiLHNydD05MCxjb2w9ImJsdWUiLGNleD0yKQpzeW1ib2xzICgzLCAyLCBjaXJjbGVzPTEuNSwgY29sPSJyZWQiLGFkZD1UUlVFLGZnPSJibHVlIixpbmNoZXM9RkFMU0UsbHdkPTIpCmZvciAoaSBpbiAwOjIpCglmb3IgKGogaW4gMDo0KQp7CgoJa29wdm9ldGVyKDIuMStqKigzLjktMi4xKS80LDEuMStpKQp9CnRleHQoNy41LDIsIkVmZmVjdCB2YW4gcm9rZW4gaW4gc3RlZWtwcm9lZiIsY29sPSJibHVlIixjZXg9MS4yKQoKYXJyb3dzKDMsNS45LDMsNC4xLGNvbD0iYmxhY2siLGx3ZD0zKQphcnJvd3MoNyw0LjEsNyw1LjksY29sPSJibGFjayIsbHdkPTMpCnRleHQoMS41LDUsIkVYUC4gREVTSUdOICgxKSIsY29sPSJibGFjayIsY2V4PTEuMikKdGV4dCg4LjUsNSwiU0NIQVRURU4gJlxuSU5GRVJFTlRJRSAoMykiLGNvbD0iYmxhY2siLGNleD0xLjIpCnRleHQoNy41LC41LCJEQVRBIEVYUExPUkFUSUUgJlxuQkVTQ0hSSUpWRU5ERSBTVEFUSVNUSUVLICgyKSIsY29sPSJibGFjayIsY2V4PTEuMikKYGBgCgoKIyBDb25jZXB0ZW4KCi0gRXhwZXJpbWVudGVsZSBlZW5oZWRlbiB6aWpuIHN1YmplY3RlbiwgcGxhbnRlbiwgcHJvZWZkaWVyZW4gb2Ygb2JqZWN0ZW4gZGllIGFhbiBiZWhhbmRlbGluZ2VuIHdvcmRlbiBvbmRlcndvcnBlbiBpbiBlZW4gZXhwZXJpbWVudGVsZSBzdHVkaWUuIAotIE9ic2VydmF0aW9uZWxlIGVlbmhlZGVuIHppam4gZGUgc3ViamVjdGVuLCBwbGFudGVuIHByb2VmZGllcmVuIG9mIG9iamVjdGVuIHdhYXJvcCB3ZSBtZXRpbmdlbiBkb2VuLiBNZWVzdGFsIHppam4gZGUgZXhwZXJpbWVudGVsZSBlbiBvYnNlcnZhdGlvbmVsZSBlZW5oZWRlbiBoZXR6ZWxmZGUuIEFscyBkYXQgbmlldCBoZXQgZ2V2YWwgaXMgaGVlZnQgbWV0IHZhYWsgcHNldWRvcmVwbGljYXRpZS4gCi0gRXhwZXJpbWVudGVsZSBlZW5oZWRlbiByZXByZXNlbnRhdGllZiB2b29yIHBvcHVsYXRpZTogUmFuZG9taXNhdGllCi0gUmVwbGljYXRpZTogdGVjaG5pc2NoIHZzIGJpb2xvZ2lzY2gsIHN0ZWVrcHJvZWZncm9vdHRlIC0gcG93ZXIKLSBCcm9ubmVuIHZhbiB2YXJpYWJpbGl0ZWl0OiB0ZWNobmlzY2gsIGJpb2xvZ2lzY2gsIGJpbm5lbiBlbiB0dXNzZW4gc3ViamVjdGVuLiAKCiMgUmVwbGljYXRpZSAKClBhcGVyIG92ZXIgcmVwbGljYXRpZSBpbiBOYXR1cmUgTWV0aG9kcyAoMiBwYWdpbmEncykgW1tQREZdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbm1ldGguMzA5MS5wZGYpXQohW10oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9ubWV0aC4zMDkxLnBkZil7IHdpZHRoPTEwMCUgfQoKIyMgVm9vcmJlZWxkCgpJbiBlZW4gUk5BLXNlcSBleHBlcmltZW50IHdpbGxlbiBkZSBvbmRlcnpvZWtlcnMgaGV0IGVmZmVjdCBuYWdhYW4gdmFuIGVlbiBtZWRpY2lqbiBvcCBkZSBnZW4gZXhwcmVzc2llLiAKCk1vZ2VsaWprc2Ugb25kZXJ6b2Vrc3ZyYWFnCgotIElzIGVyIGVlbiB2ZXJzY2hpbCBpbiBnZW1pZGRlbGRlIGdlbiBleHByZXNzaWUgdHVzc2VuIGJlaGFuZGVsZGUgZW4gbmlldCBiZWhhbmRlbGRlIHNhbXBsZXMKCiMjIEJyb25uZW4gdmFuIHZhcmlhYmlsaXRlaXQKClRBQkxFIDEgTkFUVVJFIE1FVEhPRFMgfCBWT0wuMTEgTk8uOSB8IFNFUFRFTUJFUiAyMDE0IHwgODc5IC0gODgwCgp8ICAgfCBSZXBsaWNhdGUgdHlwZSB8IFJlcGxpY2F0ZSBjYXRlZ29yeSReXHRleHR7YX0kIHwKfDotLS06fDotLS18Oi0tLTp8CnwgfCBDb2xvbmllcyB8CUIgfAp8IHwgU3RyYWlucyB8CUIgfAp8IEFuaW1hbCBzdHVkeSBzdWJqZWN0cyB8IENvaG91c2VkIGdyb3VwcyB8CUIgfAp8IHwgR2VuZGVyIHwgQiB8CnwgfCBJbmRpdmlkdWFscwl8IEIgfCAKfCB8IHwgfAp8IHwgT3JnYW5zIGZyb20gc2FjcmlmaWNlZCBhbmltYWxzIHwgQiB8CnwgfCBNZXRob2RzIGZvciBkaXNzb2NpYXRpbmcgY2VsbHMgZnJvbSB0aXNzdWUgfCBUIHwKfCBTYW1wbGUgcHJlcGFyYXRpb24gfCBEaXNzb2NpYXRpb24gcnVucyBmcm9tIGdpdmVuIHRpc3N1ZSBzYW1wbGUgfCBUIHwKfCB8IEluZGl2aWR1YWwgY2VsbHMgfCBCIHwKfCB8IHwgfAp8IHwgUk5BLXNlcSBsaWJyYXJ5IGNvbnN0cnVjdGlvbiB8IFQgfAp8IHwgUnVucyBmcm9tIHRoZSBsaWJyYXJ5IG9mIGEgZ2l2ZW4gY2VsbCB8CVQgfAp8IFNlcXVlbmNpbmcgfCBSZWFkcyBmcm9tIGRpZmZlcmVudCB0cmFuc2NyaXB0IG1vbGVjdWxlcyB8IFYkXlx0ZXh0e2J9JCB8CnwgfCBSZWFkcyB3aXRoIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllciAoVU1JKSBmcm9tIGEgZ2l2ZW4gdHJhbnNjcmlwdCBtb2xlY3VsZSB8CVQgfAoKKGEpIFJlcGxpY2F0ZXMgYXJlIGNhdGVnb3JpemVkIGFzIGJpb2xvZ2ljYWwgKEIpLCB0ZWNobmljYWwgKFQpIG9yIG9mIHZhcmlhYmxlIHR5cGUgKFYpLiAKKGIpIFNlcXVlbmNlIHJlYWRzIHNlcnZlIGRpdmVyc2UgcHVycG9zZXMgZGVwZW5kaW5nIG9uIHRoZSBhcHBsaWNhdGlvbiBhbmQgaG93IHJlYWRzIGFyZSB1c2VkIGluIGFuYWx5c2lzLgoKLS0tCgojIyBPcCB3ZWxrIG5pdmVhdSBtb2V0ZW4gd2UgcmVwbGljZXJlbj8KCi0gJFx0ZXh0e3Zhcn0oWCk9XHNpZ21hXjJfQStcc2lnbWFeMl9DK1xzaWdtYV4yX009XHNpZ21hXjJfe1RPVH0kCi0gJFx0ZXh0e3Zhcn0oXGJhcntYfSk9XGZyYWN7XHNpZ21hXjJfQX17bl9BfStcZnJhY3tcc2lnbWFeMl9DfXtuX0Egbl9DfSArIFxmcmFje1xzaWdtYV4yX019e25fQSBuX0Mgbl9NfSQKCiFbXShodHRwczovL21lZGlhLnNwcmluZ2VybmF0dXJlLmNvbS9mdWxsL3NwcmluZ2VyLXN0YXRpYy9pbWFnZS9hcnQlM0ExMC4xMDM4JTJGbm1ldGguMzA5MS9NZWRpYU9iamVjdHMvNDE1OTJfMjAxNF9BcnRpY2xlX0JGbm1ldGgzMDkxX0ZpZzFfSFRNTC5qcGcpCkZpZ3VyZSAxIE5BVFVSRSBNRVRIT0RTIHwgVk9MLjExIE5PLjkgfCBTRVBURU1CRVIgMjAxNCB8IDg3OSAtIDg4MAoKKGEpIFRocmVlIGxldmVscyBvZiByZXBsaWNhdGlvbiAodHdvIGJpb2xvZ2ljYWwsIG9uZSB0ZWNobmljYWwpIHdpdGggYW5pbWFsLCBjZWxsIGFuZCBtZWFzdXJlbWVudCByZXBsaWNhdGVzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggYSBtZWFuIGFjcm9zcyBhbmltYWxzIG9mIDEwIGFuZCByYXRpbyBvZiB2YXJpYW5jZXMgMToyOjAuNS4gU29saWQgZ3JlZW4gKGJpb2xvZ2ljYWwpIGFuZCBibHVlICh0ZWNobmljYWwpIGRvdHMgc2hvdyBob3cgYSBtZWFzdXJlbWVudCBvZiB0aGUgZXhwcmVzc2lvbiAoWCA9IDEyKSBzYW1wbGVzIGZyb20gYWxsIHRocmVlIHNvdXJjZXMgb2YgdmFyaWF0aW9uLiBEaXN0cmlidXRpb24gcy5kLiBpcyBzaG93biBhcyBob3Jpem9udGFsIGxpbmVzLiAKKGIpIEV4cHJlc3Npb24gdmFyaWFuY2UsIFZhcihYKSwgYW5kIHZhcmlhbmNlIG9mIGV4cHJlc3Npb24gbWVhbiwgVmFyKCRcYmFyIFgkKSwgY29tcHV0ZWQgYWNyb3NzIDEwLDAwMCBzaW11bGF0aW9ucyBvZiBuQW5Dbk0gPSA0OCBtZWFzdXJlbWVudHMgZm9yIHVuaXF1ZSBjb21iaW5hdGlvbnMgb2YgdGhlIG51bWJlciBvZiBhbmltYWxzIChuQSA9IDEgdG8gNDgpLCBjZWxscyBwZXIgYW5pbWFsIChuQyA9IDEgdG8gNDgpIGFuZCB0ZWNobmljYWwgcmVwbGljYXRlIG1lYXN1cmVtZW50cyBwZXIgY2VsbCAobk0gPSAxIGFuZCAzKS4gVGhlIHJhdGlvIG9mIFZhcihYKSBhbmQgVmFyKCRcYmFyIFgkKSBpcyB0aGUgZWZmZWN0aXZlIHNhbXBsZSBzaXplLCBuLCB3aGljaCBjb3JyZXNwb25kcyB0byB0aGUgZXF1aXZhbGVudCBudW1iZXIgb2Ygc3RhdGlzdGljYWxseSBpbmRlcGVuZGVudCBtZWFzdXJlbWVudHMuIEhvcml6b250YWwgZGFzaGVkIGxpbmVzIGNvcnJlc3BvbmQgdG8gYmlvbG9naWNhbCBhbmQgdG90YWwgdmFyaWF0aW9uLiBFcnJvciBiYXJzIG9uIFZhcihYKSBzaG93IHMuZC4gZnJvbSB0aGUgMTAsMDAwIHNpbXVsYXRlZCBzYW1wbGVzIChuTSA9IDEpLgoKLS0tCgpXYWFyIGxpZ3QgaW50ZXJlc3NlPyAKCi0gS2FyYWt0ZXJpc2VyZW4gdmFuIGJyb25uZW4gdmFuIHZhcmlhYmlsaXRlaXQgaW4gZXhwZXJpbWVudCA9PT4gdGVjaG5pc2NoZSBlbiBiaW9sb2dpc2NoZSByZXBlYXRzIHppam4gbm9kaWcKLSBWb29ybmFtZWxpamtlIGludGVyZXNzZSBpbiBzY2hhdHRlbiB2YW4gZWZmZWN0IHZhbiBiZWhhbmRlbGluZyA9PT4gZm9jdXMgb3AgYmlvbG9naXNjaGUgcmVwZWF0cwotIEV4dHJhIGJpb2xvZ2lzY2hlIHJlcGVhdHMgbHVtcGVuIGFsbGUgYnJvbm5lbiB2YW4gdmFyaWFiaWxpdGVpdDogYmlvbG9naXNjaGUgKyB0ZWNobmlzY2hlIHZhcmlhYmlsaXRlaXQuIAoKLSBHb2VkIGV4cGVyaW1lbnRlZWwgZGVzaWduIHZlcmVpc3QgbmEgZGVua2VuIG92ZXIgcmVwbGljYXRpZSAKCiAgMS4gSWRlbnRpZmljZWVyIG9uZGVyem9la3N2cmFnZW4gZGllIG1lbiB3ZW5zdCB0ZSBiZWFudHdvb3JkZW4uIAogIDIuIEJyZW5nIGJyb25uZW4gdmFuIHZhcmlhYmlsaXRlaXQgaW4ga2FhcnQgaW4gZWxrZSBzdGFwIHZhbiBoZXQgZXhwZXJpbWVudAogIDMuIExldCBvcCB2b29yIHBzZXVkb3JlcGxpY2F0aWUsIGJlb29nIHpvdmVlbCBtb2dlbGlqayBvbmFmaGFua2VsaWprZSByZXBlYXRzLiAKCi0tLSAKCiMgUG93ZXIsIHN0ZWVrcHJvZWZncm9vdHRlIGVuIGFuZGVyZSBkZXNpZ24gYXNwZWN0ZW4uIAoKUmVhZGluZyBtYXRlcmlhbHM6IApbTmF0dXJlIE1ldGhvZHMgKDIwMTMpLCAxMCgxMiksIDExMznigJMxMTQwXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjI3MzgucGRmKQoKIyMgSW50ZXJtZXp6byBsaW5lYXJlIHJlZ3Jlc3Npb24gaW4gbWF0cml4IHZvcm0gCgotIExpbmVhaXJlIHJlZ3Jlc3NpZSBpcyBlZW4gYmVsYW5ncmlqa2UgbWV0aG9kZSBpbiBzdGF0aXN0aXNjaGUgZGF0YSBhbmFseXNlLgoKIyMjIFNjYWxhaXJlIHZvcm0KCi0gU3RlbCBkYXQgbWVuIHZvb3IgZWxrZSBleHBlcmltZW50ZWxlIGVlbmhlaWQgJHAkIHByZWRpY3RvcmVuIG1lZXQgJFxtYXRoYmZ7eH09KHhfMSxcbGRvdHMseF9wKSQgZW4KLSBjb250aW51ZSByZXNwb25zICRZJAotIGhldCBsaW5lYXIgcmVncmVzc2llbW9kZWwga2FuIGRhbiB3b3JkZW4gZ2VzY2hyZXZlbiBhbHM6IApcWwpZPWYoXG1hdGhiZnt4fSkgK1xlcHNpbG9uPVxiZXRhXzArXHN1bVxsaW1pdHNfe2o9MX1ecCB4X2pcYmV0YV9qICsgXGVwc2lsb24KXF0KbWV0IGkuaS5kLiAkXGVwc2lsb25cc2ltIE4oMCxcc2lnbWFeMikkCgoKCiMjIyBWZWN0b3IvTWF0cml4IHZvcm0KCi0gJG4kIG9ic2VydmF0aWUgJChcbWF0aGJme3h9XzEseV8xKSBcbGRvdHMgKFxtYXRoYmZ7eH1fbix5X24pJCB3aXRoICRcbWF0aGJme3h9XzFeVD1bMSB4XzEgXGxkb3RzIHhfcF0kCi0gUmVncmVzc2llIGluIG1hdHJpeCBub3RhdGllClxbXG1hdGhiZntZfT1cbWF0aGJme1hcYmV0YX0gKyBcbWF0aGJme1xlcHNpbG9ufVxdCm1ldCAkXG1hdGhiZntZfT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9eV8xXFwgXHZkb3RzXFx5X25cZW5ke2FycmF5fVxyaWdodF0kLAokXG1hdGhiZntYfT1cbGVmdFtcYmVnaW57YXJyYXl9e2NjY2N9IDEmeF97MTF9JlxsZG90cyZ4X3sxcH1cXApcdmRvdHMmXHZkb3RzJiZcdmRvdHNcXAoxJnhfe24xfSZcbGRvdHMmeF97bnB9ClxlbmR7YXJyYXl9XHJpZ2h0XSQgb3IgJFxtYXRoYmZ7WH09XGxlZnRbXGJlZ2lue2FycmF5fXtjfSBcbWF0aGJme3h9XzFeVFxcXHZkb3RzXFxcbWF0aGJme3h9X25eVFxlbmR7YXJyYXl9XHJpZ2h0XSQsCiRcYm9sZHN5bWJvbHtcYmV0YX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfVxiZXRhXzBcXCBcdmRvdHNcXCBcYmV0YV9wXGVuZHthcnJheX1ccmlnaHRdJCBhbmQKJFxtYXRoYmZ7XGVwc2lsb259PVxsZWZ0W1xiZWdpbnthcnJheX17Y30gXGVwc2lsb25fMSBcXCBcdmRvdHMgXFwgXGVwc2lsb25fblxlbmR7YXJyYXl9XHJpZ2h0XSQKCi0tLQoKIyMga2xlaW5zdGUga3dhZHJhdGVuIHRlY2huaWVrL0xlYXN0IFNxdWFyZXMgKExTKQoKLSBNaW5pbWltYWxpc2VlciBkZSAgcmVzaWR1ZWxlIGt3YWRyYXRlbnNvbQpcYmVnaW57ZXFuYXJyYXkqfQpSU1MoXGJvbGRzeW1ib2x7XGJldGF9KSY9JlxzdW1cbGltaXRzX3tpPTF9Xm4gZV4yX2lcXAomPSZcc3VtXGxpbWl0c197aT0xfV5uIFxsZWZ0KHlfaS1cYmV0YV8wLVxzdW1cbGltaXRzX3tqPTF9XnAgeF97aWp9XGJldGFfalxyaWdodCleMgpcZW5ke2VxbmFycmF5Kn0KCi0gb2YgaW4gbWF0cml4IG5vdGF0aWUKCiQkClx0ZXh0e1JTU30oXGJvbGRzeW1ib2x7XGJldGF9KT0oXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pXlQoXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pCiQkCgokJFxyaWdodGFycm93IFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fT1cdGV4dHthcmdtaW59X1xiZXRhIFx0ZXh0eyBSU1N9KFxib2xkc3ltYm9se1xiZXRhfSkkJAoKLS0tCgojIyMgTWluaW1pbWFsaXNlZXIgUlNTClxbClxiZWdpbnthcnJheX17Y2NjfQpcZnJhY3tccGFydGlhbCBSU1N9e1xwYXJ0aWFsIFxib2xkc3ltYm9se1xiZXRhfX0mPSZcbWF0aGJmezB9XFxcXApcZnJhY3soXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pXlQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9KX17XHBhcnRpYWwgXGJvbGRzeW1ib2x7XGJldGF9fSY9JlxtYXRoYmZ7MH1cXFxcCi0yXG1hdGhiZntYfV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfSkmPSZcbWF0aGJmezB9XFxcXApcbWF0aGJme1h9XlRcbWF0aGJme1hcYmV0YX0mPSZcbWF0aGJme1h9XlRcbWF0aGJme1l9XFxcXApcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0mPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX0KXGVuZHthcnJheX0KXF0KCi0tLQoKIyMgVmFyaWFudGllIHNjaGF0dGVyPwpcWwpcYmVnaW57YXJyYXl9e2NjbH0KXGhhdHtcYm9sZHN5bWJvbHtcU2lnbWF9fV97XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fQomPSZcdGV4dHt2YXJ9XGxlZnRbKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1l9XHJpZ2h0XVxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcdGV4dHt2YXJ9XGxlZnRbXG1hdGhiZntZfVxyaWdodF1cbWF0aGJme1h9KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UKFxtYXRoYmZ7SX1cc2lnbWFeMilcbWF0aGJme1h9KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX0KXFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7SX1ccXVhZFxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxzaWdtYV4yXFxcXAolXGhhdHtcYm9sZG1hdGh7XFNpZ21hfX1fe1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX0mPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfVxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjIKXGVuZHthcnJheX0KXF0KCkRlIG9uemVrZXJoZWlkIG9wIG1vZGVsIHBhcmFtZXRlcnMgaGFuZ3QgZHVzIGFmIHZhbiBkZSByZXNpZHVlbGUgdmFyaWFiaWxpdGVpdCBlbiBoZXQgZGUgcHJvZWZvcHpldCEKCi0gSG9lIGdyb3RlciAkXG1hdGhiZntYfV5UXG1hdGhiZntYfSQgaG9lIG1lZXIgaW5mb3JtYXRpZSBoZXQgZXhwZXJpbWVudCB6YWwgaGViYmVuIG92ZXIgZGUgbW9kZWwgcGFyYW1ldGVycyBlbiBob2Uga2xlaW5lciBodW4gdmFyaWFudGllIGVuIHN0YW5kYWFyZCBlcnJvcnMhIAoKLSBGYWN0b3Jpw6tsZSBkZXNpZ25zPyAKLSBEZXNpZ25zIHdpdGggY29udGludWUgcHJlZGljdG9yZW4/IAoKLS0tCgpEZSBlZmZlY3Rncm9vdHRlIHZhbiBpbnRlcmVzc2UgaXMgdHlwaXNjaCBlZW4gbGluZWFpcmUgY29tYmluYXRpZSB2YW4gZGUgbW9kZWwgcGFyYW1ldGVycywgIAokJApsXzAgXHRpbWVzIFxiZXRhXzAgKyBsXzEgXHRpbWVzIFxiZXRhXzEgKyAuLi4gKyBsX3twLTF9IFx0aW1lcyBcYmV0YV97cC0xfSA9IFxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfQokJCAKCkRlIG51bGh5cG90aGVzZSB2YW4gb256ZSB0ZXN0IGthbiBkYW4gd29yZGVuIGdlc2NocmV2ZW4gYWxzIAoKCiQkCkhfMDogXG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9ID0gMCAKJCQgCgp2cyBkZSBhbHRlcm5hdGlldmUgaHlwb3RoZXNlIAoKJCQKSF8xOiBcbWF0aGJme0x9XlRcYm9sZHN5bWJvbHtcYmV0YX0gXG5lcSAwIAokJAoKRW4gaGV0IGJld2lqcyBpbiBoZXQgZXhwZXJpbWVudCB0ZWdlbiAkSF8wJCBrYW4gd29yZGVuIGdla3dhbnRpZmljZWVyZCBtZXQgZGUgdC10ZXN0IHN0YXRpc3RpZWs6IAoKJCQKdD1cZnJhY3tcbWF0aGJme0x9XlRcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gLSAwfXtcdGV4dHtzZX1fe1xtYXRoYmZ7TH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX19CiQkCmRpZSBlZW4gdC1kaXN0cmlidXRpZSB2b2xndCBtZXQgbi1wIHZyaWpoZWlkc2dyYWRlbiBvbmRlciBkZSBudWwgaHlwb3RoZXNlIGFscyBhbGxlIGFhbm5hbWVzIHZhbiBoZXQgbW9kZWwgZ2VsZGlnIHppam4uCgpEZSBrcmFjaHQgdmFuIGRlIHRvZXRzIGlzIGRhbgoKJCRQKHAgPCAwLjA1IHwgSF8xKSQkCmVuIGhhbmd0IGFmIHZhbiAKCi0gZGUgd2Vya2VsaWprZSBlZmZlY3Rncm9vdHRlIGluIGRlIHBvcHVsYXRpZSAkXG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9JC4KLSBIZXQgYWFudGFsIG9ic2VydmF0aWVzOiBTRSBlbiBkZiB2YW4gdC10ZXN0LgotIEtldXplIHZhbiBkZSBkZXNpZ25wdW50ZW4KLSBLZXV6ZSB2YW4gc2lnbmlmaWNhbnRpZS1uaXZlYXUgJFxhbHBoYSQuIAoKV2Uga3VubmVuIGRpdCBldmFsdWVyZW4gYWFuIGRlIGhhbmQgdmFuIHNpbXVsYXRpZXMuCgojIyBNdWlzIHZvb3JiZWVsZAoKLSBJbiAyMDIxIHB1YmxpY2VlcmRlbiBDaG9hIGV0IGFsLiBkYXQgY3l0b2tpbmUgVGh5bWljIHN0cm9tYWwgbHltcGhvcG9pZXRpbgooVFNMUCkgdmV0dmVybGllcyBpbmR1Y2VlcnQgYmlqIG11aXplbiBkaWUgZWVuIHZldCBkaWVldCBrcmlqZ2VuIGRvb3IgZGUgc2VjcmV0aWUgdmFuIHRhbGcuIFtbaHRtbF0oaHR0cHM6Ly93d3cuc2NpZW5jZS5vcmcvZG9pL2Z1bGwvMTAuMTEyNi9zY2llbmNlLmFiZDI4OTMpXSBbW1BERl0oaHR0cHM6Ly93d3cuc2NpZW5jZS5vcmcvZG9pL3BkZi8xMC4xMTI2L3NjaWVuY2UuYWJkMjg5MyldCgotIFN0ZWwgZGF0IGplIGVlbiBnZWxpamthYXJkaWcgZXhwZXJpbWVudCBvcCB3aWwgemV0dGVuIG9tIG5hIHRlIGdhYW4gb2YgY3l0b2tpbmUgaW50ZXJsZXVraW4gMjUgKElMKSBvb2sgZWVuIGVmZmVjdCBoZWVmdCBvcCBoZXQgZ2V3aWNodCB2YW4gbXVpemVuLiAKCi0gSmUgcGxhbnQgZWVuIHN0dWRpZSBtZXQgZWVuIGNvbnRyb2xlIGdyb2VwIG11aXplbiBkaWUgZWVuIGhvb2cgdmV0IGRpZWV0IGtyaWpnZW4gIChIRkQpIGVuIGVlbiBiZWhhbmRlbGluZ3Nncm9lcCBkaWUgZWVuIEhGRCBkaWVldCBtZXQgSUwga3Jpamd0LiAKCi0gV2F0IGlzIGRlIHZlcmVpc3RlIHN0ZWVrcHJvZWZncm9vdHRlIG9tIGVlbiBiZXBhYWxkIGVmZmVjdCBvcCB0ZSBwaWtrZW4gdmFuIGRlIGJlaGFuZGVsaW5nPyAKCgojIyMgSG9lIHphbCBqZSBkZSBkYXRhIHZhbiBoZXQgZXhwZXJpbWVudCBhbmFseXNlcmVuPyAKCgotLS0KCgoKLSAkSF8wJDogRXIgaXMgZ2VlbiB2ZXJzY2hpbCBpbiBoZXQgZ2V3aWNodCB2YW4gbXVpemVuIGRpZSBlZW4gZGllZXQgbWV0IElMIGtyaWpnZW4gZW4gbXVpemVuIGRpZSBoZXQgY29udHJvbGUgZGllZXQga3Jpamdlbi4KLSAkSF8xJDogSGV0IGdld2ljaHQgaXMgZ2VtaWRkZWxkZSB2ZXJzY2hpbGxlbmQgdHVzc2VuIG11aXplbiBkaWUgaGV0IElMIGRpZWV0IGtyaWpnZW4gZW4gbXVpemVuIGRpZSBoZXQgY29udHJvbGUgZGllZXQga3Jpamdlbi4KCi0gVHdvIHNhbXBsZSB0LXRlc3Qgb2YgZWVuIHQtdGVzdCBvcCBkZSBoZWxsaW5nIHZhbiBoZXQgbGluZWFpcmUgbW9kZWwgbWV0IDEgZHVtbXkgdmFyaWFiZWxlCgokJCBZX2kgPSBcYmV0YV8wICsgXGJldGFfMSBYX1x0ZXh0e2lJTH0gKyBcZXBzaWxvbl9pJCQgCgokJApcdGV4dHttZXQgfQpYX1x0ZXh0e2lJTH09XGJlZ2lue2Nhc2VzfVhfe2lJTH09MCAmIFx0ZXh0e0hGRH1cXFhfe2lJTH09MSAmIFx0ZXh0e0hGRCArIElMfVxlbmR7Y2FzZXN9LgokJAoKLSBHZXNjaGF0dGUgZWZmZWN0Z3Jvb3R0ZT8KCiQkXGhhdFxkZWx0YSA9IFxiYXIgWF97SUx9IC0gXGJhciBYX3tjfSA9IFxoYXQgXGJldGFfMSQkCgotIFRlc3Qgc3RhdGlzdGllawokJApUID0gXGZyYWN7XGJhcntYfV97SUx9LVxiYXJ7WH1fe2N9fXtTRF9cdGV4dHtwb29sZWR9IFx0aW1lcyBcc3FydHtcZnJhY3sxfXtuXzF9ICsgXGZyYWN7MX17bl8yfX19ID0gXGZyYWN7XGhhdFxiZXRhXzF9e1x0ZXh0e1NFfV97XGhhdFxiZXRhXzF9fQokJAotICRcaGF0IFxiZXRhXzEkIGlzIGVlbiBvbnZlcnRla2VuZGUgc2NoYXR0ZXIgdmFuIGhldCB3ZXJrZWxpamsgZ2V3aWNodHN2ZXJzY2hpbCAoJFxkZWx0YSA9XG11X3tJTH0tXG11X3tjfSA9IFxiZXRhXzEkKSBkYXQgem91IHZvb3Jrb21lbiBpbiBkZSBwb3B1bGF0aWUgdmFuIG11aXplbiBnZXZvZWQgbWV0IEhGRCB2cyBtdWl6ZW4gZ2V2b2VkIG1ldCBIRkQgKyBJTC4gIAoKIyMjIFBvd2VyPyAKCiQkClBbcCA8IFxhbHBoYSBcdmVydCBcYmV0YV8xIFxuZXEgMF0KJCQKCgpEZSBrcmFjaHQgKHBvd2VyKSB6YWwgZHVzIGFmaGFuZ2VuIHZhbiAKCi0gSGV0IHdlcmtlbGlqa2UgZ2VtaWRkZWxkZSBnZXdpY2h0c3ZlcnNjaGlsICRcYmV0YV8xJCBpbiBkZSBwb3B1bGF0aWUuCi0gRGUgdmFyaWFiaWxpdGVpdCB2YW4gZGUgZ2V3aWNodHNtZXRpbmdlbgotIGhldCBzaWduaWZpY2FudGllLW5pdmVhdSAkXGFscGhhJAotIHN0ZWVrcHJvZWZncm9vdHRlICRuX3tJTH0kIGVuICRuX2MkIGluIGJlaWRlIGdyb2VwZW4gCgoKV2Uga3VubmVuIGRlIHBvd2VyIHNjaGF0dGVuIGFscyAKCjEuIHZvbGRhYW4gaXMgYWFuIGRlICBhYW5uYW1lbnMgdmFuIGhldCBtb2RlbDogZ2V3aWNodCBpbiBiZWlkZSBncm9lcGVuIGlzIG5vcm1hYWwgdmVyZGVlbGQgbWV0IGdlbGlqa2UgdmFyaWFudGllCgplbiBhbHMgd2UgZGUgCgoyLiBzdGFuZGFhcmQgZGV2aWF0aWUga2VubmVuIHZhbiBnZXdpY2h0c21ldGluZ2VuIHJvbmQgaHVuIGdyb2Vwc2dlbWlkZGVsZGUgdm9vciBtdWl6ZW4gZGllIGVlbiBIRkQgZGlldCBrcmlqZ2VuCgozLiBkZSBlY2h0ZSBlZmZlY3Rncm9vdHRlIGluIGRlIHBvcHVsYXRpZQo0LiAkbl8xJCBlbiAkbl8yJCAKCiMjIyBHZWJydWlrIGRhdGEgdWl0IHZvcmlnIGV4cGVyaW1lbnQgb20gaW56aWNodCB0ZSBrcmlqZ2VuIGluIGRlIGdld2ljaHRzZGF0YSB2YW4gbXVpemVuCgpTdGVsIGRhdCB3ZSB0b2VnYW5nIGhlYmJlbiB0b3QgZGF0YSB2YW4gZWVuIHZvcmlnIGV4cGVyaW1lbnQgKGhpZXIgZGF0IHZhbiBLYXJlbiBTdmVuc29uIGVuIERhbiBHYXR0aSwgZ2VmaW5hbmNpZXJkIGRvb3IgUDUwIEdNMDcwNjgzIGVuIGJlc2NoaWtiYWFyIG9wIFtQSDUyNXhdKGh0dHA6Ly9nZW5vbWljc2NsYXNzLmdpdGh1Yi5pby9ib29rL3BhZ2VzL3JhbmRvbV92YXJpYWJsZXMuaHRtbCkpCgpgYGB7cn0KbWljZSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dlbm9taWNzY2xhc3MvZGFnZGF0YS9tYXN0ZXIvaW5zdC9leHRkYXRhL2ZlbWFsZU1pY2VXZWlnaHRzLmNzdiIpCgptaWNlICU+JSAKICBnZ3Bsb3QoYWVzKHg9RGlldCx5PUJvZHl3ZWlnaHQpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9RkFMU0UpICsKICBnZW9tX2ppdHRlcigpCgptaWNlICU+JSAKICBnZ3Bsb3QoYWVzKHNhbXBsZT1Cb2R5d2VpZ2h0KSkgKwogIGdlb21fcXEoKSArIAogIGdlb21fcXFfbGluZSgpICsKICBmYWNldF93cmFwKH5EaWV0KQpgYGAKCmBgYHtyfQptaWNlIDwtIG1pY2UgJT4lIG11dGF0ZShEaWV0ID0gYXMuZmFjdG9yKERpZXQpKQptaWNlU3VtIDwtIG1pY2UgJT4lIAogIGdyb3VwX2J5KERpZXQpICAlPiUKICBzdW1tYXJpemVfYXQoIkJvZHl3ZWlnaHQiLGxpc3QobWVhbj1+bWVhbiguLG5hLnJtPVRSVUUpLAogICAgICAgICAgICAgICAgICAgIHNkPX5zZCguLG5hLnJtPVRSVUUpLAogICAgICAgICAgICAgICAgICAgIG49ZnVuY3Rpb24oeCkgeCU+JWlzLm5hJT4lYCFgJT4lc3VtKSkgJT4lCiAgbXV0YXRlKHNlPXNkL3NxcnQobikpCm1pY2VTdW0gCmBgYAoKSW4gaGV0IGV4cGVyaW1lbnQgd2VyZGVuIHR3ZWUgZGnDq3RlbiBnZWJydWlrdDogCgotIGdld29vbiBkaWV0IG8uYi52LiBncmFuZW4gKENob3cpIAotIEhlZWwgdmV0IGRpZWV0IChoaWdoIGZhdDogaGYpCgpXZSBrdW5uZW4gZGUgZGF0YSB2YW4gZGUgaGYgbXVpemVuIGdlYnJ1aWtlbiBhbHMgaW5wdXQgdm9vciBvbnplIHBvd2VyIGFuYWx5c2UuIAoKLSBEZSBkYXRhIHZhbiBoZiBtdWl6ZW4gbGlqa2VuIG5vcm1hYWwgdmVyZGVlbGQgdGUgemlqbi4gIAotIEhldCBnZW1pZGRlbGRlIGdld2ljaHQgdmFuIGVlbiBoZiBtdWlzIGlzIGByIG1pY2VTdW0gJT4lIGZpbHRlcihEaWV0ID09ICJoZiIpICU+JSBwdWxsKCJtZWFuIikgJT4lIHJvdW5kKC4sMSlgZyAKLSBEZSBzdGFuZGFhcmQgZGV2aWF0aWUgKFNEKSB2YW4gaGV0IGdld2ljaHQgdmFuIGhmIG11aXplbiBpcyBgciBtaWNlU3VtICU+JSBmaWx0ZXIoRGlldCA9PSAiaGYiKSAlPiUgcHVsbCgic2QiKSAlPiUgcm91bmQoLiwxKWBnIAoKRWZmZWN0Z3Jvb3RlPyAKCi0gRGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNlIGlzIGNvbXBsZXguIAotIE9tdmF0IGFsbGUgbW9nZWxpamtlIGVmZmVjdGVuISAKLSBPbSBlZW4gcG93ZXIgYW5hbHlzZSB0ZSBkb2VuIGtpZXplbiB3ZSBkYWFyb20gZWVuIG1pbmltYWxlIGVmZmVjdGdyb290dGUgZGllIHdlIHdlbnNlbiBvcCB0ZSBwaWtrZW4gaW4gaGV0IG5pZXV3ZSBleHBlcmltZW50LiAKLSBWZXJvbmRlcnN0ZWwgZGF0IHdlIGVlbiBnZXdpY2h0c3ZlcnNjaGlsIHdpbGxlbiBvcHBpa2tlbiB2YW4gbWluaW1hYWwgMTAlLiAKCmBgYHtyfQpkZWx0YSA8LSAtIHJvdW5kKG1pY2VTdW0kbWVhblsyXSAqLjEsMSkKZGVsdGEKYGBgCgpNZXJrIG9wIGRhdCBoZXQgZ2VtaWRkZWxkIGdld2ljaHQgZGFuIGRpY2h0IGtvbXQgYmlqIGRhdCB2YW4gbXVpemVuIGluIGhldCBwaWxvb3RleHBlcmltZW50IGRpZSB3ZXJkZW4gZ2V2b2VkIG1ldCBoZXQgbm9ybWFsZSBkaWVldC4gCgpXZSBrdW5uZW4gbnUgZWVuIHNpbXVsYXRpZSBzdHVkaWUgb3B6ZXR0ZW4gb20gZGUgcG93ZXIgdGUgYmVyZWtlbmVuIHZvb3IgZWVuIGV4cGVyaW1lbnQgbWV0IDggbXVpemVuIGluIGVsa2UgZ3JvZXAuIAoKIyMjIyBFw6luIHNpbXVsYXRpZQoKYGBge3J9CnNldC5zZWVkKDE0MjMpCm4xIDwtIG4yIDwtIDgKYjAgPC0gcm91bmQobWljZVN1bSRtZWFuWzJdLDEpCmIxIDwtIC0gZGVsdGEKc2QgPC0gcm91bmQobWljZVN1bSRzZFsyXSwxKQphbHBoYSA8LSAwLjA1Cgp4IDwtIHJlcCgwOjEsYyhuMSxuMikpCnkgPC0gYjAgKyBiMSAqIHggKyBybm9ybShuMStuMiwwLCBzZCA9IHNkKQpmaXQgPC0gbG0oeX54KQpiaGF0IDwtIGZpdCRjb2VmIApzdGF0IDwtIHN1bW1hcnkoZml0KQpzdW1tYXJ5KGZpdCkkY29lZlsyLF0KYGBgCgpWb29yIGhldCBnZXNpbXVsZWVyZGUgZXhwZXJpbWVudCBrb25kZW4gd2UgaGV0IGVmZmVjdCB2YW4gZGUgYmVoYW5kZWxpbmcgYHIgaWZlbHNlKHN1bW1hcnkoZml0KSRjb2VmWzIsIlByKD58dHwpIl0gPCBhbHBoYSwiIiwibmlldCIpYCBvcHBpa2tlbiBiaWogaGV0ICAkXGFscGhhJD1gciBhbHBoYWAgKHAgPSBgciByb3VuZChzdW1tYXJ5KGZpdCkkY29lZlsyLCJQcig+fHR8KSJdLDIpYCkgc2lnbmlmaWNhbnRpZS1uaXZlYXUhIAoKLSBXZSBtb2V0ZW4gaGV0IGV4cGVyaW1lbnQgaGVyaGFsZW4gb20gZGUgcG93ZXIgdGUgc2NoYXR0ZW4uIAotIFdlIHp1bGxlbiBkZSBzaW11bGF0aWUgZGFhcm9tIGNvZGVyZW4gaW4gZWVuIGZ1bmN0aWUgZGllIHdlIGt1bm5lbiBoZXJnZWJydWlrZW4uIAoKIyMjIyBTaW11bGF0aWUgdmFuIGhlcmhhYWxkZSBleHBlcmltZW50ZW4gKHJlcGVhdGVkIGV4cGVyaW1lbnRzKQoKYGBge3J9CmxpYnJhcnkobXVsdGNvbXApCm4xIDwtIG4yIDwtIDgKCmIwIDwtIHJvdW5kKG1pY2VTdW0kbWVhblsyXSwxKQpiMSA8LSAtIGRlbHRhCgpzZCA8LSByb3VuZChtaWNlU3VtJHNkWzJdLDEpCnByZWRpY3RvckRhdGEgPC0gZGF0YS5mcmFtZShEaWV0ID0gcmVwKGMoImMiLCJoZiIpLGMobjEsbjIpKSAlPiUgYXMuZmFjdG9yKQoKYWxwaGEgPC0gMC4wNQoKc2ltTG0gPC0gZnVuY3Rpb24oZm9ybSwgZGF0YSwgYmV0YXMsIHNkLCBjb250cmFzdHMsIHNpbUluZGV4ID0gTkEpCnsKICBkYXRhU2ltIDwtIGRhdGEKICBYIDwtIG1vZGVsLm1hdHJpeChmb3JtLGRhdGFTaW0pCiAgZGF0YVNpbSR5U2ltIDwtIFglKiViZXRhcyArIHJub3JtKG5yb3coZGF0YVNpbSksMCxzZCkKICBmb3JtIDwtIGZvcm11bGEocGFzdGUoInlTaW0gfiIsZm9ybVtbMl1dKSkKICBmaXRTaW0gPC0gbG0oZm9ybSxkYXRhU2ltKQogIG1jcCA8LSBnbGh0KGZpdFNpbSxsaW5mY3QgPSBjb250cmFzdHMpCiAgcmV0dXJuKHN1bW1hcnkobWNwKSR0ZXN0W2MoImNvZWZmaWNpZW50cyIsInNpZ21hIiwidHN0YXQiLCJwdmFsdWVzIildJT4lIHVubGlzdCkKfQoKc2ltTG0oCiAgZm9ybSA9IH5EaWV0LAogIGRhdGEgPSBwcmVkaWN0b3JEYXRhLAogIGJldGFzID0gYyhiMCxiMSksCiAgc2QgPSBzZCwKICBjb250cmFzdHMgPSAiRGlldGhmID0gMCIpCmBgYAoKV2UgaGViYmVuIG51IGVlbiBnZW5lcmllayBmdW5jdGllIHdhYXJtZWUgd2UgZGF0YSBrdW5uZW4gc2ltdWxlcmVuIGRpZSBub3JtYWFsIHZlcmRlZWxkIHppam4gdm9vciBlbGsgbW9nZWxpamsgZGVzaWduIGRpZSB3ZSBtZXQgaGV0IGxpbmVhaXIgbW9kZWwga3VubmVuIGFuYWx5c2VyZW4uIAoKRGUgZnVuY3RpZSBoZWVmdCB2b2xnZW5kZSBhcmd1bWVudGVuOiAKCi0gYGZvcm1gOiBPbmUgc2lkZWQgZm9ybXVsYSBpbmNsdWRpbmcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwgCi0gYGRhdGFgOiBEYXRhIGZyYW1lIHdpdGggdGhlIHByZWRpY3RvciB2YWx1ZXMgZm9yIHRoZSBkZXNpZ24KLSBgYmV0YXNgOiBBIHZlY3RvciB3aXRoIHZhbHVlcyBmb3IgYWxsIG1lYW4gbW9kZWwgcGFyYW1ldGVycwotIGBzZGA6IFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGVycm9yIAotIGBjb250cmFzdHNgOiBhIHNjYWxhciBvciBhIHZlY3RvciB3aXRoIHRoZSBudWxsIGh5cG90aGVzZXMgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIGFzc2Vzcy4gCi0gYHNpbUluZGV4YDogYW4gYXJiaXRyYXJ5IGFyZ3VtZW50IHRoYXQgaXMgbm90IHVzZWQgYnkgdGhlICBmdW5jdGlvbiBidXQgdGhhdCB3aWxsIGFsbG93IGl0IHRvIGJlIHVzZWQgaW4gYW4gc2FwcGx5IGxvb3AgdGhhdCBydW5zIGZyb20gMSB1cCB0byB0aGUgbnVtYmVyIG9mIHNpbXVsYXRpb25zLiAKClNpbXVsZWVyIG5TaW0gPSAxMDAwIGhlcmhhYWxkZSBleHBlcmltZW50ZW46IAoKYGBge3J9CnNldC5zZWVkKDE0MjUpCm5TaW0gPC0gMTAwMAoKc2ltUmVzdWx0cyAgPC0gdChzYXBwbHkoMTpuU2ltLHNpbUxtLGZvcm0gPSB+RGlldCxkYXRhID0gcHJlZGljdG9yRGF0YSxiZXRhcyA9IGMoYjAsYjEpLHNkID0gc2QsY29udHJhc3RzID0gIkRpZXRoZiA9IDAiKSkKcG93ZXIgPC0gbWVhbihzaW1SZXN1bHRzWyw0XSA8IGFscGhhKQpwb3dlcgpgYGAKCldlIGhlYmJlbiBlZW4gcG93ZXIgdmFuIGByIHBvd2VyKjEwMGAlIG9tIGhldCBiZWhhbmRlbGluZ3NlZmZlY3Qgb3AgdGUgcGlra2VuIG1ldCA4IGJpb2xvZ2lzY2hlIGhlcmhhbGluZ2VuIGluIGVsa2UgYmVoYW5kZWxpbmdzZ3JvZXAuIAoKTWVyayBvcCBkYXQgZGUgcG93ZXIgdmFuIGVlbiBleHBlcmltZW50IG9vayBoZXQgaG9vZ3N0ZSBpcyBhbHMgaGV0IGFhbnRhbCBvYnNlcnZhdGllcyBnZWJhbGFuY2VlcmQgaXMsIG0uYS53LiAkbl8xPW5fMiQuIAoKIyMjIyBQb3dlciB2b29yIHZlcnNjaGlsbGVuZGUgc3RlZWtwcm9lZmdyb290dGVzIAoKV2Uga3VubmVuIGRlIHBvd2VyIG51IGJlcmVrZW5lbiB2b29yIG1lZXJkZXJlIHN0ZWVrcHJvZWZncm9vdGVzLiAKCmBgYHtyfQpwb3dlciA8LSBkYXRhLmZyYW1lKG49YygzLDUsMTAsMjUsNTAsNzUsMTAwKSxwb3dlcj1OQSkKCmZvciAoaSBpbiAxOm5yb3cocG93ZXIpKQp7CiAgbjEgPC0gbjIgPC0gcG93ZXIkbltpXQogIHByZWRpY3RvckRhdGEgPC0gZGF0YS5mcmFtZShEaWV0ID0gcmVwKGMoImMiLCJoZiIpLGMobjEsbjIpKSAlPiUgYXMuZmFjdG9yKQogIHNpbVJlc3VsdHMgIDwtIHQoc2FwcGx5KDE6blNpbSxzaW1MbSxmb3JtID0gfkRpZXQsZGF0YSA9IHByZWRpY3RvckRhdGEsYmV0YXMgPSBjKGIwLGIxKSxzZCA9IHNkLGNvbnRyYXN0cyA9ICJEaWV0aGYgPSAwIikpCiAgcG93ZXIkcG93ZXJbaV0gPC0gbWVhbihzaW1SZXN1bHRzWywicHZhbHVlcyJdIDwgYWxwaGEpCn0KcG93ZXIKYGBgCgpgYGB7cn0KcG93ZXIgJT4lIAogIGdncGxvdChhZXMoeD1uLHk9cG93ZXIpKSArCiAgZ2VvbV9saW5lKCkKYGBgCgojIyMjIEJlc3R1ZGVlciBwb3dlciBpbiBmdW5jdGllIHZhbiBzdGVla3Byb2VmZ3Jvb3R0ZSBlbiBlZmZlY3Rncm9vdHRlIAoKTWVyayBvcCBkYXQgCgotIGhldCB0ZWtlbiB2YW4gZGVsdGEgYXJiaXRyYWlyIGlzIG9tZGF0IHdlIHR3ZWV6aWpkaWcgdGVzdGVuLgotIGhldCBpbnRlcmNlcHQgYXJiaXRyYWlyIGlzIG9tZGF0IHdlIGFsbGVlbiB0ZXN0ZW4gb3AgJFxiZXRhXzEkCi0gV2UgbmVtZW4gZGFhcm9tIHR5cGlzY2ggJFxiZXRhXzAgPSAwJAoKYGBge3J9Cm5TaW0gPC0gMTAwMApiMCA8LSAwCmRlbHRhcyA8LSBjKDEsMiwzLDUsMTApCnNkIDwtIHJvdW5kKG1pY2VTdW0kc2RbMl0sMSkKbnMgPC0gIGMoMyw1LDEwLDIwLDI1LDUwLDc1LDEwMCkgIAoKcG93ZXIgPC0gZGF0YS5mcmFtZShiMT1yZXAoZGVsdGFzLGVhY2g9bGVuZ3RoKG5zKSksCiAgICAgICAgICAgICAgICAgICAgbj1yZXAobnMsbGVuZ3RoKGRlbHRhcykpLAogICAgICAgICAgICAgICAgICAgIHBvd2VyPU5BKQoKZm9yIChpIGluIDE6bnJvdyhwb3dlcikpCnsKICBiMSA8LXBvd2VyJGIxW2ldCiAgbjEgPC0gbjIgPC0gIHBvd2VyJG5baV0KICBwcmVkaWN0b3JEYXRhIDwtIGRhdGEuZnJhbWUoRGlldCA9IHJlcChjKCJjIiwiaGYiKSxjKG4xLG4yKSkgJT4lIGFzLmZhY3RvcikKICBzaW1SZXN1bHRzICA8LSB0KHNhcHBseSgxOm5TaW0sc2ltTG0sZm9ybSA9IH5EaWV0LGRhdGEgPSBwcmVkaWN0b3JEYXRhLGJldGFzID0gYyhiMCxiMSksc2QgPSBzZCxjb250cmFzdHMgPSAiRGlldGhmID0gMCIpKQogIHBvd2VyJHBvd2VyW2ldIDwtIG1lYW4oc2ltUmVzdWx0c1ssInB2YWx1ZXMiXSA8IGFscGhhKQp9CmBgYAoKYGBge3J9CnBvd2VyICU+JSAKICBnZ3Bsb3QoYWVzKHg9bix5PXBvd2VyLGNvbD1iMSU+JWFzLmZhY3RvcikpICsKICBnZW9tX2xpbmUoKQpgYGAKCk1lcmsgb3AgZGF0IGRlIHBvd2VyIGN1cnZlcyB3YXQgc2Nob2trZXJpZyB6aWpuIG9tZGF0IHdlIG1hYXIgZWVuIGJlcGVya3QgYWFudGFsIHN0ZWVrcHJvZWZncm9vdHRlcyBlbiBlZW4gYmVwZXJrdCBhYW50YWwgc2ltdWxhdGllcyB1aXR2b2VyZGVuLiAKVm9vciBkZSBsYWdlIHBvd2VycyBpcyBoZXQgYWFudGFsIHNpbXVsYXRpZXMgemVrZXIgb252b2xkb2VuZGUgb20gaGVlbCBiZXRyb3V3YmFyZSBzY2hhdHRpbmdlbiB2YW4gZGUgcG93ZXIgdGUgYmVrb21lbi4gCgojIE9wbWVya2luZ2VuCgotIERlIGNvZGUga2FuIGVlbnZvdWRpZyBhYW5nZXBhc3Qgd29yZGVuIG5hYXIgYW5kZXJlIGRlc2lnbnMgKHppZSBvZWZlbmluZ2VuKQogICAgCiAgICAtIHByZWRpY3RvciBkYXRhIAogICAgLSBmb3JtdWxhCiAgICAtIGNvbnRyYXN0CgotIERlIHNpbXVsYXRpZXMga3VubmVuIGxhbmcgZHVyZW4gd2FubmVlciBqZSB2ZWVsIHNjZW5hcmlvJ3MgZXZhbHVlZXJ0LiAgCiAgICAKICAgIC0gTWVlciBlZmZpY2nDq250ZSBjb2RlIG1ldCBtYXRyaWNlcwogICAgLSBWb29yIHR3byBncm91cCBjb21wYXJpc29uIGJlc3RhYXQgZWVuIGFuYWx5dGlzY2hlIG9wbG9zc2luZy4gCiAgICAKCgoKIyMgIE1lZXIgZWZmaWNpw6tudGUgY29kZSBtZXQgbWF0cmljZXMKCkNvZGUgaXMgdmVlbCBzbmVsbGVyLiBXZSBzaW11bGFsZXJlbiBudSAyMCBrZWVyIG1lZXIgZXhwZXJpbWVudGVuIGluIGVlbiB2ZWVsIGtvcnRlcmUgdGlqZCEgCgpgYGB7cn0KblNpbSA8LSAyMDAwMApiMCA8LSAwCnNkIDwtIHJvdW5kKG1pY2VTdW0kc2RbMl0sMSkKbnMgPC0gIGMoMyw1LDEwLDIwLDI1LDUwLDc1LDEwMCkgIApkZWx0YXMgPC0gYygxLDIsMyw1LDEwKQoKCnBvd2VyRmFzdCA8LSBtYXRyaXgoTkEsbnJvdz1sZW5ndGgobnMpKmxlbmd0aChkZWx0YXMpLG5jb2w9MykgJT4lIGFzLmRhdGEuZnJhbWUKbmFtZXMocG93ZXJGYXN0KSA8LSBjKCJiMSIsIm4iLCJwb3dlciIpCgppIDwtIDAKCmZvciAobiBpbiBucykKewogIG4xIDwtIG4yIDwtICBuCiAgCiAgIyMjIFNpbXVsYXRpb24KICBwcmVkaWN0b3JEYXRhIDwtIGRhdGEuZnJhbWUoRGlldCA9IHJlcChjKCJjIiwiaGYiKSxjKG4xLG4yKSkgJT4lIGFzLmZhY3RvcikKICBkZXNpZ24gPC0gbW9kZWwubWF0cml4KH5EaWV0LHByZWRpY3RvckRhdGEpCiAgTCA8LSBsaW1tYTo6bWFrZUNvbnRyYXN0cygiRGlldGhmIixsZXZlbHMgPSBjb2xuYW1lcyhkZXNpZ24pKQoKICAKICBmb3IgKGIxIGluIGRlbHRhcykKICB7CiAgICB5U2ltIDwtIHJub3JtKG5yb3cocHJlZGljdG9yRGF0YSkqblNpbSxzZD1zZCkKICAgIGRpbSh5U2ltKSA8LWMobnJvdyhwcmVkaWN0b3JEYXRhKSxuU2ltKQogICAgeVNpbSA8LSB5U2ltICsgYyhkZXNpZ24gJSolYyhiMCxiMSkpCiAgICB5U2ltIDwtIHQoeVNpbSkKICAKICAgICMjIyBGaXR0aW5nCiAgICBmaXRBbGwgPC0gbGltbWE6OmxtRml0KHlTaW0sZGVzaWduKQogIAogICAgIyMjIEluZmVyZW5jZQogICAgdmFyVW5zY2FsZWQgPC0gYyh0KEwpJSolZml0QWxsJGNvdi5jb2VmZmljaWVudHMlKiVMKQogICAgY29udHJhc3RzIDwtIGZpdEFsbCRjb2VmZmljaWVudHMgJSolTAogICAgc2VDb250cmFzdHMgPC0gdmFyVW5zY2FsZWReLjUqZml0QWxsJHNpZ21hCiAgICB0c3RhdHMgPC0gY29udHJhc3RzL3NlQ29udHJhc3RzCiAgICBwdmFscyA8LSBwdChhYnModHN0YXRzKSxmaXRBbGwkZGYucmVzaWR1YWwsbG93ZXIudGFpbCA9IEZBTFNFKSoyCiAgICAKICAgIGkgPC0gaSsxCiAgICBwb3dlckZhc3RbaSxdIDwtIGMoYjEsbixtZWFuKHB2YWxzIDwgYWxwaGEpKQogIH0KfQpgYGAKCmBgYHtyfQpwb3dlckZhc3QgJT4lIAogIGdncGxvdChhZXMoeD1uLHk9cG93ZXIsY29sPWIxJT4lYXMuZmFjdG9yKSkgKwogIGdlb21fbGluZSgpCmBgYAoKIyMgTWVlciBlZmZpY2nDq250ZSBjb2RlIGdlYmFzZWVyZCBvcCBkZSBhbmFseXRpc2NoZSBvcGxvc3Npbmcgdm9vciBlZW4gdHdvIGdyb3VwIGNvbXBhcmlzb24KCi0gVm9vciBkZSB0d28gc2FtcGxlIHQtdGVzdCBiZXN0YWF0IGVyIGVlbiBhbmFseXRpc2NoZSBvcGxvc3Npbmcgb20gZGUgcG93ZXIgdGUgYmVyZWtlbmVuLiAKCi0gSW4gZGV6ZSBjb250ZXh0IG1hYWt0IG1lbiB0eXBpc2NoIGdlYnJ1aWsgdmFuIGRlICJDb2hlbidzIGVmZmVjdCBzaXplIjogCgokRCA9IFxmcmFje1xkZWx0YX17U0R9JAoKCmBgYHtyfQpwb3dlci50LnRlc3Qobj04LCBkZWx0YSA9IGRlbHRhLCBzZCA9IHNkLCB0eXBlPSd0d28uc2FtcGxlJykKYGBgCgpNZXJrIG9wIGRhdCBkZSBwb3dlciBvdmVyZWVua29tdCBtZXQgZGVnZW5lIGRpZSB3ZSB2aWEgc2ltdWxhdGllcyBoZWJiZW4gZ2VzY2hhdCEgCgoKYGBge3J9CmIwIDwtIDAKc2QgPC0gcm91bmQobWljZVN1bSRzZFsyXSwxKQpucyA8LSAgYygzLDUsMTAsMjAsMjUsNTAsNzUsMTAwKSAgCmRlbHRhcyA8LSBjKDEsMiwzLDUsMTApCgoKcG93ZXJUaGVvIDwtIGRhdGEuZnJhbWUoZGVsdGFzPXJlcChkZWx0YXMsZWFjaD1sZW5ndGgobnMpKSwKICAgICAgICAgICAgICAgICAgICBuPXJlcChucyxsZW5ndGgoZGVsdGFzKSksCiAgICAgICAgICAgICAgICAgICAgcG93ZXI9TkEpCgpwb3dlclRoZW8kcG93ZXIgPC0gYXBwbHkocG93ZXJUaGVvWywxOjJdLDEsZnVuY3Rpb24oeCkgcG93ZXIudC50ZXN0KGRlbHRhPXhbMV0sbj14WzJdLHNkPXNkLHR5cGU9InR3by5zYW1wbGUiKSRwb3dlcikKYGBgCgoKYGBge3J9CnBvd2VyVGhlbyAlPiUgCiAgZ2dwbG90KGFlcyh4PW4seT1wb3dlcixjb2w9ZGVsdGFzJT4lYXMuZmFjdG9yKSkgKwogIGdlb21fbGluZSgpCmBgYA==