library(data.table)
library(foreign)
library(rgdal)
library(spdep)
library(splm)
library(tmap)
load("../tmp/resilience.RData")
# Display options
library(pander)
panderOptions("big.mark", ",")
panderOptions("round", 2)
panderOptions("missing", ".")
panderOptions("table.split.table", 100)
# Helper
"%||%" <- function(a, b) if (!is.null(a)) a else b
# Helper - Combine results from list of `splm` objects
splm.combine <- function(x) data.table(
  model=names(x), 
  do.call(rbind, lapply(x, coef)), 
  do.call(rbind, lapply(x, `[[`, "arcoef")),
  phi=as.numeric(do.call(rbind, lapply(x, function(x) try(x$errcomp[["phi"]])))),
  psi=as.numeric(do.call(rbind, lapply(x, function(x) try(x$errcomp[["psi"]])))),
  rho=as.numeric(do.call(rbind, lapply(x, function(x) try(x$errcomp[["rho"]])))))
# Helper - AIC function for `spml`, show goodness of fit measure
# at https://stat.ethz.ch/pipermail/r-sig-geo/2016-February/024077.html
godf.spml<-function(object, k=2, criterion=c("AIC", "BIC"),  ...) {
  s <- summary(object)
  l <- s$logLik[1,1]
  np <- length(coef(s))
  N <- nrow(s$model)
  if (criterion=="AIC") {
    aic <- -2*l+k*np
    names(aic) <-"AIC"
    return(aic)
  }
  if (criterion=="BIC") {
    bic <- -2*l+log(N)*np
    names(bic) <-"BIC"
    if (k!=2) {
      warning("parameter <k> not used for BIC")
    }
    return(bic)
  }
}

All biophysical variables were extracted by Mel/Tim (e.g. code at https://github.com/IFPRI/sda-rdata/blob/master/R/data_2016.R#L870) and Beliyou constructed long-term and seasonal summary variables. For the spatial panel analysis we use R package splm developed by Millo and Piras, 20091. splm is documented at https://www.r-project.org/conferences/useR-2009/slides/Millo+Piras.pdf and http://facweb.knowlton.ohio-state.edu/pviton/courses/crp87105/millo-piras.pdf. Beliyou also suggested to try a “pooled” spatial regression combining weights at district and household levels.

We need to test for spatial auto-correlation in the sample and test which of the SPEI, drought, temperature, travel time, elevation, and/or rainfall shock variable(s) to include in the model.

Literature review

splm methods expect spatial panels in a particular format. Also note that splm commands for spatial panels expect a balanced dataset (same number of observations over time). Spatial locations should also be stable over time, though might be feasible to define time-dependent weight matrices \(W_t\).

Spatial weights

A good discussion of spatial weights and using Rook/Queen contiguity vs. distance (distance thresholds, k-nearest neighbor, or other) approach is in Chapter 9 Bivand 20082.

The first step is to define which relationships between observations are to be given a non-zero weight; that is to choose the neighbor criterion to be used; the second is to assign weights to the identified neighbor links.[…] analysing areal data is crucially dependent on the choices made in constructing the spatial weights.

Contiguity weights are usually used with administrative units (e.g. districts), but other definitions are possible. Alternate options can be tested econometrically. From World Bank 20083Rook and Queen Contiguity spatial weighting often leads to a very unbalanced structure. Larger units can have more neighbors and small units a smaller number of neighbors. The solution is to set a unique number of neighbors for all areas by creating a k-nearest neighbor weighting matrix. When geo-referenced coordinates are available, the spatial weights can be derived from the distance between different points. Euclidean distance weighting fixes a specified distance and then counts the number of neighbors that fall within that distance.

On choosing a weight matrix, see also Elhorst, 20144:

As an alternative to row-normalization, W might be normalized such that the elements of each column sum to one. This type of normalization is sometimes used in the social economics literature (Leenders 2002). Note that the row elements of a spatial weights matrix display the impact on a particular unit by all other units, while the column elements of a spatial weights matrix display the impact of a particular unit on all other units. Consequently, row normalization has the effect that the impact on each unit by all other units is equalized, while column normalization has the effect that the impact of each unit on all other units is equalized.

If we know little about the assumed spatial process, we try to avoid moving far from the binary representation (Bavaud, 1998).

Difference between spatial lag and spatial error models

Fom Elhorst, 20145:

When specifying interaction between spatial units, the model may contain a spatially lagged dependent variable or a spatial autoregressive process in the error term, known as the spatial lag and the spatial error model, respectively.

  • The spatial lag model posits that the dependent variable depends on the dependent variable observed in neighboring units and on a set of observed local characteristics. According to Anselin et al. (2006, p. 6), the spatial lag model is typically considered as the formal specification for the equilibrium outcome of a spatial or social interaction process, in which the value of the dependent variable for one agent is jointly determined with that of the neighboring agents. In the empirical literature on strategic interaction among local governments, for example, the spatial lag model is theoretically consistent with the situation where taxation and expenditures on public services interact with taxation and expenditures on public services in nearby jurisdictions (Brueckner 2003).

  • The spatial error model, on the other hand, posits that the dependent variable depends on a set of observed local characteristics and that the error terms are correlated across space According to Anselin et al. (2006, p. 7), a spatial error specification does not require a theoretical model for a spatial or social interaction process, but, instead, is a special case of a nonspherical error covariance matrix. In the empirical literature on strategic interaction among local governments, the spatial error model is consistent with a situation where determinants of taxation or expenditures on public services omitted from the model are spatially autocorrelated, and with a situation where unobserved shocks follow a spatial pattern. A spatially autocorrelated error term may also be interpreted to reflect a mechanism to correct rent-seeking politicians for unanticipated fiscal policy changes (Allers and Elhorst 2005).

Fixed and random effects

The spatial specific effects may be treated as fixed effects or as random effects. In the fixed effects model, a dummy variable is introduced for each spatial unit, while in the random effects model, \(mu\) is treated as a random variable that is independently and identically distributed.

  • […] A related problem of controlling for spatial fixed effects is that any variable that does not change over time or only varies a little cannot be estimated, because it is wiped out by the demeaning transformation. This is the main reason for many studies not controlling for spatial fixed effects.

  • […] A compromise solution to the all or nothing way of utilizing the crosssectional component of the data is the random effects model. This model avoids the loss of degrees of freedom incurred in the fixed effects model associated with a relatively large N and the problem that the coefficients of time-invariant variables cannot be estimated. However, whether the random effects model is an appropriate specification in spatial research remains controversial.

The random effects model can be tested against the fixed effects model using Hausman’s specification test (Baltagi 2005, pp. 66-68) available in command sphtest.

Example

To start with let’s try to replicate and understand Millo’s example.

# Look at the sample data provided in `splm`. In that example a proximity matrix is
# constructed considering all the farms of the same village as neighbours. One can
# expect both village-level heterogeneity and spatial correlation between farms
# belonging to the same village. Spatial dependence is easier to justify for the error
# terms, due to spillovers across neighbouring farms in idiosyncratic factors and
# climate conditions; more difficult to find reasons for the inclusion of a spatial lag
# of the dependent variable, as it seems unrealistic for the outcome in one farm to
# influence those of neighbours.
data(RiceFarms, riceww)
RiceFarms <- data.table(RiceFarms)
RiceFarms[, .N, keyby=time]
# 171 obs in each period
dim(riceww)
# [1] 171 171
RiceFarms[, .N, keyby=.(region, time)]
# => stable # of obs, balanced dataset is used here

# The full model formula
fm <- log(goutput) ~ 
  log(seed) + log(urea) + phosphate + 
  log(totlabor) + log(size) + I(pesticide > 0) + I(varieties=="high") +
  I(varieties=="mixed") + as.factor(region) + I(as.numeric(time) %in% c(1,3,5))

# Make sure we can reproduce table 4. in Millo 2013
errors <- c("semsrre", "sem2srre", "semre", "sem2re", "semsr", "srre", "sem", "re", "sr", "ols")
mod <- lapply(errors, function(x) spreml(fm, data=RiceFarms, w=riceww, errors=x, lag=T))
names(mod) <- errors
# Print results
tmp <- splm.combine(mod)
Error in x$errcomp[["phi"]] : subscript out of bounds
Error in x$errcomp[["phi"]] : subscript out of bounds
Error in x$errcomp[["phi"]] : subscript out of bounds
Error in x$errcomp[["psi"]] : subscript out of bounds
Error in x$errcomp[["psi"]] : subscript out of bounds
Error in x$errcomp[["psi"]] : subscript out of bounds
Error in x$errcomp[["psi"]] : subscript out of bounds
Error in x$errcomp[["rho"]] : subscript out of bounds
Error in x$errcomp[["rho"]] : subscript out of bounds
Error in x$errcomp[["rho"]] : subscript out of bounds
setnames(tmp, c(2:10), c("Y", "seed", "urea", "tsp", "lab", "size", "pest", "high", "mixed"))
pander(tmp[, .SD, .SDcols=c(1:10, 17:20)])

-----------------------------------------------------------------------------------------------
 model    Y    seed   urea   tsp   lab   size   pest   high   mixed   lambda   phi   psi   rho 
-------- ---- ------ ------ ----- ----- ------ ------ ------ ------- -------- ----- ----- -----
semsrre  4.03  0.12   0.13    0   0.23   0.51  -0.01   0.12    0.1     0.17   0.17  0.09  0.67 

sem2srre  4    0.12   0.13    0   0.23   0.51  -0.01   0.12    0.1     0.18   0.16  0.09  0.66 

 semre   4.03  0.12   0.13    0   0.23   0.51  -0.01   0.12    0.1     0.17    0.2    .   0.67 

 sem2re  3.98  0.12   0.13    0   0.23   0.51  -0.01   0.12    0.1     0.18   0.19    .   0.67 

 semsr   4.04  0.12   0.13    0   0.23   0.51  -0.01   0.13    0.1     0.17     .    0.2  0.65 

  srre   2.59  0.12   0.12    0   0.21   0.5    0.02   0.12    0.1     0.4    0.11  0.11    .  

  sem    4.01  0.12   0.14    0   0.22   0.51  -0.01   0.13   0.09     0.17     .     .   0.64 

   re    2.55  0.12   0.13    0    0.2   0.5    0.03   0.11   0.09     0.4    0.15    .     .  

   sr    2.66  0.12   0.13    0   0.21   0.5    0.01   0.13    0.1     0.38     .   0.18    .  

  ols    2.65  0.12   0.14    0    0.2   0.51   0.02   0.12   0.08     0.39   0.17  0.09  0.67 
-----------------------------------------------------------------------------------------------

Interpretation:

  • \(phi\) individual effects
  • \(psi\) the serial correlation coefficient, is significant but small
  • \(rho\) spatial error correlation
  • \(lamda\) spatial lag coefficient is non-significant

Data preparation, validation (GHA, TZA, UGA)

# Load Beliyou's Hhld variables (800MB, 2670 vars!)
hh <- read.dta("./tmp/Combined_4_Mel.12.dta")

# Keep STATA var labels
hh.lbl <- data.table(varCode=names(hh), varLabel=attr(hh, "var.labels"))
hh <- data.table(hh)

# Load spatial features from biophysical workspace
load("../../hc-data/out/2016.09/svyL2Maps_r16.09.RData")

# Keep only the datasets and spatial features we need
rm(list=ls()[!ls() %in% c("hh", "hh.lbl", "gps", "g2", "g2.lbl", "gps.pts", "iso3", "svy")])

# For the panel regressions, we limit obs to 
panels <- list(TZA=c("NPS09", "NPS11", "NPS13"), UGA=c("NPS10", "NPS11", "NPS12"))

# Also list vars we want to include in the regression
# STATA: xi:xtreg ${outcome`j'} ${cont`t'`i'`c'`v't`t'} $headcont $hhcont $wealth $bio $round $region 
#          if $filter /*[pweight=weight_]*/, `p' vce(r)
models <- list(
  Y=c("pcexp_pppimp", "pcfoodexp_pppimp"), # take the ln()
  X=list(
    headcont=c("femhead", "agehead"),
    hhcont=c("hhsize", "hhsizesq", "educave"),
    wealth=c("landown", "tlu_total", "agwealth_paran", "nonagwealth_paran"), # "electricity"
    bio=c("TT20k_hours", "elevation", "lgp"), # "far", "mean_popden2000""
    health=c("malariain")
  )
)

panels.hh <- hh[(ISO3=="TZA" & survey %in% panels$TZA) | (ISO3=="UGA" & survey %in% panels$UGA)]
rm(hh)

Balance survey panels

# Tally obs across panels
tmp <- panels.hh[, .N, keyby=.(ISO3, region=regionname, panel=survey)]
tmp <- tmp[, lapply(.SD, paste, collapse=", "), .SDcols=c("N", "panel"), keyby=.(ISO3, region)]
pander(tmp, caption="Obs. across panels")

----------------------------------------------------------------
 ISO3          region                N              panel       
------ ----------------------- ------------- -------------------
 TZA           Arusha          104, 122, 149 NPS09, NPS11, NPS13

 TZA        Dar es salaam      555, 624, 742 NPS09, NPS11, NPS13

 TZA           Dodoma          88, 109, 138  NPS09, NPS11, NPS13

 TZA           Iringa          128, 137, 156 NPS09, NPS11, NPS13

 TZA       KASKAZINI PEMBA      80, 90, 103  NPS09, NPS11, NPS13

 TZA      KASKAZINI UNGUJA      72, 77, 89   NPS09, NPS11, NPS13

 TZA        KUSINI PEMBA        88, 97, 106  NPS09, NPS11, NPS13

 TZA        KUSINI UNGUJA       32, 47, 50   NPS09, NPS11, NPS13

 TZA           Kagera          120, 157, 216 NPS09, NPS11, NPS13

 TZA           Kigoma          104, 130, 172 NPS09, NPS11, NPS13

 TZA         Kilimanjaro       104, 114, 130 NPS09, NPS11, NPS13

 TZA            Lindi          150, 180, 218 NPS09, NPS11, NPS13

 TZA   MJINI/MAGHARIBI UNGUJA  207, 222, 241 NPS09, NPS11, NPS13

 TZA           Manyara          80, 83, 94   NPS09, NPS11, NPS13

 TZA            Mara            56, 64, 85   NPS09, NPS11, NPS13

 TZA            Mbeya          152, 173, 211 NPS09, NPS11, NPS13

 TZA          Morogoro         112, 135, 188 NPS09, NPS11, NPS13

 TZA           Mtwara          200, 237, 319 NPS09, NPS11, NPS13

 TZA           Mwanza          128, 175, 304 NPS09, NPS11, NPS13

 TZA            Pwani           64, 85, 117  NPS09, NPS11, NPS13

 TZA            Rukwa          88, 100, 110  NPS09, NPS11, NPS13

 TZA           Ruvuma          136, 158, 191 NPS09, NPS11, NPS13

 TZA          Shinyanga        136, 194, 277 NPS09, NPS11, NPS13

 TZA           Singida          56, 66, 87   NPS09, NPS11, NPS13

 TZA           Tabora          112, 144, 227 NPS09, NPS11, NPS13

 TZA            Tanga          112, 124, 163 NPS09, NPS11, NPS13

 UGA           Kampala         232, 179, 186 NPS10, NPS11, NPS12

 UGA   Central without Kampala 685, 636, 653 NPS10, NPS11, NPS12

 UGA           Eastern         677, 633, 645 NPS10, NPS11, NPS12

 UGA          Northern         703, 678, 732 NPS10, NPS11, NPS12

 UGA           Western         626, 507, 600 NPS10, NPS11, NPS12
----------------------------------------------------------------

Table: Obs. across panels
# TODO Append X,Y coordinates
# Verify unique records first
setkey(panels.hh, ISO3, survey, round, cluster, hhid)
panels.hh[duplicated(panels.hh), unique(hhid)]

# Balance panels (we need stable count of obs. across survey rounds)
pander(panels.hh[balanced==1, .N, keyby=.(ISO3, survey, svyCode)], caption="Obs. across panels, balanced")


# Summarize and graph/map regressor vars, check for missing data

Summarize biophysical conditions and shocks

# Try to merge features and attributes across panels

# Using SPEI, UDEL, and CHIRPS, map skewness of distribution


# Add drought median duration


# Map bio shock variables

Test for spatial autocorrelation

# Moran I test on all regressors and outcome variables
# Collect results

Generate spatial weights

The LSMS-ISA panels provide GPS coords for all households. Intuitively we could try a few different approaches:

  • \(W_1\) create spatial weights using k-nearest neighbors within a specific distance threshold (or enumeration area, or marketshed, or agro-ecological zone, or region/province)
  • \(W_2\) use inverse travel times weights (possibly within a distance or travel time threshold)
  • \(W_3\) simply use inverse distance weights between households.

\(W_1\) seems more common in the socio-economic literature. \(W_3\) is more straightforward but Euclidian distances could be misleading. \(W_2\) requires more work (e.g. can we use Google Maps Distance Matrix API to compute realistic travel times between all household locations?

# Generate NxN travel time matrix using Google Distance API

# API quotas
# entries per day: 2,500    
# requests per 100 seconds: 10,000  
# requests per 100 seconds per user: Unlimited
# results are in seconds

# Hit the API survey by survey, start with most recent panel
i <- svy[3]
tmp <- data.table(gps.pts@data[gps.pts$svyCode==i,])
# Init matrix
panels.tt <- matrix(as.numeric(NA), nrow=nrow(tmp), ncol=nrow(tmp))
dimnames(panels.tt) <- c(tmp$hhid, tmp$hhid)

# Helper - Google maps distance matrix API
gmapsapi <- function(o, d) {
  require(httr)
  url <- "https://maps.googleapis.com/maps/api/distancematrix/json"
  out <- GET(url, query=list(origins=o, destinations=d, mode="driving", key=api_key))
  out <- jsonlite::fromJSON(content(out, as="text"))
  return(out$rows$elements[[1]]$duration$value)
}

# Hit the API and collect responses
for (i in 1:nrow(tmp)[1]) {
  N <- seq(i, nrow(tmp), 200)
  for (j in 1:length(N)) {
    f <- N[j]
    t <- N[j+1]-1
    panels.tt[i, f:t] <- gmapsapi(
      tmp[i, paste(Y_mod, X_mod, sep=",")],
      tmp[f:t, paste(Y_mod, X_mod, sep=",", collapse="|")])
  }
}
#=> works but returns quite a few NAs

# Map results
l <- data.table(tmp[1, X_mod], tmp[1, Y_mod], tmp[-1, X_mod], tmp[-1,Y_mod])
l <- lapply(1:nrow(l), function(i) list(L=Line(matrix(unlist(l[i,]), ncol=2, byrow=T)), i=i))
l <- SpatialLines(lapply(l, function(E) Lines(list(E$L),as.character(E$i))),
  proj4string=CRS("+init=epsg:4326"))
l <- SpatialLinesDataFrame(l, data.frame(time=panels.tt[1,-1]/(60*60)))
tm_shape(World) + tm_polygons() +
  tm_shape(l[1:2297,], is.master=T) + 
  tm_lines(col="time", colorNA="white", lwd=.4, alpha=.4, title.col="travel (hours)") +
  tm_style_grey()

Google API works but returns a lot of missing values between pairs of locations, seems difficult to avoid. Another approach is to use an arbitrary distance to define neighbors (e.g. 50km).

# Generate spatial neighbour list
nb1 <- knn2nb(knearneigh(coords, k=50), row.names=paste(g2.nb$ISO3, g2.nb$rn, sep="."))
summary(nb1)

# Inverse distance matrix
w1 <- lapply(nb1, function(x) 1/(x/1000))
w1 <- nb2listw(nb1, glist=w1, style="B")

summary(w1$weights)
summary(sapply(w1$weights, sum))

# Generate spatial neighbour list
nb2 <- poly2nb(g2.nb, row.names=paste(g2.nb$ISO3, g2.nb$rn, sep="."))
summary(nb2)

# Distance matrix
w2 <- nb2mat(nb2, style="W", zero.policy=T)

Spatial panel regressions for Tanzania and Uganda (NPS panels)

In short:

  1. spfml command estimates both fixed effects spatial lag and error models (with different methods to calculate the determinants, default eigen) and different effects:
    • pooled constant term only
    • spfe cross-sectional specific effects
    • tpfe time-period specific effects
    • sptpfe include both spatial and temporal fixed effects

spfml returns residuals, a table of estimated coefficients (rho is the coeff of the spatially lagged dependent variable). Fixed effects can be extracted using effects(res) that returns the type of effects with significance levels and the constant term.

  1. spreml command is used to generate spatial random effects with several error options:
    • semsrre full (most general) model
    • semsr serially and spatially correlated disturbances, no random effects
    • srre serial correlation and random effects
    • semre exclude serial correlation
    • retraditional random effects model
    • sr panel regression with serially correlated errors
    • sem pooled model with spatially autocorrelated residuals
  2. Command bsktest is used to test for random effects and spatial error correlation (e.g. Baltagi, Song and Koh SLM1 marginal test).

  3. Command bsjktest is used to test for joint, marginal and conditional tests for random effects, serial and spatial error correlation (takes a model formula as input).

Model specifications

# Subsample to "rural != 0 & agri_hh != 0"
# Start iterating across model specfications
# Collect results

Pooled regressions for Ghana, Tanzania, Uganda cross-sections

# Save snapshot
rm(tmp, RiceFarms, riceww, x, i, j)
save.image("./tmp/resilience.RData")

References


  1. Millo, G and Piras, G. (2009) “splm: Spatial Panel data models in R”, Journal of Statistical Software, Vol. VV, Issue II. Online at http://facweb.knowlton.ohio-state.edu/pviton/courses/crp87105/millo-piras.pdf

  2. Bivand, R.S., Pebesma, E.j., and Gomez-Rubio, V. (2008) “Applied Spatial Analysis with R”. Springer. Online at http://gis.humboldt.edu/OLM/r/Spatial%20Analysis%20With%20R.pdf

  3. Elbers, C., Lanjouw, P. and Leite, P.G. (2008) “Brazil within Brazil: Testing the Poverty Map Methodology in Minas Gerais”, World Bank Policy Research Working Paper #4513. Online at http://documents.worldbank.org/curated/en/941401468231893568/pdf/wps4513.pdf

  4. Elhorst, J.P. (2014) Spatial Panel Data Models. Chapter C.2 in “Spatial Econometrics from Cross-Sectional Data to Spatial Panels” Spinger, 2014. University of Groningen. On-line at http://regroningen.nl/elhorst/doc/Spatial%20Panel%20Data%20Models.pdf

  5. Elhorst, J.P. (2014) Spatial Panel Data Models. Chapter C.2 in “Spatial Econometrics from Cross-Sectional Data to Spatial Panels” Spinger, 2014. University of Groningen. On-line at http://regroningen.nl/elhorst/doc/Spatial%20Panel%20Data%20Models.pdf

LS0tCnRpdGxlOiAgIlJlc2lsaWVuY2UgQW5hbHlzaXMgLSBHaGFuYSwgVGFuemFuaWEsIFVnYW5kYSIKYXV0aG9yOiAiSUZQUkkvSGFydmVzdENob2ljZSIKZGF0ZTogICAiT2N0LiAyMDE2LiBMYXN0IHVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAuIERSQUZULCBkbyBub3QgdXNlIG9yIGNpdGUhIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGZpZ19oZWlnaHQ6IDUKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogeWVzCi0tLQogIAogIApgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRX0KCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShmb3JlaWduKQpsaWJyYXJ5KHJnZGFsKQpsaWJyYXJ5KHNwZGVwKQpsaWJyYXJ5KHNwbG0pCmxpYnJhcnkodG1hcCkKCmxvYWQoIi4uL3RtcC9yZXNpbGllbmNlLlJEYXRhIikKCmBgYAoKCmBgYHtyIGhlbHBlcn0KCiMgRGlzcGxheSBvcHRpb25zCmxpYnJhcnkocGFuZGVyKQpwYW5kZXJPcHRpb25zKCJiaWcubWFyayIsICIsIikKcGFuZGVyT3B0aW9ucygicm91bmQiLCAyKQpwYW5kZXJPcHRpb25zKCJtaXNzaW5nIiwgIi4iKQpwYW5kZXJPcHRpb25zKCJ0YWJsZS5zcGxpdC50YWJsZSIsIDEwMCkKCiMgSGVscGVyCiIlfHwlIiA8LSBmdW5jdGlvbihhLCBiKSBpZiAoIWlzLm51bGwoYSkpIGEgZWxzZSBiCgojIEhlbHBlciAtIENvbWJpbmUgcmVzdWx0cyBmcm9tIGxpc3Qgb2YgYHNwbG1gIG9iamVjdHMKc3BsbS5jb21iaW5lIDwtIGZ1bmN0aW9uKHgpIGRhdGEudGFibGUoCiAgbW9kZWw9bmFtZXMoeCksIAogIGRvLmNhbGwocmJpbmQsIGxhcHBseSh4LCBjb2VmKSksIAogIGRvLmNhbGwocmJpbmQsIGxhcHBseSh4LCBgW1tgLCAiYXJjb2VmIikpLAogIHBoaT1hcy5udW1lcmljKGRvLmNhbGwocmJpbmQsIGxhcHBseSh4LCBmdW5jdGlvbih4KSB0cnkoeCRlcnJjb21wW1sicGhpIl1dKSkpKSwKICBwc2k9YXMubnVtZXJpYyhkby5jYWxsKHJiaW5kLCBsYXBwbHkoeCwgZnVuY3Rpb24oeCkgdHJ5KHgkZXJyY29tcFtbInBzaSJdXSkpKSksCiAgcmhvPWFzLm51bWVyaWMoZG8uY2FsbChyYmluZCwgbGFwcGx5KHgsIGZ1bmN0aW9uKHgpIHRyeSh4JGVycmNvbXBbWyJyaG8iXV0pKSkpKQoKIyBIZWxwZXIgLSBBSUMgZnVuY3Rpb24gZm9yIGBzcG1sYCwgc2hvdyBnb29kbmVzcyBvZiBmaXQgbWVhc3VyZQojIGF0IGh0dHBzOi8vc3RhdC5ldGh6LmNoL3BpcGVybWFpbC9yLXNpZy1nZW8vMjAxNi1GZWJydWFyeS8wMjQwNzcuaHRtbApnb2RmLnNwbWw8LWZ1bmN0aW9uKG9iamVjdCwgaz0yLCBjcml0ZXJpb249YygiQUlDIiwgIkJJQyIpLCAgLi4uKSB7CiAgcyA8LSBzdW1tYXJ5KG9iamVjdCkKICBsIDwtIHMkbG9nTGlrWzEsMV0KICBucCA8LSBsZW5ndGgoY29lZihzKSkKICBOIDwtIG5yb3cocyRtb2RlbCkKICBpZiAoY3JpdGVyaW9uPT0iQUlDIikgewogICAgYWljIDwtIC0yKmwraypucAogICAgbmFtZXMoYWljKSA8LSJBSUMiCiAgICByZXR1cm4oYWljKQogIH0KICBpZiAoY3JpdGVyaW9uPT0iQklDIikgewogICAgYmljIDwtIC0yKmwrbG9nKE4pKm5wCiAgICBuYW1lcyhiaWMpIDwtIkJJQyIKICAgIGlmIChrIT0yKSB7CiAgICAgIHdhcm5pbmcoInBhcmFtZXRlciA8az4gbm90IHVzZWQgZm9yIEJJQyIpCiAgICB9CiAgICByZXR1cm4oYmljKQogIH0KfQoKYGBgCgpBbGwgYmlvcGh5c2ljYWwgdmFyaWFibGVzIHdlcmUgZXh0cmFjdGVkIGJ5IE1lbC9UaW0gKGUuZy4gY29kZSBhdApodHRwczovL2dpdGh1Yi5jb20vSUZQUkkvc2RhLXJkYXRhL2Jsb2IvbWFzdGVyL1IvZGF0YV8yMDE2LlIjTDg3MCkgYW5kIEJlbGl5b3UKY29uc3RydWN0ZWQgbG9uZy10ZXJtIGFuZCBzZWFzb25hbCBzdW1tYXJ5IHZhcmlhYmxlcy4gRm9yIHRoZSBzcGF0aWFsIHBhbmVsIGFuYWx5c2lzIHdlIHVzZSBSIHBhY2thZ2UgYHNwbG1gIGRldmVsb3BlZCBieSBNaWxsbyBhbmQgUGlyYXMsIDIwMDlbXm1pbGxvMjAwOV0uIGBzcGxtYCBpcyBkb2N1bWVudGVkIGF0Cmh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvY29uZmVyZW5jZXMvdXNlUi0yMDA5L3NsaWRlcy9NaWxsbytQaXJhcy5wZGYgYW5kCmh0dHA6Ly9mYWN3ZWIua25vd2x0b24ub2hpby1zdGF0ZS5lZHUvcHZpdG9uL2NvdXJzZXMvY3JwODcxMDUvbWlsbG8tcGlyYXMucGRmLgpCZWxpeW91IGFsc28gc3VnZ2VzdGVkIHRvIHRyeSBhICJwb29sZWQiIHNwYXRpYWwgcmVncmVzc2lvbiBjb21iaW5pbmcKd2VpZ2h0cyBhdCBkaXN0cmljdCBhbmQgaG91c2Vob2xkIGxldmVscy4KCldlIG5lZWQgdG8gdGVzdCBmb3Igc3BhdGlhbCBhdXRvLWNvcnJlbGF0aW9uIGluIHRoZSBzYW1wbGUgYW5kIHRlc3Qgd2hpY2ggb2YKdGhlIFNQRUksIGRyb3VnaHQsIHRlbXBlcmF0dXJlLCB0cmF2ZWwgdGltZSwgZWxldmF0aW9uLCBhbmQvb3IgcmFpbmZhbGwgc2hvY2sgdmFyaWFibGUocykgdG8gaW5jbHVkZSBpbiB0aGUgbW9kZWwuCgoKIyBMaXRlcmF0dXJlIHJldmlldwoKYHNwbG1gIG1ldGhvZHMgZXhwZWN0IHNwYXRpYWwgcGFuZWxzIGluIGEgcGFydGljdWxhciBmb3JtYXQuIEFsc28gbm90ZSB0aGF0IGBzcGxtYCBjb21tYW5kcyBmb3Igc3BhdGlhbCBwYW5lbHMgZXhwZWN0IGEgYmFsYW5jZWQgZGF0YXNldCAoc2FtZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIG92ZXIgdGltZSkuIFNwYXRpYWwgbG9jYXRpb25zIHNob3VsZCBhbHNvIGJlIHN0YWJsZSBvdmVyIHRpbWUsIHRob3VnaCBtaWdodCBiZSBmZWFzaWJsZSB0byBkZWZpbmUgdGltZS1kZXBlbmRlbnQgd2VpZ2h0IG1hdHJpY2VzICRXX3QkLgoKCiMjIFNwYXRpYWwgd2VpZ2h0cwoKQSBnb29kIGRpc2N1c3Npb24gb2Ygc3BhdGlhbCB3ZWlnaHRzIGFuZCB1c2luZyAqKlJvb2svUXVlZW4gY29udGlndWl0eSoqIHZzLiAqKmRpc3RhbmNlKiogKGRpc3RhbmNlIHRocmVzaG9sZHMsIGstbmVhcmVzdCBuZWlnaGJvciwgb3Igb3RoZXIpIGFwcHJvYWNoIGlzIGluIENoYXB0ZXIgOSBCaXZhbmQgMjAwOFteYml2YW5kMjAwOF0uCgoiKlRoZSBmaXJzdCBzdGVwIGlzIHRvIGRlZmluZSB3aGljaCByZWxhdGlvbnNoaXBzIGJldHdlZW4gb2JzZXJ2YXRpb25zIGFyZSB0byBiZSBnaXZlbiBhIG5vbi16ZXJvIHdlaWdodDsgdGhhdCBpcyB0byBjaG9vc2UgdGhlIG5laWdoYm9yIGNyaXRlcmlvbiB0byBiZSB1c2VkOyB0aGUgc2Vjb25kIGlzIHRvIGFzc2lnbiB3ZWlnaHRzIHRvIHRoZSBpZGVudGlmaWVkIG5laWdoYm9yIGxpbmtzLlsuLi5dIGFuYWx5c2luZyBhcmVhbCBkYXRhIGlzIGNydWNpYWxseSBkZXBlbmRlbnQgb24gdGhlIGNob2ljZXMgbWFkZSBpbiBjb25zdHJ1Y3RpbmcgdGhlIHNwYXRpYWwgd2VpZ2h0cy4qIgoKKipDb250aWd1aXR5IHdlaWdodHMqKiBhcmUgdXN1YWxseSB1c2VkIHdpdGggYWRtaW5pc3RyYXRpdmUgdW5pdHMgKGUuZy4gZGlzdHJpY3RzKSwgYnV0IG90aGVyIGRlZmluaXRpb25zIGFyZSBwb3NzaWJsZS4gQWx0ZXJuYXRlIG9wdGlvbnMgY2FuIGJlIHRlc3RlZCBlY29ub21ldHJpY2FsbHkuIEZyb20gV29ybGQgQmFuayAyMDA4W153YjIwMDhdICIqUm9vayBhbmQgUXVlZW4gQ29udGlndWl0eSBzcGF0aWFsIHdlaWdodGluZyBvZnRlbiBsZWFkcyB0byBhIHZlcnkgdW5iYWxhbmNlZCBzdHJ1Y3R1cmUuIExhcmdlciB1bml0cyBjYW4gaGF2ZSBtb3JlIG5laWdoYm9ycyBhbmQgc21hbGwgdW5pdHMgYSBzbWFsbGVyIG51bWJlciBvZiBuZWlnaGJvcnMuIFRoZSBzb2x1dGlvbiBpcyB0byBzZXQgYSB1bmlxdWUgbnVtYmVyIG9mIG5laWdoYm9ycyBmb3IgYWxsIGFyZWFzIGJ5IGNyZWF0aW5nIGEgay1uZWFyZXN0IG5laWdoYm9yIHdlaWdodGluZyBtYXRyaXguIFdoZW4gZ2VvLXJlZmVyZW5jZWQgY29vcmRpbmF0ZXMgYXJlIGF2YWlsYWJsZSwgdGhlIHNwYXRpYWwgd2VpZ2h0cyBjYW4gYmUgZGVyaXZlZCBmcm9tIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGRpZmZlcmVudCBwb2ludHMuIEV1Y2xpZGVhbiBkaXN0YW5jZSB3ZWlnaHRpbmcgZml4ZXMgYSBzcGVjaWZpZWQgZGlzdGFuY2UgYW5kIHRoZW4gY291bnRzIHRoZSBudW1iZXIgb2YgbmVpZ2hib3JzIHRoYXQgZmFsbCB3aXRoaW4gdGhhdCBkaXN0YW5jZS4qIgoKT24gY2hvb3NpbmcgYSB3ZWlnaHQgbWF0cml4LCBzZWUgYWxzbyBFbGhvcnN0LCAyMDE0W15lbGhvcnN0MjAxNF06CgoiKkFzIGFuIGFsdGVybmF0aXZlIHRvICoqcm93LW5vcm1hbGl6YXRpb24qKiwgVyBtaWdodCBiZSBub3JtYWxpemVkIHN1Y2ggdGhhdCB0aGUgZWxlbWVudHMgb2YgZWFjaCBjb2x1bW4gc3VtIHRvIG9uZS4gVGhpcyB0eXBlIG9mIG5vcm1hbGl6YXRpb24gaXMgc29tZXRpbWVzIHVzZWQgaW4gdGhlIHNvY2lhbCBlY29ub21pY3MgbGl0ZXJhdHVyZSAoTGVlbmRlcnMgMjAwMikuIE5vdGUgdGhhdCB0aGUgcm93IGVsZW1lbnRzIG9mIGEgc3BhdGlhbCB3ZWlnaHRzIG1hdHJpeCBkaXNwbGF5IHRoZSBpbXBhY3Qgb24gYSBwYXJ0aWN1bGFyIHVuaXQgYnkgYWxsIG90aGVyIHVuaXRzLCB3aGlsZSB0aGUgY29sdW1uIGVsZW1lbnRzIG9mIGEgc3BhdGlhbCB3ZWlnaHRzIG1hdHJpeCBkaXNwbGF5IHRoZSBpbXBhY3Qgb2YgYSBwYXJ0aWN1bGFyIHVuaXQgb24gYWxsIG90aGVyIHVuaXRzLiBDb25zZXF1ZW50bHksICoqcm93IG5vcm1hbGl6YXRpb24qKiBoYXMgdGhlIGVmZmVjdCB0aGF0IHRoZSBpbXBhY3Qgb24gZWFjaCB1bml0IGJ5IGFsbCBvdGhlciB1bml0cyBpcyBlcXVhbGl6ZWQsIHdoaWxlICoqY29sdW1uIG5vcm1hbGl6YXRpb24qKiBoYXMgdGhlIGVmZmVjdCB0aGF0IHRoZSBpbXBhY3Qgb2YgZWFjaCB1bml0IG9uIGFsbCBvdGhlciB1bml0cyBpcyBlcXVhbGl6ZWQuKiIKCiIqSWYgd2Uga25vdyBsaXR0bGUgYWJvdXQgdGhlIGFzc3VtZWQgc3BhdGlhbCBwcm9jZXNzLCB3ZSB0cnkgdG8gYXZvaWQgbW92aW5nIGZhciBmcm9tIHRoZSBiaW5hcnkgcmVwcmVzZW50YXRpb24gKEJhdmF1ZCwgMTk5OCkuKiIKCgoKCiMjIERpZmZlcmVuY2UgYmV0d2VlbiBzcGF0aWFsIGxhZyBhbmQgc3BhdGlhbCBlcnJvciBtb2RlbHMKCkZvbSBFbGhvcnN0LCAyMDE0W15lbGhvcnN0MjAxNF06CgoiKldoZW4gc3BlY2lmeWluZyBpbnRlcmFjdGlvbiBiZXR3ZWVuIHNwYXRpYWwgdW5pdHMsIHRoZSBtb2RlbCBtYXkgY29udGFpbiBhIHNwYXRpYWxseSBsYWdnZWQgZGVwZW5kZW50IHZhcmlhYmxlIG9yIGEgc3BhdGlhbCBhdXRvcmVncmVzc2l2ZSBwcm9jZXNzIGluIHRoZSBlcnJvciB0ZXJtLCBrbm93biBhcyB0aGUgKipzcGF0aWFsIGxhZyoqIGFuZCB0aGUgKipzcGF0aWFsIGVycm9yKiogbW9kZWwsIHJlc3BlY3RpdmVseS4qIgoKKiAiKlRoZSBzcGF0aWFsIGxhZyBtb2RlbCBwb3NpdHMgdGhhdCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGRlcGVuZHMgb24gdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBvYnNlcnZlZCBpbiBuZWlnaGJvcmluZyB1bml0cyBhbmQgb24gYSBzZXQgb2Ygb2JzZXJ2ZWQgbG9jYWwgY2hhcmFjdGVyaXN0aWNzLiBBY2NvcmRpbmcgdG8gQW5zZWxpbiBldCBhbC4gKDIwMDYsIHAuIDYpLCB0aGUgc3BhdGlhbCBsYWcgbW9kZWwgaXMgdHlwaWNhbGx5IGNvbnNpZGVyZWQgYXMgdGhlIGZvcm1hbCBzcGVjaWZpY2F0aW9uIGZvciB0aGUgZXF1aWxpYnJpdW0gb3V0Y29tZSBvZiBhIHNwYXRpYWwgb3Igc29jaWFsIGludGVyYWN0aW9uIHByb2Nlc3MsIGluIHdoaWNoIHRoZSB2YWx1ZSBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGZvciBvbmUgYWdlbnQgaXMgam9pbnRseSBkZXRlcm1pbmVkIHdpdGggdGhhdCBvZiB0aGUgbmVpZ2hib3JpbmcgYWdlbnRzLiBJbiB0aGUgZW1waXJpY2FsIGxpdGVyYXR1cmUgb24gc3RyYXRlZ2ljIGludGVyYWN0aW9uIGFtb25nIGxvY2FsIGdvdmVybm1lbnRzLCBmb3IgZXhhbXBsZSwgdGhlIHNwYXRpYWwgbGFnIG1vZGVsIGlzIHRoZW9yZXRpY2FsbHkgY29uc2lzdGVudCB3aXRoIHRoZSBzaXR1YXRpb24gd2hlcmUgdGF4YXRpb24gYW5kIGV4cGVuZGl0dXJlcyBvbiBwdWJsaWMgc2VydmljZXMgaW50ZXJhY3Qgd2l0aCB0YXhhdGlvbiBhbmQgZXhwZW5kaXR1cmVzIG9uIHB1YmxpYyBzZXJ2aWNlcyBpbiBuZWFyYnkganVyaXNkaWN0aW9ucyAoQnJ1ZWNrbmVyIDIwMDMpLioiCgoqICIqVGhlIHNwYXRpYWwgZXJyb3IgbW9kZWwsIG9uIHRoZSBvdGhlciBoYW5kLCBwb3NpdHMgdGhhdCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGRlcGVuZHMgb24gYSBzZXQgb2Ygb2JzZXJ2ZWQgbG9jYWwgY2hhcmFjdGVyaXN0aWNzIGFuZCB0aGF0IHRoZSBlcnJvciB0ZXJtcyBhcmUgY29ycmVsYXRlZCBhY3Jvc3Mgc3BhY2UgQWNjb3JkaW5nIHRvIEFuc2VsaW4gZXQgYWwuICgyMDA2LCBwLiA3KSwgYSBzcGF0aWFsIGVycm9yIHNwZWNpZmljYXRpb24gZG9lcyBub3QgcmVxdWlyZSBhIHRoZW9yZXRpY2FsIG1vZGVsIGZvciBhIHNwYXRpYWwgb3Igc29jaWFsIGludGVyYWN0aW9uIHByb2Nlc3MsIGJ1dCwgaW5zdGVhZCwgaXMgYSBzcGVjaWFsIGNhc2Ugb2YgYSBub25zcGhlcmljYWwgZXJyb3IgY292YXJpYW5jZSBtYXRyaXguIEluIHRoZSBlbXBpcmljYWwgbGl0ZXJhdHVyZSBvbiBzdHJhdGVnaWMgaW50ZXJhY3Rpb24gYW1vbmcgbG9jYWwgZ292ZXJubWVudHMsIHRoZSBzcGF0aWFsIGVycm9yIG1vZGVsIGlzIGNvbnNpc3RlbnQgd2l0aCBhIHNpdHVhdGlvbiB3aGVyZSBkZXRlcm1pbmFudHMgb2YgdGF4YXRpb24gb3IgZXhwZW5kaXR1cmVzIG9uIHB1YmxpYyBzZXJ2aWNlcyBvbWl0dGVkIGZyb20gdGhlIG1vZGVsIGFyZSBzcGF0aWFsbHkgYXV0b2NvcnJlbGF0ZWQsIGFuZCB3aXRoIGEgc2l0dWF0aW9uIHdoZXJlIHVub2JzZXJ2ZWQgc2hvY2tzIGZvbGxvdyBhIHNwYXRpYWwgcGF0dGVybi4gQSBzcGF0aWFsbHkgYXV0b2NvcnJlbGF0ZWQgZXJyb3IgdGVybSBtYXkgYWxzbyBiZSBpbnRlcnByZXRlZCB0byByZWZsZWN0IGEgbWVjaGFuaXNtIHRvIGNvcnJlY3QgcmVudC1zZWVraW5nIHBvbGl0aWNpYW5zIGZvciB1bmFudGljaXBhdGVkIGZpc2NhbCBwb2xpY3kgY2hhbmdlcyAoQWxsZXJzIGFuZCBFbGhvcnN0IDIwMDUpLioiCgoKIyMgRml4ZWQgYW5kIHJhbmRvbSBlZmZlY3RzCgoiKlRoZSBzcGF0aWFsIHNwZWNpZmljIGVmZmVjdHMgbWF5IGJlIHRyZWF0ZWQgYXMgZml4ZWQgZWZmZWN0cyBvciBhcyByYW5kb20gZWZmZWN0cy4gSW4gdGhlIGZpeGVkIGVmZmVjdHMgbW9kZWwsIGEgZHVtbXkgdmFyaWFibGUgaXMgaW50cm9kdWNlZCBmb3IgZWFjaCBzcGF0aWFsIHVuaXQsIHdoaWxlIGluIHRoZSByYW5kb20gZWZmZWN0cyBtb2RlbCwgJG11JCBpcyB0cmVhdGVkIGFzIGEgcmFuZG9tIHZhcmlhYmxlIHRoYXQgaXMgaW5kZXBlbmRlbnRseSBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQuKiIKCiogKlsuLi5dIEEgcmVsYXRlZCBwcm9ibGVtIG9mIGNvbnRyb2xsaW5nIGZvciBzcGF0aWFsIGZpeGVkIGVmZmVjdHMgaXMgdGhhdCBhbnkgdmFyaWFibGUgdGhhdCBkb2VzIG5vdCBjaGFuZ2Ugb3ZlciB0aW1lIG9yIG9ubHkgdmFyaWVzIGEgbGl0dGxlIGNhbm5vdCBiZSBlc3RpbWF0ZWQsIGJlY2F1c2UgaXQgaXMgd2lwZWQgb3V0IGJ5IHRoZSBkZW1lYW5pbmcgdHJhbnNmb3JtYXRpb24uIFRoaXMgaXMgdGhlIG1haW4gcmVhc29uIGZvciBtYW55IHN0dWRpZXMgbm90IGNvbnRyb2xsaW5nIGZvciBzcGF0aWFsIGZpeGVkIGVmZmVjdHMuKgoKKiAqWy4uLl0gQSBjb21wcm9taXNlIHNvbHV0aW9uIHRvIHRoZSBhbGwgb3Igbm90aGluZyB3YXkgb2YgdXRpbGl6aW5nIHRoZSBjcm9zc3NlY3Rpb25hbCBjb21wb25lbnQgb2YgdGhlIGRhdGEgaXMgdGhlIHJhbmRvbSBlZmZlY3RzIG1vZGVsLiBUaGlzIG1vZGVsIGF2b2lkcyB0aGUgbG9zcyBvZiBkZWdyZWVzIG9mIGZyZWVkb20gaW5jdXJyZWQgaW4gdGhlIGZpeGVkIGVmZmVjdHMgbW9kZWwgYXNzb2NpYXRlZCB3aXRoIGEgcmVsYXRpdmVseSBsYXJnZSBOIGFuZCB0aGUgcHJvYmxlbSB0aGF0IHRoZSBjb2VmZmljaWVudHMgb2YgdGltZS1pbnZhcmlhbnQgdmFyaWFibGVzIGNhbm5vdCBiZSBlc3RpbWF0ZWQuIEhvd2V2ZXIsIHdoZXRoZXIgdGhlIHJhbmRvbSBlZmZlY3RzIG1vZGVsIGlzIGFuIGFwcHJvcHJpYXRlIHNwZWNpZmljYXRpb24gaW4gc3BhdGlhbCByZXNlYXJjaCByZW1haW5zIGNvbnRyb3ZlcnNpYWwuKgoKVGhlIHJhbmRvbSBlZmZlY3RzIG1vZGVsIGNhbiBiZSB0ZXN0ZWQgYWdhaW5zdCB0aGUgZml4ZWQgZWZmZWN0cyBtb2RlbCB1c2luZyAqKkhhdXNtYW4ncyBzcGVjaWZpY2F0aW9uIHRlc3QqKiAoQmFsdGFnaSAyMDA1LCBwcC4gNjYtNjgpIGF2YWlsYWJsZSBpbiBjb21tYW5kIGBzcGh0ZXN0YC4gCgoKIyMgRXhhbXBsZQoKVG8gc3RhcnQgd2l0aCBsZXQncyB0cnkgdG8gcmVwbGljYXRlIGFuZCB1bmRlcnN0YW5kIE1pbGxvJ3MgZXhhbXBsZS4KCgpgYGB7ciBleGFtcGxlLCBldmFsPUZ9CgojIExvb2sgYXQgdGhlIHNhbXBsZSBkYXRhIHByb3ZpZGVkIGluIGBzcGxtYC4gSW4gdGhhdCBleGFtcGxlIGEgcHJveGltaXR5IG1hdHJpeCBpcwojIGNvbnN0cnVjdGVkIGNvbnNpZGVyaW5nIGFsbCB0aGUgZmFybXMgb2YgdGhlIHNhbWUgdmlsbGFnZSBhcyBuZWlnaGJvdXJzLiBPbmUgY2FuCiMgZXhwZWN0IGJvdGggdmlsbGFnZS1sZXZlbCBoZXRlcm9nZW5laXR5IGFuZCBzcGF0aWFsIGNvcnJlbGF0aW9uIGJldHdlZW4gZmFybXMKIyBiZWxvbmdpbmcgdG8gdGhlIHNhbWUgdmlsbGFnZS4gU3BhdGlhbCBkZXBlbmRlbmNlIGlzIGVhc2llciB0byBqdXN0aWZ5IGZvciB0aGUgZXJyb3IKIyB0ZXJtcywgZHVlIHRvIHNwaWxsb3ZlcnMgYWNyb3NzIG5laWdoYm91cmluZyBmYXJtcyBpbiBpZGlvc3luY3JhdGljIGZhY3RvcnMgYW5kCiMgY2xpbWF0ZSBjb25kaXRpb25zOyBtb3JlIGRpZmZpY3VsdCB0byBmaW5kIHJlYXNvbnMgZm9yIHRoZSBpbmNsdXNpb24gb2YgYSBzcGF0aWFsIGxhZwojIG9mIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIGFzIGl0IHNlZW1zIHVucmVhbGlzdGljIGZvciB0aGUgb3V0Y29tZSBpbiBvbmUgZmFybSB0bwojIGluZmx1ZW5jZSB0aG9zZSBvZiBuZWlnaGJvdXJzLgpkYXRhKFJpY2VGYXJtcywgcmljZXd3KQpSaWNlRmFybXMgPC0gZGF0YS50YWJsZShSaWNlRmFybXMpClJpY2VGYXJtc1ssIC5OLCBrZXlieT10aW1lXQojIDE3MSBvYnMgaW4gZWFjaCBwZXJpb2QKZGltKHJpY2V3dykKIyBbMV0gMTcxIDE3MQpSaWNlRmFybXNbLCAuTiwga2V5Ynk9LihyZWdpb24sIHRpbWUpXQojID0+IHN0YWJsZSAjIG9mIG9icywgYmFsYW5jZWQgZGF0YXNldCBpcyB1c2VkIGhlcmUKCiMgVGhlIGZ1bGwgbW9kZWwgZm9ybXVsYQpmbSA8LSBsb2coZ291dHB1dCkgfiAKICBsb2coc2VlZCkgKyBsb2codXJlYSkgKyBwaG9zcGhhdGUgKyAKICBsb2codG90bGFib3IpICsgbG9nKHNpemUpICsgSShwZXN0aWNpZGUgPiAwKSArIEkodmFyaWV0aWVzPT0iaGlnaCIpICsKICBJKHZhcmlldGllcz09Im1peGVkIikgKyBhcy5mYWN0b3IocmVnaW9uKSArIEkoYXMubnVtZXJpYyh0aW1lKSAlaW4lIGMoMSwzLDUpKQoKIyBNYWtlIHN1cmUgd2UgY2FuIHJlcHJvZHVjZSB0YWJsZSA0LiBpbiBNaWxsbyAyMDEzCmVycm9ycyA8LSBjKCJzZW1zcnJlIiwgInNlbTJzcnJlIiwgInNlbXJlIiwgInNlbTJyZSIsICJzZW1zciIsICJzcnJlIiwgInNlbSIsICJyZSIsICJzciIsICJvbHMiKQptb2QgPC0gbGFwcGx5KGVycm9ycywgZnVuY3Rpb24oeCkgc3ByZW1sKGZtLCBkYXRhPVJpY2VGYXJtcywgdz1yaWNld3csIGVycm9ycz14LCBsYWc9VCkpCm5hbWVzKG1vZCkgPC0gZXJyb3JzCgpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQoKIyBQcmludCByZXN1bHRzCnRtcCA8LSBzcGxtLmNvbWJpbmUobW9kKQpzZXRuYW1lcyh0bXAsIGMoMjoxMCksIGMoIlkiLCAic2VlZCIsICJ1cmVhIiwgInRzcCIsICJsYWIiLCAic2l6ZSIsICJwZXN0IiwgImhpZ2giLCAibWl4ZWQiKSkKcGFuZGVyKHRtcFssIC5TRCwgLlNEY29scz1jKDE6MTAsIDE3OjIwKV0pCgpgYGAKCkludGVycHJldGF0aW9uOgoKKiAkcGhpJCBpbmRpdmlkdWFsIGVmZmVjdHMKKiAkcHNpJCB0aGUgc2VyaWFsIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50LCBpcyBzaWduaWZpY2FudCBidXQgc21hbGwKKiAkcmhvJCBzcGF0aWFsIGVycm9yIGNvcnJlbGF0aW9uCiogJGxhbWRhJCBzcGF0aWFsIGxhZyBjb2VmZmljaWVudCBpcyBub24tc2lnbmlmaWNhbnQKCgojIERhdGEgcHJlcGFyYXRpb24sIHZhbGlkYXRpb24gKEdIQSwgVFpBLCBVR0EpCgpgYGB7ciwgZXZhbD1GfQoKIyBMb2FkIEJlbGl5b3UncyBIaGxkIHZhcmlhYmxlcyAoODAwTUIsIDI2NzAgdmFycyEpCmhoIDwtIHJlYWQuZHRhKCIuL3RtcC9Db21iaW5lZF80X01lbC4xMi5kdGEiKQoKIyBLZWVwIFNUQVRBIHZhciBsYWJlbHMKaGgubGJsIDwtIGRhdGEudGFibGUodmFyQ29kZT1uYW1lcyhoaCksIHZhckxhYmVsPWF0dHIoaGgsICJ2YXIubGFiZWxzIikpCmhoIDwtIGRhdGEudGFibGUoaGgpCgojIExvYWQgc3BhdGlhbCBmZWF0dXJlcyBmcm9tIGJpb3BoeXNpY2FsIHdvcmtzcGFjZQpsb2FkKCIuLi8uLi9oYy1kYXRhL291dC8yMDE2LjA5L3N2eUwyTWFwc19yMTYuMDkuUkRhdGEiKQoKIyBLZWVwIG9ubHkgdGhlIGRhdGFzZXRzIGFuZCBzcGF0aWFsIGZlYXR1cmVzIHdlIG5lZWQKcm0obGlzdD1scygpWyFscygpICVpbiUgYygiaGgiLCAiaGgubGJsIiwgImdwcyIsICJnMiIsICJnMi5sYmwiLCAiZ3BzLnB0cyIsICJpc28zIiwgInN2eSIpXSkKCiMgRm9yIHRoZSBwYW5lbCByZWdyZXNzaW9ucywgd2UgbGltaXQgb2JzIHRvIApwYW5lbHMgPC0gbGlzdChUWkE9YygiTlBTMDkiLCAiTlBTMTEiLCAiTlBTMTMiKSwgVUdBPWMoIk5QUzEwIiwgIk5QUzExIiwgIk5QUzEyIikpCgojIEFsc28gbGlzdCB2YXJzIHdlIHdhbnQgdG8gaW5jbHVkZSBpbiB0aGUgcmVncmVzc2lvbgojIFNUQVRBOiB4aTp4dHJlZyAke291dGNvbWVgaid9ICR7Y29udGB0J2BpJ2BjJ2B2J3RgdCd9ICRoZWFkY29udCAkaGhjb250ICR3ZWFsdGggJGJpbyAkcm91bmQgJHJlZ2lvbiAKIyAgICAgICAgICBpZiAkZmlsdGVyIC8qW3B3ZWlnaHQ9d2VpZ2h0X10qLywgYHAnIHZjZShyKQptb2RlbHMgPC0gbGlzdCgKICBZPWMoInBjZXhwX3BwcGltcCIsICJwY2Zvb2RleHBfcHBwaW1wIiksICMgdGFrZSB0aGUgbG4oKQogIFg9bGlzdCgKICAgIGhlYWRjb250PWMoImZlbWhlYWQiLCAiYWdlaGVhZCIpLAogICAgaGhjb250PWMoImhoc2l6ZSIsICJoaHNpemVzcSIsICJlZHVjYXZlIiksCiAgICB3ZWFsdGg9YygibGFuZG93biIsICJ0bHVfdG90YWwiLCAiYWd3ZWFsdGhfcGFyYW4iLCAibm9uYWd3ZWFsdGhfcGFyYW4iKSwgIyAiZWxlY3RyaWNpdHkiCiAgICBiaW89YygiVFQyMGtfaG91cnMiLCAiZWxldmF0aW9uIiwgImxncCIpLCAjICJmYXIiLCAibWVhbl9wb3BkZW4yMDAwIiIKICAgIGhlYWx0aD1jKCJtYWxhcmlhaW4iKQogICkKKQoKcGFuZWxzLmhoIDwtIGhoWyhJU08zPT0iVFpBIiAmIHN1cnZleSAlaW4lIHBhbmVscyRUWkEpIHwgKElTTzM9PSJVR0EiICYgc3VydmV5ICVpbiUgcGFuZWxzJFVHQSldCnJtKGhoKQoKYGBgCgojIyBCYWxhbmNlIHN1cnZleSBwYW5lbHMKCgpgYGB7cn0KCiMgVGFsbHkgb2JzIGFjcm9zcyBwYW5lbHMKdG1wIDwtIHBhbmVscy5oaFssIC5OLCBrZXlieT0uKElTTzMsIHJlZ2lvbj1yZWdpb25uYW1lLCBwYW5lbD1zdXJ2ZXkpXQp0bXAgPC0gdG1wWywgbGFwcGx5KC5TRCwgcGFzdGUsIGNvbGxhcHNlPSIsICIpLCAuU0Rjb2xzPWMoIk4iLCAicGFuZWwiKSwga2V5Ynk9LihJU08zLCByZWdpb24pXQpwYW5kZXIodG1wLCBjYXB0aW9uPSJPYnMuIGFjcm9zcyBwYW5lbHMiKQoKYGBgCgpgYGB7ciAsIGV2YWw9Rn0KCiMgVE9ETyBBcHBlbmQgWCxZIGNvb3JkaW5hdGVzCiMgVmVyaWZ5IHVuaXF1ZSByZWNvcmRzIGZpcnN0CnNldGtleShwYW5lbHMuaGgsIElTTzMsIHN1cnZleSwgcm91bmQsIGNsdXN0ZXIsIGhoaWQpCnBhbmVscy5oaFtkdXBsaWNhdGVkKHBhbmVscy5oaCksIHVuaXF1ZShoaGlkKV0KCiMgQmFsYW5jZSBwYW5lbHMgKHdlIG5lZWQgc3RhYmxlIGNvdW50IG9mIG9icy4gYWNyb3NzIHN1cnZleSByb3VuZHMpCnBhbmRlcihwYW5lbHMuaGhbYmFsYW5jZWQ9PTEsIC5OLCBrZXlieT0uKElTTzMsIHN1cnZleSwgc3Z5Q29kZSldLCBjYXB0aW9uPSJPYnMuIGFjcm9zcyBwYW5lbHMsIGJhbGFuY2VkIikKCgojIFN1bW1hcml6ZSBhbmQgZ3JhcGgvbWFwIHJlZ3Jlc3NvciB2YXJzLCBjaGVjayBmb3IgbWlzc2luZyBkYXRhCgoKCmBgYAoKCiMjIFN1bW1hcml6ZSBiaW9waHlzaWNhbCBjb25kaXRpb25zIGFuZCBzaG9ja3MKCmBgYHtyLCBldmFsPUZ9CgojIFRyeSB0byBtZXJnZSBmZWF0dXJlcyBhbmQgYXR0cmlidXRlcyBhY3Jvc3MgcGFuZWxzCgojIFVzaW5nIFNQRUksIFVERUwsIGFuZCBDSElSUFMsIG1hcCBza2V3bmVzcyBvZiBkaXN0cmlidXRpb24KCgojIEFkZCBkcm91Z2h0IG1lZGlhbiBkdXJhdGlvbgoKCiMgTWFwIGJpbyBzaG9jayB2YXJpYWJsZXMKCgpgYGAKCiMjIFRlc3QgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uCgpgYGB7cn0KCiMgTW9yYW4gSSB0ZXN0IG9uIGFsbCByZWdyZXNzb3JzIGFuZCBvdXRjb21lIHZhcmlhYmxlcwoKIyBDb2xsZWN0IHJlc3VsdHMKCgpgYGAKCgojIEdlbmVyYXRlIHNwYXRpYWwgd2VpZ2h0cwoKVGhlIExTTVMtSVNBIHBhbmVscyBwcm92aWRlIEdQUyBjb29yZHMgZm9yIGFsbCBob3VzZWhvbGRzLiBJbnR1aXRpdmVseSB3ZSBjb3VsZCB0cnkgYSBmZXcgZGlmZmVyZW50IGFwcHJvYWNoZXM6CgoqICRXXzEkIGNyZWF0ZSBzcGF0aWFsIHdlaWdodHMgdXNpbmcgay1uZWFyZXN0IG5laWdoYm9ycyB3aXRoaW4gYSBzcGVjaWZpYyBkaXN0YW5jZSB0aHJlc2hvbGQgKG9yIGVudW1lcmF0aW9uIGFyZWEsIG9yIG1hcmtldHNoZWQsIG9yIGFncm8tZWNvbG9naWNhbCB6b25lLCBvciByZWdpb24vcHJvdmluY2UpIAoqICRXXzIkIHVzZSAqaW52ZXJzZSB0cmF2ZWwgdGltZXMqIHdlaWdodHMgKHBvc3NpYmx5IHdpdGhpbiBhIGRpc3RhbmNlIG9yIHRyYXZlbCB0aW1lIHRocmVzaG9sZCkgCiogJFdfMyQgc2ltcGx5IHVzZSBpbnZlcnNlIGRpc3RhbmNlIHdlaWdodHMgYmV0d2VlbiBob3VzZWhvbGRzLgoKJFdfMSQgc2VlbXMgbW9yZSBjb21tb24gaW4gdGhlIHNvY2lvLWVjb25vbWljIGxpdGVyYXR1cmUuICRXXzMkIGlzIG1vcmUgc3RyYWlnaHRmb3J3YXJkIGJ1dCBFdWNsaWRpYW4gZGlzdGFuY2VzIGNvdWxkIGJlIG1pc2xlYWRpbmcuICRXXzIkIHJlcXVpcmVzIG1vcmUgd29yayAoZS5nLiBjYW4gd2UgdXNlICpHb29nbGUgTWFwcyBEaXN0YW5jZSBNYXRyaXggQVBJKiB0byBjb21wdXRlIHJlYWxpc3RpYyB0cmF2ZWwgdGltZXMgYmV0d2VlbiBhbGwgaG91c2Vob2xkIGxvY2F0aW9ucz8KCgpgYGB7ciB3MSwgZXZhbD1GfQoKIyBHZW5lcmF0ZSBOeE4gdHJhdmVsIHRpbWUgbWF0cml4IHVzaW5nIEdvb2dsZSBEaXN0YW5jZSBBUEkKCiMgQVBJIHF1b3RhcwojIGVudHJpZXMgcGVyIGRheTogMiw1MDAJCiMgcmVxdWVzdHMgcGVyIDEwMCBzZWNvbmRzOiAxMCwwMDAJCiMgcmVxdWVzdHMgcGVyIDEwMCBzZWNvbmRzIHBlciB1c2VyOiBVbmxpbWl0ZWQKIyByZXN1bHRzIGFyZSBpbiBzZWNvbmRzCgojIEhpdCB0aGUgQVBJIHN1cnZleSBieSBzdXJ2ZXksIHN0YXJ0IHdpdGggbW9zdCByZWNlbnQgcGFuZWwKaSA8LSBzdnlbM10KdG1wIDwtIGRhdGEudGFibGUoZ3BzLnB0c0BkYXRhW2dwcy5wdHMkc3Z5Q29kZT09aSxdKQojIEluaXQgbWF0cml4CnBhbmVscy50dCA8LSBtYXRyaXgoYXMubnVtZXJpYyhOQSksIG5yb3c9bnJvdyh0bXApLCBuY29sPW5yb3codG1wKSkKZGltbmFtZXMocGFuZWxzLnR0KSA8LSBjKHRtcCRoaGlkLCB0bXAkaGhpZCkKCiMgSGVscGVyIC0gR29vZ2xlIG1hcHMgZGlzdGFuY2UgbWF0cml4IEFQSQpnbWFwc2FwaSA8LSBmdW5jdGlvbihvLCBkKSB7CiAgcmVxdWlyZShodHRyKQogIHVybCA8LSAiaHR0cHM6Ly9tYXBzLmdvb2dsZWFwaXMuY29tL21hcHMvYXBpL2Rpc3RhbmNlbWF0cml4L2pzb24iCiAgb3V0IDwtIEdFVCh1cmwsIHF1ZXJ5PWxpc3Qob3JpZ2lucz1vLCBkZXN0aW5hdGlvbnM9ZCwgbW9kZT0iZHJpdmluZyIsIGtleT1hcGlfa2V5KSkKICBvdXQgPC0ganNvbmxpdGU6OmZyb21KU09OKGNvbnRlbnQob3V0LCBhcz0idGV4dCIpKQogIHJldHVybihvdXQkcm93cyRlbGVtZW50c1tbMV1dJGR1cmF0aW9uJHZhbHVlKQp9CgojIEhpdCB0aGUgQVBJIGFuZCBjb2xsZWN0IHJlc3BvbnNlcwpmb3IgKGkgaW4gMTpucm93KHRtcClbMV0pIHsKICBOIDwtIHNlcShpLCBucm93KHRtcCksIDIwMCkKICBmb3IgKGogaW4gMTpsZW5ndGgoTikpIHsKICAgIGYgPC0gTltqXQogICAgdCA8LSBOW2orMV0tMQogICAgcGFuZWxzLnR0W2ksIGY6dF0gPC0gZ21hcHNhcGkoCiAgICAgIHRtcFtpLCBwYXN0ZShZX21vZCwgWF9tb2QsIHNlcD0iLCIpXSwKICAgICAgdG1wW2Y6dCwgcGFzdGUoWV9tb2QsIFhfbW9kLCBzZXA9IiwiLCBjb2xsYXBzZT0ifCIpXSkKICB9Cn0KIz0+IHdvcmtzIGJ1dCByZXR1cm5zIHF1aXRlIGEgZmV3IE5BcwoKIyBNYXAgcmVzdWx0cwpsIDwtIGRhdGEudGFibGUodG1wWzEsIFhfbW9kXSwgdG1wWzEsIFlfbW9kXSwgdG1wWy0xLCBYX21vZF0sIHRtcFstMSxZX21vZF0pCmwgPC0gbGFwcGx5KDE6bnJvdyhsKSwgZnVuY3Rpb24oaSkgbGlzdChMPUxpbmUobWF0cml4KHVubGlzdChsW2ksXSksIG5jb2w9MiwgYnlyb3c9VCkpLCBpPWkpKQpsIDwtIFNwYXRpYWxMaW5lcyhsYXBwbHkobCwgZnVuY3Rpb24oRSkgTGluZXMobGlzdChFJEwpLGFzLmNoYXJhY3RlcihFJGkpKSksCiAgcHJvajRzdHJpbmc9Q1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKbCA8LSBTcGF0aWFsTGluZXNEYXRhRnJhbWUobCwgZGF0YS5mcmFtZSh0aW1lPXBhbmVscy50dFsxLC0xXS8oNjAqNjApKSkKCmBgYAoKYGBge3J9Cgp0bV9zaGFwZShXb3JsZCkgKyB0bV9wb2x5Z29ucygpICsKICB0bV9zaGFwZShsWzE6MjI5NyxdLCBpcy5tYXN0ZXI9VCkgKyAKICB0bV9saW5lcyhjb2w9InRpbWUiLCBjb2xvck5BPSJ3aGl0ZSIsIGx3ZD0uNCwgYWxwaGE9LjQsIHRpdGxlLmNvbD0idHJhdmVsIChob3VycykiKSArCiAgdG1fc3R5bGVfZ3JleSgpCgpgYGAKCkdvb2dsZSBBUEkgd29ya3MgYnV0IHJldHVybnMgYSBsb3Qgb2YgbWlzc2luZyB2YWx1ZXMgYmV0d2VlbiBwYWlycyBvZiBsb2NhdGlvbnMsIHNlZW1zIGRpZmZpY3VsdCB0byBhdm9pZC4gQW5vdGhlciBhcHByb2FjaCBpcyB0byB1c2UgYW4gYXJiaXRyYXJ5IGRpc3RhbmNlIHRvIGRlZmluZSBuZWlnaGJvcnMgKGUuZy4gNTBrbSkuCgoKYGBge3IsIGV2YWw9Rn0KCiMgR2VuZXJhdGUgc3BhdGlhbCBuZWlnaGJvdXIgbGlzdApuYjEgPC0ga25uMm5iKGtuZWFybmVpZ2goY29vcmRzLCBrPTUwKSwgcm93Lm5hbWVzPXBhc3RlKGcyLm5iJElTTzMsIGcyLm5iJHJuLCBzZXA9Ii4iKSkKc3VtbWFyeShuYjEpCgojIEludmVyc2UgZGlzdGFuY2UgbWF0cml4CncxIDwtIGxhcHBseShuYjEsIGZ1bmN0aW9uKHgpIDEvKHgvMTAwMCkpCncxIDwtIG5iMmxpc3R3KG5iMSwgZ2xpc3Q9dzEsIHN0eWxlPSJCIikKCnN1bW1hcnkodzEkd2VpZ2h0cykKc3VtbWFyeShzYXBwbHkodzEkd2VpZ2h0cywgc3VtKSkKCiMgR2VuZXJhdGUgc3BhdGlhbCBuZWlnaGJvdXIgbGlzdApuYjIgPC0gcG9seTJuYihnMi5uYiwgcm93Lm5hbWVzPXBhc3RlKGcyLm5iJElTTzMsIGcyLm5iJHJuLCBzZXA9Ii4iKSkKc3VtbWFyeShuYjIpCgojIERpc3RhbmNlIG1hdHJpeAp3MiA8LSBuYjJtYXQobmIyLCBzdHlsZT0iVyIsIHplcm8ucG9saWN5PVQpCgoKCgoKYGBgCgoKCgojIFNwYXRpYWwgcGFuZWwgcmVncmVzc2lvbnMgZm9yIFRhbnphbmlhIGFuZCBVZ2FuZGEgKE5QUyBwYW5lbHMpCgpJbiBzaG9ydDoKCjEpIGBzcGZtbGAgY29tbWFuZCBlc3RpbWF0ZXMgYm90aCAqKmZpeGVkIGVmZmVjdHMqKiBzcGF0aWFsIGxhZyBhbmQgKiplcnJvciBtb2RlbHMqKiAod2l0aCBkaWZmZXJlbnQgbWV0aG9kcyB0byBjYWxjdWxhdGUgdGhlIGRldGVybWluYW50cywgZGVmYXVsdCBgZWlnZW5gKSBhbmQgZGlmZmVyZW50IGVmZmVjdHM6CiAgICAqIGBwb29sZWRgIGNvbnN0YW50IHRlcm0gb25seQogICAgKiBgc3BmZWAgY3Jvc3Mtc2VjdGlvbmFsIHNwZWNpZmljIGVmZmVjdHMKICAgICogYHRwZmVgIHRpbWUtcGVyaW9kIHNwZWNpZmljIGVmZmVjdHMKICAgICogYHNwdHBmZWAgaW5jbHVkZSBib3RoIHNwYXRpYWwgYW5kIHRlbXBvcmFsIGZpeGVkIGVmZmVjdHMKICAgIApgc3BmbWxgIHJldHVybnMgcmVzaWR1YWxzLCBhIHRhYmxlIG9mIGVzdGltYXRlZCBjb2VmZmljaWVudHMgKGByaG9gIGlzIHRoZSBjb2VmZiBvZiB0aGUgc3BhdGlhbGx5IGxhZ2dlZCBkZXBlbmRlbnQgdmFyaWFibGUpLiBGaXhlZCBlZmZlY3RzIGNhbiBiZSBleHRyYWN0ZWQgdXNpbmcgYGVmZmVjdHMocmVzKWAgdGhhdCByZXR1cm5zIHRoZSB0eXBlIG9mIGVmZmVjdHMgd2l0aCBzaWduaWZpY2FuY2UgbGV2ZWxzIGFuZCB0aGUgY29uc3RhbnQgdGVybS4KCjIpIGBzcHJlbWxgIGNvbW1hbmQgaXMgdXNlZCB0byBnZW5lcmF0ZSAqKnNwYXRpYWwgcmFuZG9tIGVmZmVjdHMqKiB3aXRoIHNldmVyYWwgZXJyb3Igb3B0aW9uczoKICAgICogYHNlbXNycmVgIGZ1bGwgKG1vc3QgZ2VuZXJhbCkgbW9kZWwKICAgICogYHNlbXNyYCBzZXJpYWxseSBhbmQgc3BhdGlhbGx5IGNvcnJlbGF0ZWQgZGlzdHVyYmFuY2VzLCBubyByYW5kb20gZWZmZWN0cwogICAgKiBgc3JyZWAgc2VyaWFsIGNvcnJlbGF0aW9uIGFuZCByYW5kb20gZWZmZWN0cwogICAgKiBgc2VtcmVgIGV4Y2x1ZGUgc2VyaWFsIGNvcnJlbGF0aW9uCiAgICAqIGByZWB0cmFkaXRpb25hbCByYW5kb20gZWZmZWN0cyBtb2RlbAogICAgKiBgc3JgIHBhbmVsIHJlZ3Jlc3Npb24gd2l0aCBzZXJpYWxseSBjb3JyZWxhdGVkIGVycm9ycwogICAgKiBgc2VtYCBwb29sZWQgbW9kZWwgd2l0aCBzcGF0aWFsbHkgYXV0b2NvcnJlbGF0ZWQgcmVzaWR1YWxzCiAgICAKMykgQ29tbWFuZCBgYnNrdGVzdGAgaXMgdXNlZCB0byB0ZXN0IGZvciByYW5kb20gZWZmZWN0cyBhbmQgc3BhdGlhbCBlcnJvciBjb3JyZWxhdGlvbiAoZS5nLiBCYWx0YWdpLCBTb25nIGFuZCBLb2ggU0xNMSBtYXJnaW5hbCB0ZXN0KS4KCjQpIENvbW1hbmQgYGJzamt0ZXN0YCBpcyB1c2VkIHRvIHRlc3QgZm9yIGpvaW50LCBtYXJnaW5hbCBhbmQgY29uZGl0aW9uYWwgdGVzdHMgZm9yIHJhbmRvbSBlZmZlY3RzLCBzZXJpYWwgYW5kIHNwYXRpYWwgZXJyb3IgY29ycmVsYXRpb24gKHRha2VzIGEgbW9kZWwgZm9ybXVsYSBhcyBpbnB1dCkuCiAKICAgCiAgIAojIyBNb2RlbCBzcGVjaWZpY2F0aW9ucwoKCmBgYHtyfQoKIyBTdWJzYW1wbGUgdG8gInJ1cmFsICE9IDAgJiBhZ3JpX2hoICE9IDAiCgojIFN0YXJ0IGl0ZXJhdGluZyBhY3Jvc3MgbW9kZWwgc3BlY2ZpY2F0aW9ucwoKIyBDb2xsZWN0IHJlc3VsdHMKCmBgYAoKCgogICAgCiMgUG9vbGVkIHJlZ3Jlc3Npb25zIGZvciBHaGFuYSwgVGFuemFuaWEsIFVnYW5kYSBjcm9zcy1zZWN0aW9ucwogICAgCgogICAgCiAgICAKYGBge3IgZXZhbD1GfQoKCiAgCmBgYAogICAgCiAgICAKICAgIAogICAgCiAgICAKCgpgYGB7ciBldmFsPUZ9CgojIFNhdmUgc25hcHNob3QKcm0odG1wLCBSaWNlRmFybXMsIHJpY2V3dywgeCwgaSwgaikKc2F2ZS5pbWFnZSgiLi90bXAvcmVzaWxpZW5jZS5SRGF0YSIpCgpgYGAKCgojIFJlZmVyZW5jZXMKClteYml2YW5kMjAwOF06IEJpdmFuZCwgUi5TLiwgUGViZXNtYSwgRS5qLiwgYW5kIEdvbWV6LVJ1YmlvLCBWLiAoMjAwOCkgIkFwcGxpZWQgU3BhdGlhbCBBbmFseXNpcyB3aXRoIFIiLiBTcHJpbmdlci4gT25saW5lIGF0IGh0dHA6Ly9naXMuaHVtYm9sZHQuZWR1L09MTS9yL1NwYXRpYWwlMjBBbmFseXNpcyUyMFdpdGglMjBSLnBkZgoKW15taWxsbzIwMDldOiBNaWxsbywgRyBhbmQgUGlyYXMsIEcuICgyMDA5KSAic3BsbTogU3BhdGlhbCBQYW5lbCBkYXRhIG1vZGVscyBpbiBSIiwgSm91cm5hbCBvZiBTdGF0aXN0aWNhbCBTb2Z0d2FyZSwgVm9sLiBWViwgSXNzdWUgSUkuIE9ubGluZSBhdCBodHRwOi8vZmFjd2ViLmtub3dsdG9uLm9oaW8tc3RhdGUuZWR1L3B2aXRvbi9jb3Vyc2VzL2NycDg3MTA1L21pbGxvLXBpcmFzLnBkZgoKW15taWxsbzIwMTNdOiBNaWxsbywgRy4gKDIwMTMpICJNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiBvZiBzcGF0aWFsbHkgYW5kIHNlcmlhbGx5CmNvcnJlbGF0ZWQgcGFuZWxzIHdpdGggcmFuZG9tIGVmZmVjdHMiLCBFbHNldmllciBwcmVwcmludC4gT25saW5lIGF0IGh0dHBzOi8vcGRmcy5zZW1hbnRpY3NjaG9sYXIub3JnL2VlYmQvZTI4NTJmYjI3M2NkMGYxMGI5ZDEwMDA5NzQ4YzBlZTY4OTA2LnBkZgoKW15lbGhvcnN0MjAxNF06IEVsaG9yc3QsIEouUC4gKDIwMTQpIFNwYXRpYWwgUGFuZWwgRGF0YSBNb2RlbHMuIENoYXB0ZXIgQy4yIGluICJTcGF0aWFsIEVjb25vbWV0cmljcyBmcm9tIENyb3NzLVNlY3Rpb25hbCBEYXRhIHRvIFNwYXRpYWwgUGFuZWxzIiBTcGluZ2VyLCAyMDE0LiBVbml2ZXJzaXR5IG9mIEdyb25pbmdlbi4gT24tbGluZSBhdCBodHRwOi8vcmVncm9uaW5nZW4ubmwvZWxob3JzdC9kb2MvU3BhdGlhbCUyMFBhbmVsJTIwRGF0YSUyME1vZGVscy5wZGYKCltebWF0eWFzMjAxNl06IE1hdHlhcyAsIEwuICgyMDE2KSAiVGhlIEVjb25vbWV0cmljcyBvZiBNdWx0aS1kaW1lbnNpb25hbCBQYW5lbHMgLSBUaGVvcnkgYW5kIEFwcGxpY2F0aW9uIiwgU3ByaW5nZXIuIE9ubGluZSBhdCBodHRwOi8vd3d3Lm1ldHJpeG1kcC5ldS8KCltebmlzdG9yMjAwN106IE5pc3RvciwgQS5QLiAoMjAwNykgIlRoZSBJbXBhY3Qgb2YgQ29udHJvbGxlZCBEcmFpbmFnZSBvbiBBZ3JpY3VsdHVyYWwgWWllbHM6IEEgU3BhdGlhbCBQYW5lbCBNb2RlbCB1c2luZyBZaWVsZCBNb25pdG9yIERhdGEiLCBQaC5ELiBEaXNzZXJ0YXRpb24sIFB1cmR1ZSBVbml2ZXJzaXR5LiBPbmxpbmUgYXQgaHR0cHM6Ly9ib29rcy5nb29nbGUuY29tL2Jvb2tzP2lkPWRaQTJLdmtaekU0QyZscGc9UEExMjImb3RzPUNKRE91WmpwWE8mZHE9c3BhdGlhbCUyMHBhbmVscyUyMHVuYmFsYW5jZWQmcGc9UFAxI3Y9b25lcGFnZSZxPXNwYXRpYWwlMjBwYW5lbHMlMjB1bmJhbGFuY2VkJmY9ZmFsc2UKClted2IyMDA4XTogRWxiZXJzLCBDLiwgTGFuam91dywgUC4gYW5kIExlaXRlLCBQLkcuICgyMDA4KSAiQnJhemlsIHdpdGhpbiBCcmF6aWw6IFRlc3RpbmcgdGhlIFBvdmVydHkgTWFwIE1ldGhvZG9sb2d5IGluIE1pbmFzIEdlcmFpcyIsIFdvcmxkIEJhbmsgUG9saWN5IFJlc2VhcmNoIFdvcmtpbmcgUGFwZXIgIzQ1MTMuIE9ubGluZSBhdCBodHRwOi8vZG9jdW1lbnRzLndvcmxkYmFuay5vcmcvY3VyYXRlZC9lbi85NDE0MDE0NjgyMzE4OTM1NjgvcGRmL3dwczQ1MTMucGRmCgo=