Probabilistic Analysis: Simple Universal Kriging

Homework #5


Markdown Author: Jessie Bell

Download this Rmd: Top right corner → Code → Download Rmd

sf to rast conversion function:

# use the homemade function:
sf_2_rast <-function(sfObject,variableIndex = 1){
  # coerce sf to a data.frame
  dfObject <- data.frame(st_coordinates(sfObject),
                         z=as.data.frame(sfObject)[,variableIndex])
  # coerce data.frame to SpatRaster
  rastObject <- rast(dfObject,crs=crs(sfObject))
  
  names(rastObject) <- names(sfObject)[variableIndex]
  
  return(rastObject)
}

🌴 California Precipitation

1 | Data Read

Read in and convert the data, then plot it to get a grasp on what is there:

#read in your data
prcpCA <- readRDS("prcpCA.rds")
#now the empty grid to interpolate the datad
gridCA <- readRDS("gridCA.rds")

#set the CRS and convert foreign object to SF

prcpCAsf <- prcpCA %>% st_as_sf(coords = c("X", "Y")) %>%
  st_set_crs(value = 3310)


gridCA_sf <- st_as_sf(gridCA, 
                       coords=c("X", "Y"), 
                       crs=st_crs(prcpCAsf))



# plot it
prcpCAsf %>% ggplot() + 
  geom_sf(aes(fill=ANNUAL,size=ANNUAL),color="white",
          shape=21,alpha=0.8) + 
  scale_fill_continuous(type = "viridis",name="mm") + 
  labs(title="Total Annual Precipitation") +
  scale_size(guide="none")



2 | Variogram

Use Kriging to create a surface of the total annual precip in Cali. Fit the model using the variogram, vgm and fit.variogram like the tutorial did.

#plot the variogram
CAVar <- variogram(ANNUAL~1, prcpCAsf)
plot(CAVar, pch=20, cex=1.5, col="black", 
     ylab=expression("Semivariance ("*gamma*")"), xlab="Distance (m)", main="CA Annual Precipitation (mm)")

Range: ~100,000 m

Nugget: \(\gamma\) = 1000

Sill: \(\gamma\) = 110,000



3 | Exponential Model

Create an Exponential Model using the variogram (see step 4.5 as to why I chose Exp).

exp.model <- vgm(psill=158188, model="Exp", range=89112, nugget=0)
exp.fit <- fit.variogram(object = CAVar, model = exp.model)

exp.fit
##   model    psill    range
## 1   Nug      0.0     0.00
## 2   Exp 156446.9 87158.59


4 | Theoretical Variogram

Create a theoretical variogram.

plot(CAVar,model=exp.fit,pch=20,cex=1.5,col="black",
     ylab=expression("Semivariance ("*gamma*")"),
     xlab="Distance (m)", main = "Annual Precipitation (mm)",
     sub="Points: Empirical, Line: Exponential Model")

Above you can see our observed (empirical/categorical) data alongside a fitted continuous model.



4.5 | Steps 2-4 FAST

Do the last 3 steps faster with autofitVariogram, check each model shape to find the best fit:

⭐ Exp ⭐

#The exponential Model
CAVarAuto <- autofitVariogram(formula = ANNUAL~1,input_data = prcpCAsf, model="Exp")

plot(CAVarAuto)

The best model fit seems to be the Exponential Model (Exp), so I will plug the terms into the Exp Equation:

b = Nugget

C\(_0\) = Sill

d is the distance in question

r = Range

a = \(\frac{r}{3}\) = 29704

\(\hat{\gamma}(d)= 0+158188(1-e^{-d/29704})\)



Ste

#Checking all model types:

#the Matern, M. Stein's parameterization (Ste)
CAVarAuto <- autofitVariogram(formula = ANNUAL~1,input_data = prcpCAsf, model="Ste")

plot(CAVarAuto)



Mat

#The exponential Model
CAVarAuto <- autofitVariogram(formula = ANNUAL~1,input_data = prcpCAsf, model="Mat")

plot(CAVarAuto)



Gau

#The Gaussian Model
CAVarAuto <- autofitVariogram(formula = ANNUAL~1,input_data = prcpCAsf, model="Gau")

plot(CAVarAuto)



Sph

##### sph
CAVarAuto <- autofitVariogram(formula = ANNUAL~1,input_data = prcpCAsf, model="Sph")

plot(CAVarAuto)



5 | Kriging

Krige the data.

CAGstat <- gstat(formula = ANNUAL~1, locations = prcpCAsf, 
                   model = exp.fit)

CAKrige_sf <- predict(CAGstat, newdata=gridCA_sf)
## [using ordinary kriging]


6 | Convert to Raster

Convert sf to rast using homemade function sf_2_rast.

CAKrige_rast <- sf_2_rast(CAKrige_sf)


7 | Plot Prediction

Plot the prediction surface.

ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.pred),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="Precip. (mm)",na.value = "transparent") + 
  labs(title="CA Annual Precipitation (Exp)") +
  theme_minimal()



8 | Plot Variance

Check out the variance around the predictions and plot it.

CAKrige_sf$var1.var.sqrt <- sqrt(CAKrige_sf$var1.var)

CAKrige_rast <- sf_2_rast(CAKrige_sf,variableIndex = 4)

ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.var.sqrt ),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="precip.(mm)",na.value = "transparent") + 
  labs(title="Variance of annual precip. (Exp)") +
  theme_minimal()



9 | Cross-Validate

Cross-validate using krige.cv and calculate \(R^2\).

#cross validate with LOOCV
ExpKrigeLOOCV_sf <- krige.cv(formula = ANNUAL~1, 
                           locations = prcpCAsf, 
                           model = exp.fit, verbose = FALSE)


##R^2 for exp model
Exp_r2_LOOCV <- cor(ExpKrigeLOOCV_sf$observed, ExpKrigeLOOCV_sf$var1.pred)^2

# RMSE
Exp_rmse_LOOCV <-sqrt(mean(ExpKrigeLOOCV_sf$residual^2))

#cross validate with nfold
ExpKrigenfold_sf <- krige.cv(formula = ANNUAL~1,
                            nfold=10,
                           locations = prcpCAsf, 
                           model = exp.fit, verbose = FALSE)

##R^2 for ste model
Exp_r2_nfold <- cor(ExpKrigenfold_sf$observed, ExpKrigenfold_sf$var1.pred)^2

# RMSE
Exp_rmse_nfold <- sqrt(mean(ExpKrigenfold_sf$residual^2))

Model5 <- c("Exponential")

#make a nice table

stats1 <- as.data.frame(cbind(Model5, Exp_r2_LOOCV, Exp_rmse_LOOCV, Exp_r2_nfold, Exp_rmse_nfold))
stats1 <- type.convert(stats1, as.is = TRUE) #makes it so that numeric stays numeric and characters stay as characters in df


colnames(stats1) <- c("Model", "R-Sq LOOCV","RMSE LOOCV", "R-Sq 10fold", "RMSE nfold") #rename columns


gt(stats1)|>
  tab_options(table.font.size = px(10L))%>%
  opt_table_font("Corbel")%>%
  opt_stylize(style=4)%>%
  fmt_number(columns = where(is.numeric), n_sigfig = 3, drop_trailing_zeros = T)
Model R-Sq LOOCV RMSE LOOCV R-Sq 10fold RMSE nfold
Exponential 0.902 138 0.890 147

The LOOCV \(R^2\) is 0.9020, that seems pretty, pretty good! The RMSE is 138, which is lower than the RMSE for an nfold of 10. I think this is because our precipitation dataset is small, and so using LOOCV works better here.



10 | Tune

Tune and assess the surface fit as you like. Interpret and explain your surface and anything you notice in particular about it.

Please see interpretation in step 11 below.



Ste/Mat

#create the model
ste.model <- vgm(psill=150643, model="Ste", range=104784, nugget=0)
ste.fit <- fit.variogram(object = CAVar, model = ste.model)

#Krige the data
CAGstat <- gstat(formula = ANNUAL~1, locations = prcpCAsf, 
                   model = ste.fit)

CAKrige_sf <- predict(CAGstat, newdata=gridCA_sf)
## [using ordinary kriging]
#convert to raster
CAKrige_rast <- sf_2_rast(CAKrige_sf)

#plot prediction
a <- ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.pred),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="Precip. (mm)",na.value = "transparent") + 
  labs(title="CA Annual Precipitation (Ste)") +
  theme_minimal()

#plot variance
CAKrige_sf$var1.var.sqrt <- sqrt(CAKrige_sf$var1.var)

CAKrige_rast <- sf_2_rast(CAKrige_sf,variableIndex = 4)

b <- ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.var.sqrt ),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="precip.(mm)",na.value = "transparent") + 
  labs(title="Variance of annual precip. (Ste)") +
  theme_minimal()

ggarrange(a,b)

#cross validate with LOOCV
SteKrigeLOOCV_sf <- krige.cv(formula = ANNUAL~1, 
                           locations = prcpCAsf, 
                           model = ste.fit, verbose = FALSE)


##R^2 for ste model
SteR2LOOCV <- cor(SteKrigeLOOCV_sf$observed, SteKrigeLOOCV_sf$var1.pred)^2

# RMSE
StermseLOOCV <- sqrt(mean(SteKrigeLOOCV_sf$residual^2))

#cross validate with nfold
SteKrigenfold_sf <- krige.cv(formula = ANNUAL~1,
                            nfold=10,
                           locations = prcpCAsf, 
                           model = ste.fit, verbose = FALSE)

##R^2 for ste model
Ster2nfold <- cor(SteKrigenfold_sf$observed, SteKrigenfold_sf$var1.pred)^2

# RMSE
Stermsenfold <- sqrt(mean(SteKrigenfold_sf$residual^2))

Model2 <- c("M. Stein's Parameterization")

#make a nice table

stats2 <- as.data.frame(cbind(Model2, SteR2LOOCV, StermseLOOCV, Ster2nfold, Stermsenfold))
stats2 <- type.convert(stats2, as.is = TRUE) #makes it so that numeric stays numeric and characters stay as characters in df


colnames(stats2) <- c("Model", "R-Sq LOOCV","RMSE LOOCV", "R-Sq 10fold", "RMSE nfold") #rename columns


gt(stats2)|>
  tab_options(table.font.size = px(10L))%>%
  opt_table_font("Corbel")%>%
  opt_stylize(style=4)%>%
  fmt_number(columns = where(is.numeric), n_sigfig = 3, drop_trailing_zeros = T)
Model R-Sq LOOCV RMSE LOOCV R-Sq 10fold RMSE nfold
M. Stein's Parameterization 0.902 138 0.888 148

Gau

#create the model
gau.model <- vgm(psill=125428, model="Gau", range=54097, nugget=9733)
gau.fit <- fit.variogram(object = CAVar, model = gau.model)

#Krige the data
CAGstat <- gstat(formula = ANNUAL~1, locations = prcpCAsf, 
                   model = gau.fit)

CAKrige_sf <- predict(CAGstat, newdata=gridCA_sf)
## [using ordinary kriging]
#convert to raster
CAKrige_rast <- sf_2_rast(CAKrige_sf)

#plot prediction
a <- ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.pred),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="Precip. (mm)",na.value = "transparent") + 
  labs(title="CA Annual Precipitation (Gau)") +
  theme_minimal()

#plot variance
CAKrige_sf$var1.var.sqrt <- sqrt(CAKrige_sf$var1.var)

CAKrige_rast <- sf_2_rast(CAKrige_sf,variableIndex = 4)

b <- ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.var.sqrt ),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="precip.(mm)",na.value = "transparent") + 
  labs(title="Variance of annual precip. (Gau)") +
  theme_minimal()

ggarrange(a,b)

#cross validate with LOOCV
GauKrigeLOOCV_sf <- krige.cv(formula = ANNUAL~1, 
                           locations = prcpCAsf, 
                           model = gau.fit, verbose = FALSE)


##R^2 for ste model
Gaur2a <- cor(GauKrigeLOOCV_sf$observed, GauKrigeLOOCV_sf$var1.pred)^2

# RMSE
GauRMSEb <- sqrt(mean(GauKrigeLOOCV_sf$residual^2))

#cross validate with nfold
GauKrigenfold_sf <- krige.cv(formula = ANNUAL~1,
                            nfold=10,
                           locations = prcpCAsf, 
                           model = gau.fit, verbose = FALSE)

##R^2 for ste model
Gaur2b <- cor(GauKrigenfold_sf$observed, GauKrigenfold_sf$var1.pred)^2

# RMSE
Gaurmseb <- sqrt(mean(GauKrigenfold_sf$residual^2))


Model3 <- c("Gaussian")

#make a nice table

stats3 <- as.data.frame(cbind(Model3, Gaur2a, GauRMSEb, Gaur2b, Gaurmseb))
stats3 <- type.convert(stats3, as.is = TRUE) #makes it so that numeric stays numeric and characters stay as characters in df


colnames(stats3) <- c("Model", "R-Sq LOOCV","RMSE LOOCV", "R-Sq 10fold", "RMSE nfold") #rename columns


gt(stats3)|>
  tab_options(table.font.size = px(10L))%>%
  opt_table_font("Corbel")%>%
  opt_stylize(style=4)%>%
  fmt_number(columns = where(is.numeric), n_sigfig = 3, drop_trailing_zeros = T)
Model R-Sq LOOCV RMSE LOOCV R-Sq 10fold RMSE nfold
Gaussian 0.869 160 0.865 162


Sph

#create the model
sph.model <- vgm(psill=135302, model="Sph", range=144746, nugget=630)
sph.fit <- fit.variogram(object = CAVar, model = sph.model)

#Krige the data
CAGstat <- gstat(formula = ANNUAL~1, locations = prcpCAsf, 
                   model = sph.fit)

CAKrige_sf <- predict(CAGstat, newdata=gridCA_sf)
## [using ordinary kriging]
#convert to raster
CAKrige_rast <- sf_2_rast(CAKrige_sf)

#plot prediction
a <- ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.pred),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="Precip. (mm)",na.value = "transparent") + 
  labs(title="CA Annual Precipitation (Sph)") +
  theme_minimal()

#plot variance
CAKrige_sf$var1.var.sqrt <- sqrt(CAKrige_sf$var1.var)

CAKrige_rast <- sf_2_rast(CAKrige_sf,variableIndex = 4)

b <- ggplot() +
  geom_spatraster(data=CAKrige_rast, mapping = aes(fill=var1.var.sqrt ),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="precip.(mm)",na.value = "transparent") + 
  labs(title="Variance of annual precip. (Sph)") +
  theme_minimal()

ggarrange(a,b)

#cross validate with LOOCV
SphKrigeLOOCV_sf <- krige.cv(formula = ANNUAL~1, 
                           locations = prcpCAsf, 
                           model = sph.fit, verbose = FALSE)


##R^2 for ste model
sphr2a <- cor(SphKrigeLOOCV_sf$observed, SphKrigeLOOCV_sf$var1.pred)^2

# RMSE
sphrmsea <- sqrt(mean(SphKrigeLOOCV_sf$residual^2))

#cross validate with nfold
SphKrigenfold_sf <- krige.cv(formula = ANNUAL~1,
                            nfold=10,
                           locations = prcpCAsf, 
                           model = sph.fit, verbose = FALSE)

##R^2 for ste model
Sphr2b <- cor(SphKrigenfold_sf$observed, SphKrigenfold_sf$var1.pred)^2

# RMSE
Sphrmseb <- sqrt(mean(SphKrigenfold_sf$residual^2))



Model4 <- c("Spherical")

#make a nice table

stats4 <- as.data.frame(cbind(Model4, sphr2a, sphrmsea, Sphr2b, Sphrmseb))
stats4 <- type.convert(stats4, as.is = TRUE) #makes it so that numeric stays numeric and characters stay as characters in df


colnames(stats4) <- c("Model", "R-Sq LOOCV","RMSE LOOCV", "R-Sq 10fold", "RMSE nfold") #rename columns


gt(stats4)|>
  tab_options(table.font.size = px(10L))%>%
  opt_table_font("Corbel")%>%
  opt_stylize(style=4)%>%
  fmt_number(columns = where(is.numeric), n_sigfig = 3, drop_trailing_zeros = T)
Model R-Sq LOOCV RMSE LOOCV R-Sq 10fold RMSE nfold
Spherical 0.896 143 0.887 149


11 | All Models

finalstats <- rbind(stats1, stats2, stats3, stats4)

finalstats <- type.convert(finalstats, as.is = TRUE) #makes it so that numeric stays numeric and characters stay as characters in df





gt(finalstats)|>
  tab_options(table.font.size = px(10L))%>%
  opt_table_font("Corbel")%>%
  opt_stylize(style=4)%>%
  fmt_number(columns = where(is.numeric), n_sigfig = 3, drop_trailing_zeros = T)
Model R-Sq LOOCV RMSE LOOCV R-Sq nfold RMSE nfold
Exponential 0.902 138 0.890 147
M. Stein's Parameterization 0.902 138 0.888 148
Gaussian 0.869 160 0.865 162
Spherical 0.896 143 0.887 149

LOOCV Method Just by looking at the variograms, I originally thought the exponential model was best. In step 10 above, I made surface prediction and variance plots for each of the models to help with the interpretation. I found that the Exp Model and Ste Model had the same LOOCV \(R^2\) value. After reassessing the variogram models – they each seemed to fit lines of similar distances between the 11649 and 10504 points on the variogram. I think that each of these models has a similar compromise between the two points, rendering them the same fit overall.

10fold Method Using this method, the exponential model seems to have the best fit. It is still not as good as the fit for the LOOCV method, though. After reading through the Stackoverflow about these methods, it seems that the LOOCV has less bias and in the table above, you can see it also has less root mean square error. I think this means LOOCV is the best method?

Question I still have: What makes a dataset “small” or “large”? Is there a standard size? Maybe n=10000 is large and anything below that is small? And how do you choose the number of best folds when using the nfold function?



✏️ Tutorial

About Kriging

IDW has no error term associated with it. Kriging is a geostatistical method of interpolation that uses spatial autocorrelation – and it works best when there is strong autocorrelation in the data. When kriging we use the distance between sample points (and sometimes direction) to explain variation in a variable.

With IDW the weight was a deterministic function of distance. But with kriging, we use the distance between the measured points and the prediction location as well as the overall spatial structure of the measured points themselves.

Variogram Redux

Here, we demonstrate a variogram with the meuse dataset.

data(meuse.all)
data(meuse.grid, package="sp")

#make a variable to work with
meuse.all$logLead <- log(meuse.all$lead)

#make into an sf
meuse_sf <- st_as_sf(meuse.all, 
                     coords=c("x", "y"))%>%
  st_set_crs(value=28992)

meuse.grid <- st_as_sf(meuse.grid, 
                       coords=c("x", "y"), 
                       crs=st_crs(meuse_sf))

#print meusegrid data to see that it is now a simple feature collection with 5 fields, x and y points, Amersfoort CRS, etc...

#now plot it in ggplot

p1 <- ggplot(data=meuse_sf)+
  geom_sf(aes(fill=logLead), size=4, 
          shape=21, color="white", alpha=0.8)+
  scale_fill_continuous(type="viridis", name="log(ppm)")+
  labs(title="Lead concentrations (DISCRETE VARIOGRAM")

p1

#now use the variogram function from gstat to determine autocorrelation

#sample variogram, observed variogram, etc...is the name of the variogram without the line

leadVar <- variogram(logLead~1, meuse_sf)
plot(leadVar, pch=20, cex=1.5, col="black", 
     ylab=expression("Semivariance ("*gamma*")"), xlab="Distance (m)", main="Lead concentrations (log(ppm))")

Below is a diagram of how to interpret these variograms:

The range is the distance beyond which the data are no longer correlated.

The sill is the variance of the variable.

The nugget is the autocorrelation at very small scales. The nugget represents independent error, measurement error and/or micro-scale variation at fine spatial scales. In a continuous variable we would expect that the nugget effect will be zero because at distance zero the values will be the same. However some variables can change in an abrupt manner and so in very short distance there is a difference.

If there is no structure (no correlation) and the variable is therefore a purely random variable, the variogram will be flat with a nugget effect.

Variogram Model

After plotting the sample variogram we can then fit a statistical model to the points. The model is a continuous estimate of \(\gamma\) as a function of distance: \(\hat{\gamma}=f(d)\).

We will fit two different functions a spherical model and an exponential model. The text has descriptions of the various functions. In practice, we tend to look at the general shape of the plot and then choose a mathematical model that fits.

Spherical Model

# note out initial estimates for the sill, range, and nugget
sph.model <- vgm(psill=0.6, model="Sph", range=750, nugget=0.05)
sph.fit <- fit.variogram(object = leadVar, model = sph.model)

sph.fit
##   model     psill    range
## 1   Nug 0.1095742    0.000
## 2   Sph 0.4560318 1002.626
plot(leadVar,model=sph.fit,pch=20,cex=1.5,col="black",
     ylab=expression("Semivariance ("*gamma*")"),
     xlab="Distance (m)", main = "Lead concentrations (log(ppm))",
     sub="Points: Empirical, Line: Spherical Model/THEORETICAL VARIOGRAM")

#this is a nonlinear function. This is a 3 term model. The nugget = y intercept (b in y=mx+b)
#the range x axis
#the sill telsyou the values of semivariance when there is no autocorrelation. the y axis

#these terms give you the distance at which point the curve flatens out. Values past the range are not autocorrelated

#the shape of teh variogram below

The solid line above is a fitted, theoretical, variogram model. This is done by iterations. We have gone from our observed data (empirical categorical data) and we have fitted a line so that it is now a continuous model! COOL. The spherical model is:

\(\hat{\gamma}(d)=b+C_0(1.5(\frac{d}{r})-0.5(\frac{d^3}{r}))\)

b is the nugget

\(C_0\) is the sill

d is the distance

r is the range

Exponential Model

There are also exponential models:

\(\hat{\gamma}(d)= b+C_0(1-e^{-d/a})\) where \(a = r/3\) and all other terms are the same as above.

Toy Example

Imagine you have a dataframe with x, y, and z:

#any data frame with xyz
foo <- data.frame(x=c(1, 3, 1, 4, 5), 
                  y=c(5, 4, 3, 5, 1), 
                  z=c(100, 105, 105, 100, 115))

#plot it

p2 <- ggplot() + 
  geom_point(data=foo,aes(x=x,y=y,size=z)) + 
  lims(x=c(0,6),y=c(0,6))
p2

Now, imagine you don’t know z at (2,4):

p2 <- p2 + 
  geom_point(aes(x=2,y=4),color="plum",size=10,shape=0) +
  geom_point(aes(x=2,y=4),color="plum",size=6,shape=63)
p2

Find ?

Interpolate with Kriging to find z at (2,4). The Euclidean distance to our unknown point from each of our known points is a vector:

\(d_i=\) \[\begin{bmatrix} 1 & 1.414 \\ 2 & 1.000 \\ 3 & 1.414 \\ 4 & 2.236 \\ 5 & 4.243 \\ \end{bmatrix}\]

To get \(\hat{Z}(s_0)\) you need to calculate \(\lambda\). To do this, fit a variogram by plotting semivariance \((\gamma)\) against distance \((d)\) and get the function that predicts \(\gamma\).

Imagine you did this and found the variogram model is:

\(\hat{\gamma}=0+13.5(d)\)

How will we get the weights \((\lambda)\)?

\(\lambda=g\)\(\Gamma^{-1}\)

\(\Gamma\) = matrix of semivariance of all sampled point pairs predicted as a function of euclidian distance using the fitted variogram model

\(g\) = a vector of predicted semivariance for unknown points using euclidian distances from known points

…and once we have \(\lambda\) we can interpolate (predict) over a surface.

Step 1

Calculate \(g\) for the point you want to predict.

\(g=0+13.5(d_i)\):

#euclidian distance from each point to our unknown point: 
d_i <- sqrt((2-foo$x)^2+(4-foo$y)^2)

#now use your variogram model equation (given to you by Andy) and plug in your d_i values to solve for g
g <- d_i*13.5

g
## [1] 19.09188 13.50000 19.09188 30.18692 57.27565
\[g=\] \[\begin{bmatrix} 1 & 19.09 \\ 2 & 13.50 \\ 3 & 19.09 \\ 4 & 30.19 \\ 5 & 57.28 \\ \end{bmatrix}\]

Step 2

Find \(\Gamma\).

Here is the distance matrix between all measured points (using the proxy library):

euclid <- dist(foo[1:2], method="euclidean")
euclid
##          1        2        3        4
## 2 2.236068                           
## 3 2.000000 2.236068                  
## 4 3.000000 1.414214 3.605551         
## 5 5.656854 3.605551 4.472136 4.123106

Now multiply this by the variogram model to get \(\Gamma\):

Gamma <- 13.5*euclid

Gamma
##          1        2        3        4
## 2 30.18692                           
## 3 27.00000 30.18692                  
## 4 40.50000 19.09188 48.67494         
## 5 76.36753 48.67494 60.37384 55.66193

Step 3

Now take the inverse of the matrix for \(\Gamma\) using solve function:

inverse <- solve(Gamma)
inverse
##               1            2            3            4             5
## 1 -0.0229376496  0.005173891  0.016563227  0.008980417  0.0004308396
## 2  0.0051738909 -0.044601070  0.009873698  0.021194456  0.0028991841
## 3  0.0165632271  0.009873698 -0.025695317 -0.003488374  0.0070317344
## 4  0.0089804170  0.021194456 -0.003488374 -0.027071371  0.0072122450
## 5  0.0004308396  0.002899184  0.007031734  0.007212245 -0.0074569672

Step 4

Find \(\lambda\) where \(\lambda=g\)\(\Gamma^{-1}\):

baby_lambda <- g %*% inverse

baby_lambda
##              1         2       3           4           5
## [1,] 0.2439155 0.4910203 0.25639 -0.01313665 -0.02777361

Step 5

Use \(\lambda_i\) to create the final prediction equation:

\(\hat{Z}(s_0)=0.244(100)+0.491(105)+0.256(105)-0.013(100)-0.028(115)=98.3\)

Lead Example

Predict

meuse.grid
## Simple feature collection with 3103 features and 5 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 178460 ymin: 329620 xmax: 181540 ymax: 333740
## Projected CRS: Amersfoort / RD New
## First 10 features:
##    part.a part.b       dist soil ffreq              geometry
## 1       1      0 0.00000000    1     1 POINT (181180 333740)
## 2       1      0 0.00000000    1     1 POINT (181140 333700)
## 3       1      0 0.01222430    1     1 POINT (181180 333700)
## 4       1      0 0.04346780    1     1 POINT (181220 333700)
## 5       1      0 0.00000000    1     1 POINT (181100 333660)
## 6       1      0 0.01222430    1     1 POINT (181140 333660)
## 7       1      0 0.03733950    1     1 POINT (181180 333660)
## 8       1      0 0.05936620    1     1 POINT (181220 333660)
## 9       1      0 0.00135803    1     1 POINT (181060 333620)
## 10      1      0 0.01222430    1     1 POINT (181100 333620)
meuse_grid_sf <- st_as_sf(meuse.grid)

leadVar <- variogram(logLead~1, meuse_sf)

#use the plot to determine the parameters for vgm()
plot(leadVar)

leadModel <- vgm(psill=0.6, model="Sph", range=750, nugget=0.05)
#there are lots of different models. We are using spherical based on the variogram look

leadFit <- fit.variogram(object = leadVar, model = leadModel)

leadGstat <- gstat(formula = logLead~1, locations = meuse_sf, 
                   model = leadFit)
leadKrige_sf <- predict(leadGstat,newdata = meuse_grid_sf)
## [using ordinary kriging]
leadKrige_sf
## Simple feature collection with 3103 features and 2 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 178460 ymin: 329620 xmax: 181540 ymax: 333740
## Projected CRS: Amersfoort / RD New
## First 10 features:
##    var1.pred  var1.var              geometry
## 1   5.336713 0.3183947 POINT (181180 333740)
## 2   5.406841 0.2724001 POINT (181140 333700)
## 3   5.343480 0.2854597 POINT (181180 333700)
## 4   5.279198 0.3003649 POINT (181220 333700)
## 5   5.486278 0.2238744 POINT (181100 333660)
## 6   5.415401 0.2377063 POINT (181140 333660)
## 7   5.339935 0.2536512 POINT (181180 333660)
## 8   5.266665 0.2701756 POINT (181220 333660)
## 9   5.562294 0.1801611 POINT (181060 333620)
## 10  5.496279 0.1894106 POINT (181100 333620)

Above you can see that the leadKrige is an sf object that has both predicted surface (var1.pred) and the variance around those predictions (var1.var).

Convert sf into rast:

# use the homemade function:
sf_2_rast <-function(sfObject,variableIndex = 1){
  # coerce sf to a data.frame
  dfObject <- data.frame(st_coordinates(sfObject),
                         z=as.data.frame(sfObject)[,variableIndex])
  # coerce data.frame to SpatRaster
  rastObject <- rast(dfObject,crs=crs(sfObject))
  
  names(rastObject) <- names(sfObject)[variableIndex]
  
  return(rastObject)
}

leadKrige_rast <- sf_2_rast(leadKrige_sf)

leadKrige_rast
## class       : SpatRaster 
## dimensions  : 104, 78, 1  (nrow, ncol, nlyr)
## resolution  : 40, 40  (x, y)
## extent      : 178440, 181560, 329600, 333760  (xmin, xmax, ymin, ymax)
## coord. ref. : Amersfoort / RD New (EPSG:28992) 
## source(s)   : memory
## name        : var1.pred 
## min value   :  3.749342 
## max value   :  6.174216

Nice! Now, let’s plot the prediction surface:

# and plot
ggplot() +
  geom_spatraster(data=leadKrige_rast, mapping = aes(fill=var1.pred),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="log(ppm)",na.value = "transparent") + 
  labs(title="Lead concentrations") +
  theme_minimal()

Now, to look at the variance around those predictions, you need to take the square root of var1.var so that the units match the var1.pred units.

leadKrige_sf$var1.var.sqrt <- sqrt(leadKrige_sf$var1.var)

leadKrige_rast <- sf_2_rast(leadKrige_sf,variableIndex = 4)
leadKrige_rast
## class       : SpatRaster 
## dimensions  : 104, 78, 1  (nrow, ncol, nlyr)
## resolution  : 40, 40  (x, y)
## extent      : 178440, 181560, 329600, 333760  (xmin, xmax, ymin, ymax)
## coord. ref. : Amersfoort / RD New (EPSG:28992) 
## source(s)   : memory
## name        : var1.var.sqrt 
## min value   :     0.3881239 
## max value   :     0.6503917

And plot it:

# and plot
ggplot() +
  geom_spatraster(data=leadKrige_rast, mapping = aes(fill=var1.var.sqrt ),alpha=0.8) +
  scale_fill_continuous(type = "viridis",name="log(ppm)",na.value = "transparent") + 
  labs(title="Variance of lead concentrations") +
  theme_minimal()

Cross Validate

Using krige.cv we can cross validate. The default is to leave-one-out cross validate (LOOCV), but you can tell it to k-fold too:

leadKrigeLOOCV_sf <- krige.cv(formula = logLead~1, 
                           locations = meuse_sf, 
                           model = leadFit, verbose = FALSE)
leadKrigeLOOCV_sf
## Simple feature collection with 164 features and 6 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 178605 ymin: 329714 xmax: 181390 ymax: 333611
## Projected CRS: Amersfoort / RD New
## First 10 features:
##    var1.pred  var1.var observed    residual       zscore fold
## 1   5.402571 0.2277973 5.700444  0.29787270  0.624103170    1
## 2   5.473044 0.2199374 5.624018  0.15097361  0.321922586    2
## 3   5.233697 0.2180910 5.293305  0.05960821  0.127640125    3
## 4   5.077189 0.2520550 4.753590 -0.32359847 -0.644553277    4
## 5   4.794539 0.2134651 4.762174 -0.03236554 -0.070051822    5
## 6   4.584552 0.2571416 4.919981  0.33542860  0.661475718    6
## 7   4.970576 0.2088842 4.882802 -0.08777385 -0.192049137    7
## 8   5.253444 0.2072102 5.010635 -0.24280892 -0.533407471    8
## 9   4.886148 0.1786532 4.890349  0.00420153  0.009940359    9
## 10  4.589513 0.1984181 4.382027 -0.20748612 -0.465798826   10
##                 geometry
## 1  POINT (181072 333611)
## 2  POINT (181025 333558)
## 3  POINT (181165 333537)
## 4  POINT (181298 333484)
## 5  POINT (181307 333330)
## 6  POINT (181390 333260)
## 7  POINT (181165 333370)
## 8  POINT (181027 333363)
## 9  POINT (181060 333231)
## 10 POINT (181232 333168)

Now calculate the \(R^2\):

cor(leadKrigeLOOCV_sf$observed, leadKrigeLOOCV_sf$var1.pred)^2
## [1] 0.6192935

The LOOCV \(R^2\) is 0.6193 which is obtained from the observed vs. predicted values. Also note, all the other cool values you get from the krige.cv funcion.

Postscript Automap

You can easily get the nugget, sill, and range using this code:

CAVarAuto <- autofitVariogram(formula = ANNUAL~1,input_data = prcpCAsf)
summary(CAVarAuto)
## Experimental variogram:
##       np      dist      gamma dir.hor dir.ver   id
## 1    113   7117.10   9085.183       0       0 var1
## 2    291  15356.15  20118.936       0       0 var1
## 3    482  24216.82  38977.831       0       0 var1
## 4    971  36289.97  54933.344       0       0 var1
## 5   1268  50835.99  68790.951       0       0 var1
## 6   1476  65163.37  87346.782       0       0 var1
## 7   6010  97592.63 110476.975       0       0 var1
## 8   6996 144845.61 123661.014       0       0 var1
## 9  11649 204160.38 129492.937       0       0 var1
## 10 10504 275857.80 150605.694       0       0 var1
## 11  8883 347876.03 163634.401       0       0 var1
## 12 10027 432324.39 177899.128       0       0 var1
## 
## Fitted variogram model:
##   model    psill    range kappa
## 1   Nug      0.0      0.0   0.0
## 2   Ste 150642.5 104783.9   0.6
## Sums of squares betw. var. model and sample var.[1] 132.477
plot(CAVarAuto)

You can also quickly get cross-validation:

CAAutoKrigeLOOCV <- autoKrige.cv(formula = ANNUAL~1, input_data = prcpCAsf,
                                   verbose = c(FALSE,FALSE))

summary(CAAutoKrigeLOOCV)
##             [,1]     
## mean_error  -1.153   
## me_mean     -0.001881
## MAE         92.21    
## MSE         18840    
## MSNE        0.7799   
## cor_obspred 0.9502   
## cor_predres 0.08809  
## RMSE        137.3    
## RMSE_sd     0.3124   
## URMSE       137.3    
## iqr         108.5
cor(CAAutoKrigeLOOCV$krige.cv_output$observed, CAAutoKrigeLOOCV$krige.cv_output$var1.pred)^2
## [1] 0.902961

Here we get a LOOCV \(R^2\) of 0.9030 which is very (very) close to what we got with the cross validation we did manually above.

LS0tDQp0aXRsZTogIiAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNzczogc3R5bGUuY3NzDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGLCBjYWNoZSA9IFQsIHNpemU9InNtYWxsIikNCmxpYnJhcnkoZ3N0YXQpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRlcnJhKQ0KbGlicmFyeSh0aWR5dGVycmEpDQpsaWJyYXJ5KHByb3h5KQ0KbGlicmFyeShhdXRvbWFwKSAjdXNlZCBmb3IgcG9zdHNjcmlwdCBzZWN0aW9uDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkoZ3QpDQpsaWJyYXJ5KGd0RXh0cmFzKQ0KYGBgDQoNCiMgKipQcm9iYWJpbGlzdGljIEFuYWx5c2lzOiBTaW1wbGUgVW5pdmVyc2FsIEtyaWdpbmcqKg0KDQojIyMjIEhvbWV3b3JrICM1DQoNCjxicj4NCg0KKipNYXJrZG93biBBdXRob3I6KiogSmVzc2llIEJlbGwNCg0KfCANCg0KKipEb3dubG9hZCB0aGlzIFJtZDoqKiBUb3AgcmlnaHQgY29ybmVyIOKGkiBDb2RlIOKGkiBEb3dubG9hZCBSbWQNCg0KfA0KDQpgYGBzZmBgYCB0byBgYGByYXN0YGBgIGNvbnZlcnNpb24gZnVuY3Rpb246DQpgYGB7ciBzZnRvcmFzdH0NCiMgdXNlIHRoZSBob21lbWFkZSBmdW5jdGlvbjoNCnNmXzJfcmFzdCA8LWZ1bmN0aW9uKHNmT2JqZWN0LHZhcmlhYmxlSW5kZXggPSAxKXsNCiAgIyBjb2VyY2Ugc2YgdG8gYSBkYXRhLmZyYW1lDQogIGRmT2JqZWN0IDwtIGRhdGEuZnJhbWUoc3RfY29vcmRpbmF0ZXMoc2ZPYmplY3QpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHo9YXMuZGF0YS5mcmFtZShzZk9iamVjdClbLHZhcmlhYmxlSW5kZXhdKQ0KICAjIGNvZXJjZSBkYXRhLmZyYW1lIHRvIFNwYXRSYXN0ZXINCiAgcmFzdE9iamVjdCA8LSByYXN0KGRmT2JqZWN0LGNycz1jcnMoc2ZPYmplY3QpKQ0KICANCiAgbmFtZXMocmFzdE9iamVjdCkgPC0gbmFtZXMoc2ZPYmplY3QpW3ZhcmlhYmxlSW5kZXhdDQogIA0KICByZXR1cm4ocmFzdE9iamVjdCkNCn0NCmBgYA0KDQoNCnwgDQoNCiMjICoq8J+MtCBDYWxpZm9ybmlhIFByZWNpcGl0YXRpb24qKg0KDQojIyMgMSB8IERhdGEgUmVhZA0KDQoqKlJlYWQgaW4gYW5kIGNvbnZlcnQgdGhlIGRhdGEsIHRoZW4gcGxvdCBpdCB0byBnZXQgYSBncmFzcCBvbiB3aGF0IGlzIHRoZXJlOioqDQoNCmBgYHtyIHByZWNpcCBwb2ludHN9DQoNCiNyZWFkIGluIHlvdXIgZGF0YQ0KcHJjcENBIDwtIHJlYWRSRFMoInByY3BDQS5yZHMiKQ0KI25vdyB0aGUgZW1wdHkgZ3JpZCB0byBpbnRlcnBvbGF0ZSB0aGUgZGF0YWQNCmdyaWRDQSA8LSByZWFkUkRTKCJncmlkQ0EucmRzIikNCg0KI3NldCB0aGUgQ1JTIGFuZCBjb252ZXJ0IGZvcmVpZ24gb2JqZWN0IHRvIFNGDQoNCnByY3BDQXNmIDwtIHByY3BDQSAlPiUgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIikpICU+JQ0KICBzdF9zZXRfY3JzKHZhbHVlID0gMzMxMCkNCg0KDQpncmlkQ0Ffc2YgPC0gc3RfYXNfc2YoZ3JpZENBLCANCiAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzPWMoIlgiLCAiWSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgY3JzPXN0X2NycyhwcmNwQ0FzZikpDQoNCg0KDQojIHBsb3QgaXQNCnByY3BDQXNmICU+JSBnZ3Bsb3QoKSArIA0KICBnZW9tX3NmKGFlcyhmaWxsPUFOTlVBTCxzaXplPUFOTlVBTCksY29sb3I9IndoaXRlIiwNCiAgICAgICAgICBzaGFwZT0yMSxhbHBoYT0wLjgpICsgDQogIHNjYWxlX2ZpbGxfY29udGludW91cyh0eXBlID0gInZpcmlkaXMiLG5hbWU9Im1tIikgKyANCiAgbGFicyh0aXRsZT0iVG90YWwgQW5udWFsIFByZWNpcGl0YXRpb24iKSArDQogIHNjYWxlX3NpemUoZ3VpZGU9Im5vbmUiKQ0KDQpgYGANCg0KfA0KfA0KfA0KDQojIyMgMiB8IFZhcmlvZ3JhbQ0KDQoqKlVzZSBLcmlnaW5nIHRvIGNyZWF0ZSBhIHN1cmZhY2Ugb2YgdGhlIHRvdGFsIGFubnVhbCBwcmVjaXAgaW4gQ2FsaS4gRml0IHRoZSBtb2RlbCB1c2luZyB0aGUgYGBgdmFyaW9ncmFtYGBgLCBgYGB2Z21gYGAgYW5kIGBgYGZpdC52YXJpb2dyYW1gYGAgbGlrZSB0aGUgdHV0b3JpYWwgZGlkLioqIA0KDQpgYGB7ciB2YXJpb2dyYW19DQojcGxvdCB0aGUgdmFyaW9ncmFtDQpDQVZhciA8LSB2YXJpb2dyYW0oQU5OVUFMfjEsIHByY3BDQXNmKQ0KcGxvdChDQVZhciwgcGNoPTIwLCBjZXg9MS41LCBjb2w9ImJsYWNrIiwgDQogICAgIHlsYWI9ZXhwcmVzc2lvbigiU2VtaXZhcmlhbmNlICgiKmdhbW1hKiIpIiksIHhsYWI9IkRpc3RhbmNlIChtKSIsIG1haW49IkNBIEFubnVhbCBQcmVjaXBpdGF0aW9uIChtbSkiKQ0KYGBgDQoNCioqUmFuZ2U6KiogfjEwMCwwMDAgbQ0KDQoqKk51Z2dldDoqKiAkXGdhbW1hJCA9IDEwMDANCg0KKipTaWxsOioqICRcZ2FtbWEkID0gMTEwLDAwMA0KDQp8DQp8DQp8DQoNCg0KIyMjIDMgfCBFeHBvbmVudGlhbCBNb2RlbCANCg0KKipDcmVhdGUgYW4gRXhwb25lbnRpYWwgTW9kZWwgdXNpbmcgdGhlIHZhcmlvZ3JhbSAoc2VlIHN0ZXAgNC41IGFzIHRvIHdoeSBJIGNob3NlIEV4cCkuKioNCg0KYGBge3IgbW9kZX0NCmV4cC5tb2RlbCA8LSB2Z20ocHNpbGw9MTU4MTg4LCBtb2RlbD0iRXhwIiwgcmFuZ2U9ODkxMTIsIG51Z2dldD0wKQ0KZXhwLmZpdCA8LSBmaXQudmFyaW9ncmFtKG9iamVjdCA9IENBVmFyLCBtb2RlbCA9IGV4cC5tb2RlbCkNCg0KZXhwLmZpdA0KYGBgDQoNCnwNCnwNCnwNCg0KIyMjIDQgfCBUaGVvcmV0aWNhbCBWYXJpb2dyYW0NCg0KKipDcmVhdGUgYSB0aGVvcmV0aWNhbCB2YXJpb2dyYW0uKioNCg0KYGBge3IgdGhlb30NCnBsb3QoQ0FWYXIsbW9kZWw9ZXhwLmZpdCxwY2g9MjAsY2V4PTEuNSxjb2w9ImJsYWNrIiwNCiAgICAgeWxhYj1leHByZXNzaW9uKCJTZW1pdmFyaWFuY2UgKCIqZ2FtbWEqIikiKSwNCiAgICAgeGxhYj0iRGlzdGFuY2UgKG0pIiwgbWFpbiA9ICJBbm51YWwgUHJlY2lwaXRhdGlvbiAobW0pIiwNCiAgICAgc3ViPSJQb2ludHM6IEVtcGlyaWNhbCwgTGluZTogRXhwb25lbnRpYWwgTW9kZWwiKQ0KYGBgDQoNCkFib3ZlIHlvdSBjYW4gc2VlIG91ciBvYnNlcnZlZCAoZW1waXJpY2FsL2NhdGVnb3JpY2FsKSBkYXRhIGFsb25nc2lkZSBhIGZpdHRlZCBjb250aW51b3VzIG1vZGVsLg0KDQp8DQp8DQp8DQoNCiMjIyA0LjUgfCBTdGVwcyAyLTQgRkFTVCB7LnRhYnNldH0NCg0KKipEbyB0aGUgbGFzdCAzIHN0ZXBzIGZhc3RlciB3aXRoIGBgYGF1dG9maXRWYXJpb2dyYW1gYGAsIGNoZWNrIGVhY2ggbW9kZWwgc2hhcGUgdG8gZmluZCB0aGUgYmVzdCBmaXQ6KioNCg0KIyMjIyDirZAgRXhwIOKtkA0KDQpgYGB7ciBleHB9DQojVGhlIGV4cG9uZW50aWFsIE1vZGVsDQpDQVZhckF1dG8gPC0gYXV0b2ZpdFZhcmlvZ3JhbShmb3JtdWxhID0gQU5OVUFMfjEsaW5wdXRfZGF0YSA9IHByY3BDQXNmLCBtb2RlbD0iRXhwIikNCg0KcGxvdChDQVZhckF1dG8pDQoNCmBgYA0KDQoqKlRoZSBiZXN0IG1vZGVsIGZpdCBzZWVtcyB0byBiZSB0aGUgRXhwb25lbnRpYWwgTW9kZWwgKEV4cCksIHNvIEkgd2lsbCBwbHVnIHRoZSB0ZXJtcyBpbnRvIHRoZSBFeHAgRXF1YXRpb246KioNCg0KDQoNCioqYioqID0gTnVnZ2V0IA0KDQoqKkMkXzAkKiogPSBTaWxsDQoNCioqZCoqIGlzIHRoZSBkaXN0YW5jZSBpbiBxdWVzdGlvbg0KDQoqKnIqKiA9IFJhbmdlDQoNCioqYSoqID0gJFxmcmFje3J9ezN9JCA9IDI5NzA0DQoNCg0K4q2QICRcaGF0e1xnYW1tYX0oZCk9IDArMTU4MTg4KDEtZV57LWQvMjk3MDR9KSQg4q2QIA0KDQp8DQp8DQp8DQoNCiMjIyMgU3RlDQoNCmBgYHtyIHF1aWNrfQ0KDQojQ2hlY2tpbmcgYWxsIG1vZGVsIHR5cGVzOg0KDQojdGhlIE1hdGVybiwgTS4gU3RlaW4ncyBwYXJhbWV0ZXJpemF0aW9uIChTdGUpDQpDQVZhckF1dG8gPC0gYXV0b2ZpdFZhcmlvZ3JhbShmb3JtdWxhID0gQU5OVUFMfjEsaW5wdXRfZGF0YSA9IHByY3BDQXNmLCBtb2RlbD0iU3RlIikNCg0KcGxvdChDQVZhckF1dG8pDQoNCmBgYA0KDQp8DQp8DQp8DQoNCg0KDQojIyMjIE1hdA0KDQpgYGB7ciBNYXR9DQojVGhlIGV4cG9uZW50aWFsIE1vZGVsDQpDQVZhckF1dG8gPC0gYXV0b2ZpdFZhcmlvZ3JhbShmb3JtdWxhID0gQU5OVUFMfjEsaW5wdXRfZGF0YSA9IHByY3BDQXNmLCBtb2RlbD0iTWF0IikNCg0KcGxvdChDQVZhckF1dG8pDQoNCmBgYA0KDQp8DQp8DQp8DQoNCiMjIyMgR2F1DQoNCmBgYHtyIGdhaX0NCiNUaGUgR2F1c3NpYW4gTW9kZWwNCkNBVmFyQXV0byA8LSBhdXRvZml0VmFyaW9ncmFtKGZvcm11bGEgPSBBTk5VQUx+MSxpbnB1dF9kYXRhID0gcHJjcENBc2YsIG1vZGVsPSJHYXUiKQ0KDQpwbG90KENBVmFyQXV0bykNCmBgYA0KDQp8DQp8DQp8DQoNCiMjIyMgU3BoDQoNCmBgYHtyIHNwaH0NCiMjIyMjIHNwaA0KQ0FWYXJBdXRvIDwtIGF1dG9maXRWYXJpb2dyYW0oZm9ybXVsYSA9IEFOTlVBTH4xLGlucHV0X2RhdGEgPSBwcmNwQ0FzZiwgbW9kZWw9IlNwaCIpDQoNCnBsb3QoQ0FWYXJBdXRvKQ0KYGBgDQoNCg0KfA0KfA0KfA0KDQojIyMgNSB8IEtyaWdpbmcNCg0KKipLcmlnZSB0aGUgZGF0YS4qKg0KDQpgYGB7ciBrcmlnZX0NCkNBR3N0YXQgPC0gZ3N0YXQoZm9ybXVsYSA9IEFOTlVBTH4xLCBsb2NhdGlvbnMgPSBwcmNwQ0FzZiwgDQogICAgICAgICAgICAgICAgICAgbW9kZWwgPSBleHAuZml0KQ0KDQpDQUtyaWdlX3NmIDwtIHByZWRpY3QoQ0FHc3RhdCwgbmV3ZGF0YT1ncmlkQ0Ffc2YpDQoNCmBgYA0KDQoNCnwNCnwNCnwNCg0KIyMjIDYgfCBDb252ZXJ0IHRvIFJhc3Rlcg0KDQoqKkNvbnZlcnQgYGBgc2ZgYGAgdG8gYGBgcmFzdGBgYCB1c2luZyBob21lbWFkZSBmdW5jdGlvbiBgYGBzZl8yX3Jhc3RgYGAuKiogDQoNCmBgYHtyIHNmZnVuY3Rpb25yYXN0fQ0KQ0FLcmlnZV9yYXN0IDwtIHNmXzJfcmFzdChDQUtyaWdlX3NmKQ0KDQpgYGANCg0KfA0KfA0KfA0KDQojIyMgNyB8IFBsb3QgUHJlZGljdGlvbg0KDQoqKlBsb3QgdGhlIHByZWRpY3Rpb24gc3VyZmFjZS4qKg0KDQpgYGB7ciBwbG96fQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NwYXRyYXN0ZXIoZGF0YT1DQUtyaWdlX3Jhc3QsIG1hcHBpbmcgPSBhZXMoZmlsbD12YXIxLnByZWQpLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJQcmVjaXAuIChtbSkiLG5hLnZhbHVlID0gInRyYW5zcGFyZW50IikgKyANCiAgbGFicyh0aXRsZT0iQ0EgQW5udWFsIFByZWNpcGl0YXRpb24gKEV4cCkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KfA0KfA0KfA0KDQojIyMgOCB8IFBsb3QgVmFyaWFuY2UNCg0KKipDaGVjayBvdXQgdGhlIHZhcmlhbmNlIGFyb3VuZCB0aGUgcHJlZGljdGlvbnMgYW5kIHBsb3QgaXQuKioNCg0KYGBge3IgdmFyaWFuY2V9DQpDQUtyaWdlX3NmJHZhcjEudmFyLnNxcnQgPC0gc3FydChDQUtyaWdlX3NmJHZhcjEudmFyKQ0KDQpDQUtyaWdlX3Jhc3QgPC0gc2ZfMl9yYXN0KENBS3JpZ2Vfc2YsdmFyaWFibGVJbmRleCA9IDQpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zcGF0cmFzdGVyKGRhdGE9Q0FLcmlnZV9yYXN0LCBtYXBwaW5nID0gYWVzKGZpbGw9dmFyMS52YXIuc3FydCApLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJwcmVjaXAuKG1tKSIsbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIA0KICBsYWJzKHRpdGxlPSJWYXJpYW5jZSBvZiBhbm51YWwgcHJlY2lwLiAoRXhwKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQp8DQp8DQp8DQoNCiMjIyA5IHwgQ3Jvc3MtVmFsaWRhdGUNCg0KKipDcm9zcy12YWxpZGF0ZSB1c2luZyBgYGBrcmlnZS5jdmBgYCBhbmQgY2FsY3VsYXRlICRSXjIkLioqDQoNCmBgYHtyIGNyb3NzfQ0KI2Nyb3NzIHZhbGlkYXRlIHdpdGggTE9PQ1YNCkV4cEtyaWdlTE9PQ1Zfc2YgPC0ga3JpZ2UuY3YoZm9ybXVsYSA9IEFOTlVBTH4xLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9ucyA9IHByY3BDQXNmLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gZXhwLmZpdCwgdmVyYm9zZSA9IEZBTFNFKQ0KDQoNCiMjUl4yIGZvciBleHAgbW9kZWwNCkV4cF9yMl9MT09DViA8LSBjb3IoRXhwS3JpZ2VMT09DVl9zZiRvYnNlcnZlZCwgRXhwS3JpZ2VMT09DVl9zZiR2YXIxLnByZWQpXjINCg0KIyBSTVNFDQpFeHBfcm1zZV9MT09DViA8LXNxcnQobWVhbihFeHBLcmlnZUxPT0NWX3NmJHJlc2lkdWFsXjIpKQ0KDQojY3Jvc3MgdmFsaWRhdGUgd2l0aCBuZm9sZA0KRXhwS3JpZ2VuZm9sZF9zZiA8LSBrcmlnZS5jdihmb3JtdWxhID0gQU5OVUFMfjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZvbGQ9MTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbnMgPSBwcmNwQ0FzZiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9IGV4cC5maXQsIHZlcmJvc2UgPSBGQUxTRSkNCg0KIyNSXjIgZm9yIHN0ZSBtb2RlbA0KRXhwX3IyX25mb2xkIDwtIGNvcihFeHBLcmlnZW5mb2xkX3NmJG9ic2VydmVkLCBFeHBLcmlnZW5mb2xkX3NmJHZhcjEucHJlZCleMg0KDQojIFJNU0UNCkV4cF9ybXNlX25mb2xkIDwtIHNxcnQobWVhbihFeHBLcmlnZW5mb2xkX3NmJHJlc2lkdWFsXjIpKQ0KDQpNb2RlbDUgPC0gYygiRXhwb25lbnRpYWwiKQ0KDQojbWFrZSBhIG5pY2UgdGFibGUNCg0Kc3RhdHMxIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoTW9kZWw1LCBFeHBfcjJfTE9PQ1YsIEV4cF9ybXNlX0xPT0NWLCBFeHBfcjJfbmZvbGQsIEV4cF9ybXNlX25mb2xkKSkNCnN0YXRzMSA8LSB0eXBlLmNvbnZlcnQoc3RhdHMxLCBhcy5pcyA9IFRSVUUpICNtYWtlcyBpdCBzbyB0aGF0IG51bWVyaWMgc3RheXMgbnVtZXJpYyBhbmQgY2hhcmFjdGVycyBzdGF5IGFzIGNoYXJhY3RlcnMgaW4gZGYNCg0KDQpjb2xuYW1lcyhzdGF0czEpIDwtIGMoIk1vZGVsIiwgIlItU3EgTE9PQ1YiLCJSTVNFIExPT0NWIiwgIlItU3EgMTBmb2xkIiwgIlJNU0UgbmZvbGQiKSAjcmVuYW1lIGNvbHVtbnMNCg0KDQpndChzdGF0czEpfD4NCiAgdGFiX29wdGlvbnModGFibGUuZm9udC5zaXplID0gcHgoMTBMKSklPiUNCiAgb3B0X3RhYmxlX2ZvbnQoIkNvcmJlbCIpJT4lDQogIG9wdF9zdHlsaXplKHN0eWxlPTQpJT4lDQogIGZtdF9udW1iZXIoY29sdW1ucyA9IHdoZXJlKGlzLm51bWVyaWMpLCBuX3NpZ2ZpZyA9IDMsIGRyb3BfdHJhaWxpbmdfemVyb3MgPSBUKQ0KDQoNCiAgDQoNCmBgYA0KVGhlIExPT0NWICRSXjIkIGlzIDAuOTAyMCwgdGhhdCBzZWVtcyBwcmV0dHksIHByZXR0eSBnb29kISBUaGUgUk1TRSBpcyAxMzgsIHdoaWNoIGlzIGxvd2VyIHRoYW4gdGhlIFJNU0UgZm9yIGFuIG5mb2xkIG9mIDEwLiBJIHRoaW5rIHRoaXMgaXMgYmVjYXVzZSBvdXIgcHJlY2lwaXRhdGlvbiBkYXRhc2V0IGlzIHNtYWxsLCBhbmQgc28gdXNpbmcgTE9PQ1Ygd29ya3MgYmV0dGVyIGhlcmUuIA0KDQoNCnwNCnwNCnwNCg0KIyMjIDEwIHwgVHVuZSB7LnRhYnNldH0NCg0KKipUdW5lIGFuZCBhc3Nlc3MgdGhlIHN1cmZhY2UgZml0IGFzIHlvdSBsaWtlLiBJbnRlcnByZXQgYW5kIGV4cGxhaW4geW91ciBzdXJmYWNlIGFuZCBhbnl0aGluZyB5b3Ugbm90aWNlIGluIHBhcnRpY3VsYXIgYWJvdXQgaXQuKioNCg0KUGxlYXNlIHNlZSBpbnRlcnByZXRhdGlvbiBpbiBzdGVwIDExIGJlbG93Lg0KDQp8DQp8DQp8DQoNCg0KIyMjIyBTdGUvTWF0DQoNCmBgYHtyIHN0ZXBhfQ0KI2NyZWF0ZSB0aGUgbW9kZWwNCnN0ZS5tb2RlbCA8LSB2Z20ocHNpbGw9MTUwNjQzLCBtb2RlbD0iU3RlIiwgcmFuZ2U9MTA0Nzg0LCBudWdnZXQ9MCkNCnN0ZS5maXQgPC0gZml0LnZhcmlvZ3JhbShvYmplY3QgPSBDQVZhciwgbW9kZWwgPSBzdGUubW9kZWwpDQoNCiNLcmlnZSB0aGUgZGF0YQ0KQ0FHc3RhdCA8LSBnc3RhdChmb3JtdWxhID0gQU5OVUFMfjEsIGxvY2F0aW9ucyA9IHByY3BDQXNmLCANCiAgICAgICAgICAgICAgICAgICBtb2RlbCA9IHN0ZS5maXQpDQoNCkNBS3JpZ2Vfc2YgPC0gcHJlZGljdChDQUdzdGF0LCBuZXdkYXRhPWdyaWRDQV9zZikNCg0KI2NvbnZlcnQgdG8gcmFzdGVyDQpDQUtyaWdlX3Jhc3QgPC0gc2ZfMl9yYXN0KENBS3JpZ2Vfc2YpDQoNCiNwbG90IHByZWRpY3Rpb24NCmEgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NwYXRyYXN0ZXIoZGF0YT1DQUtyaWdlX3Jhc3QsIG1hcHBpbmcgPSBhZXMoZmlsbD12YXIxLnByZWQpLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJQcmVjaXAuIChtbSkiLG5hLnZhbHVlID0gInRyYW5zcGFyZW50IikgKyANCiAgbGFicyh0aXRsZT0iQ0EgQW5udWFsIFByZWNpcGl0YXRpb24gKFN0ZSkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojcGxvdCB2YXJpYW5jZQ0KQ0FLcmlnZV9zZiR2YXIxLnZhci5zcXJ0IDwtIHNxcnQoQ0FLcmlnZV9zZiR2YXIxLnZhcikNCg0KQ0FLcmlnZV9yYXN0IDwtIHNmXzJfcmFzdChDQUtyaWdlX3NmLHZhcmlhYmxlSW5kZXggPSA0KQ0KDQpiIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zcGF0cmFzdGVyKGRhdGE9Q0FLcmlnZV9yYXN0LCBtYXBwaW5nID0gYWVzKGZpbGw9dmFyMS52YXIuc3FydCApLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJwcmVjaXAuKG1tKSIsbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIA0KICBsYWJzKHRpdGxlPSJWYXJpYW5jZSBvZiBhbm51YWwgcHJlY2lwLiAoU3RlKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdnYXJyYW5nZShhLGIpDQoNCiNjcm9zcyB2YWxpZGF0ZSB3aXRoIExPT0NWDQpTdGVLcmlnZUxPT0NWX3NmIDwtIGtyaWdlLmN2KGZvcm11bGEgPSBBTk5VQUx+MSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbnMgPSBwcmNwQ0FzZiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9IHN0ZS5maXQsIHZlcmJvc2UgPSBGQUxTRSkNCg0KDQojI1JeMiBmb3Igc3RlIG1vZGVsDQpTdGVSMkxPT0NWIDwtIGNvcihTdGVLcmlnZUxPT0NWX3NmJG9ic2VydmVkLCBTdGVLcmlnZUxPT0NWX3NmJHZhcjEucHJlZCleMg0KDQojIFJNU0UNClN0ZXJtc2VMT09DViA8LSBzcXJ0KG1lYW4oU3RlS3JpZ2VMT09DVl9zZiRyZXNpZHVhbF4yKSkNCg0KI2Nyb3NzIHZhbGlkYXRlIHdpdGggbmZvbGQNClN0ZUtyaWdlbmZvbGRfc2YgPC0ga3JpZ2UuY3YoZm9ybXVsYSA9IEFOTlVBTH4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mb2xkPTEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25zID0gcHJjcENBc2YsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSBzdGUuZml0LCB2ZXJib3NlID0gRkFMU0UpDQoNCiMjUl4yIGZvciBzdGUgbW9kZWwNClN0ZXIybmZvbGQgPC0gY29yKFN0ZUtyaWdlbmZvbGRfc2Ykb2JzZXJ2ZWQsIFN0ZUtyaWdlbmZvbGRfc2YkdmFyMS5wcmVkKV4yDQoNCiMgUk1TRQ0KU3Rlcm1zZW5mb2xkIDwtIHNxcnQobWVhbihTdGVLcmlnZW5mb2xkX3NmJHJlc2lkdWFsXjIpKQ0KDQpNb2RlbDIgPC0gYygiTS4gU3RlaW4ncyBQYXJhbWV0ZXJpemF0aW9uIikNCg0KI21ha2UgYSBuaWNlIHRhYmxlDQoNCnN0YXRzMiA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKE1vZGVsMiwgU3RlUjJMT09DViwgU3Rlcm1zZUxPT0NWLCBTdGVyMm5mb2xkLCBTdGVybXNlbmZvbGQpKQ0Kc3RhdHMyIDwtIHR5cGUuY29udmVydChzdGF0czIsIGFzLmlzID0gVFJVRSkgI21ha2VzIGl0IHNvIHRoYXQgbnVtZXJpYyBzdGF5cyBudW1lcmljIGFuZCBjaGFyYWN0ZXJzIHN0YXkgYXMgY2hhcmFjdGVycyBpbiBkZg0KDQoNCmNvbG5hbWVzKHN0YXRzMikgPC0gYygiTW9kZWwiLCAiUi1TcSBMT09DViIsIlJNU0UgTE9PQ1YiLCAiUi1TcSAxMGZvbGQiLCAiUk1TRSBuZm9sZCIpICNyZW5hbWUgY29sdW1ucw0KDQoNCmd0KHN0YXRzMil8Pg0KICB0YWJfb3B0aW9ucyh0YWJsZS5mb250LnNpemUgPSBweCgxMEwpKSU+JQ0KICBvcHRfdGFibGVfZm9udCgiQ29yYmVsIiklPiUNCiAgb3B0X3N0eWxpemUoc3R5bGU9NCklPiUNCiAgZm10X251bWJlcihjb2x1bW5zID0gd2hlcmUoaXMubnVtZXJpYyksIG5fc2lnZmlnID0gMywgZHJvcF90cmFpbGluZ196ZXJvcyA9IFQpDQoNCg0KDQoNCmBgYA0KDQoNCg0KDQoNCiMjIyMgR2F1DQoNCmBgYHtyIHN0ZXBhc30NCiNjcmVhdGUgdGhlIG1vZGVsDQpnYXUubW9kZWwgPC0gdmdtKHBzaWxsPTEyNTQyOCwgbW9kZWw9IkdhdSIsIHJhbmdlPTU0MDk3LCBudWdnZXQ9OTczMykNCmdhdS5maXQgPC0gZml0LnZhcmlvZ3JhbShvYmplY3QgPSBDQVZhciwgbW9kZWwgPSBnYXUubW9kZWwpDQoNCiNLcmlnZSB0aGUgZGF0YQ0KQ0FHc3RhdCA8LSBnc3RhdChmb3JtdWxhID0gQU5OVUFMfjEsIGxvY2F0aW9ucyA9IHByY3BDQXNmLCANCiAgICAgICAgICAgICAgICAgICBtb2RlbCA9IGdhdS5maXQpDQoNCkNBS3JpZ2Vfc2YgPC0gcHJlZGljdChDQUdzdGF0LCBuZXdkYXRhPWdyaWRDQV9zZikNCg0KI2NvbnZlcnQgdG8gcmFzdGVyDQpDQUtyaWdlX3Jhc3QgPC0gc2ZfMl9yYXN0KENBS3JpZ2Vfc2YpDQoNCiNwbG90IHByZWRpY3Rpb24NCmEgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NwYXRyYXN0ZXIoZGF0YT1DQUtyaWdlX3Jhc3QsIG1hcHBpbmcgPSBhZXMoZmlsbD12YXIxLnByZWQpLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJQcmVjaXAuIChtbSkiLG5hLnZhbHVlID0gInRyYW5zcGFyZW50IikgKyANCiAgbGFicyh0aXRsZT0iQ0EgQW5udWFsIFByZWNpcGl0YXRpb24gKEdhdSkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojcGxvdCB2YXJpYW5jZQ0KQ0FLcmlnZV9zZiR2YXIxLnZhci5zcXJ0IDwtIHNxcnQoQ0FLcmlnZV9zZiR2YXIxLnZhcikNCg0KQ0FLcmlnZV9yYXN0IDwtIHNmXzJfcmFzdChDQUtyaWdlX3NmLHZhcmlhYmxlSW5kZXggPSA0KQ0KDQpiIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zcGF0cmFzdGVyKGRhdGE9Q0FLcmlnZV9yYXN0LCBtYXBwaW5nID0gYWVzKGZpbGw9dmFyMS52YXIuc3FydCApLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJwcmVjaXAuKG1tKSIsbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIA0KICBsYWJzKHRpdGxlPSJWYXJpYW5jZSBvZiBhbm51YWwgcHJlY2lwLiAoR2F1KSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdnYXJyYW5nZShhLGIpDQoNCiNjcm9zcyB2YWxpZGF0ZSB3aXRoIExPT0NWDQpHYXVLcmlnZUxPT0NWX3NmIDwtIGtyaWdlLmN2KGZvcm11bGEgPSBBTk5VQUx+MSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbnMgPSBwcmNwQ0FzZiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9IGdhdS5maXQsIHZlcmJvc2UgPSBGQUxTRSkNCg0KDQojI1JeMiBmb3Igc3RlIG1vZGVsDQpHYXVyMmEgPC0gY29yKEdhdUtyaWdlTE9PQ1Zfc2Ykb2JzZXJ2ZWQsIEdhdUtyaWdlTE9PQ1Zfc2YkdmFyMS5wcmVkKV4yDQoNCiMgUk1TRQ0KR2F1Uk1TRWIgPC0gc3FydChtZWFuKEdhdUtyaWdlTE9PQ1Zfc2YkcmVzaWR1YWxeMikpDQoNCiNjcm9zcyB2YWxpZGF0ZSB3aXRoIG5mb2xkDQpHYXVLcmlnZW5mb2xkX3NmIDwtIGtyaWdlLmN2KGZvcm11bGEgPSBBTk5VQUx+MSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZm9sZD0xMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9ucyA9IHByY3BDQXNmLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gZ2F1LmZpdCwgdmVyYm9zZSA9IEZBTFNFKQ0KDQojI1JeMiBmb3Igc3RlIG1vZGVsDQpHYXVyMmIgPC0gY29yKEdhdUtyaWdlbmZvbGRfc2Ykb2JzZXJ2ZWQsIEdhdUtyaWdlbmZvbGRfc2YkdmFyMS5wcmVkKV4yDQoNCiMgUk1TRQ0KR2F1cm1zZWIgPC0gc3FydChtZWFuKEdhdUtyaWdlbmZvbGRfc2YkcmVzaWR1YWxeMikpDQoNCg0KTW9kZWwzIDwtIGMoIkdhdXNzaWFuIikNCg0KI21ha2UgYSBuaWNlIHRhYmxlDQoNCnN0YXRzMyA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKE1vZGVsMywgR2F1cjJhLCBHYXVSTVNFYiwgR2F1cjJiLCBHYXVybXNlYikpDQpzdGF0czMgPC0gdHlwZS5jb252ZXJ0KHN0YXRzMywgYXMuaXMgPSBUUlVFKSAjbWFrZXMgaXQgc28gdGhhdCBudW1lcmljIHN0YXlzIG51bWVyaWMgYW5kIGNoYXJhY3RlcnMgc3RheSBhcyBjaGFyYWN0ZXJzIGluIGRmDQoNCg0KY29sbmFtZXMoc3RhdHMzKSA8LSBjKCJNb2RlbCIsICJSLVNxIExPT0NWIiwiUk1TRSBMT09DViIsICJSLVNxIDEwZm9sZCIsICJSTVNFIG5mb2xkIikgI3JlbmFtZSBjb2x1bW5zDQoNCg0KZ3Qoc3RhdHMzKXw+DQogIHRhYl9vcHRpb25zKHRhYmxlLmZvbnQuc2l6ZSA9IHB4KDEwTCkpJT4lDQogIG9wdF90YWJsZV9mb250KCJDb3JiZWwiKSU+JQ0KICBvcHRfc3R5bGl6ZShzdHlsZT00KSU+JQ0KICBmbXRfbnVtYmVyKGNvbHVtbnMgPSB3aGVyZShpcy5udW1lcmljKSwgbl9zaWdmaWcgPSAzLCBkcm9wX3RyYWlsaW5nX3plcm9zID0gVCkNCmBgYA0KDQoNCg0KfA0KfA0KfA0KDQojIyMjIFNwaA0KDQpgYGB7ciBzdGVwYXNkfQ0KI2NyZWF0ZSB0aGUgbW9kZWwNCnNwaC5tb2RlbCA8LSB2Z20ocHNpbGw9MTM1MzAyLCBtb2RlbD0iU3BoIiwgcmFuZ2U9MTQ0NzQ2LCBudWdnZXQ9NjMwKQ0Kc3BoLmZpdCA8LSBmaXQudmFyaW9ncmFtKG9iamVjdCA9IENBVmFyLCBtb2RlbCA9IHNwaC5tb2RlbCkNCg0KI0tyaWdlIHRoZSBkYXRhDQpDQUdzdGF0IDwtIGdzdGF0KGZvcm11bGEgPSBBTk5VQUx+MSwgbG9jYXRpb25zID0gcHJjcENBc2YsIA0KICAgICAgICAgICAgICAgICAgIG1vZGVsID0gc3BoLmZpdCkNCg0KQ0FLcmlnZV9zZiA8LSBwcmVkaWN0KENBR3N0YXQsIG5ld2RhdGE9Z3JpZENBX3NmKQ0KDQojY29udmVydCB0byByYXN0ZXINCkNBS3JpZ2VfcmFzdCA8LSBzZl8yX3Jhc3QoQ0FLcmlnZV9zZikNCg0KI3Bsb3QgcHJlZGljdGlvbg0KYSA8LSBnZ3Bsb3QoKSArDQogIGdlb21fc3BhdHJhc3RlcihkYXRhPUNBS3JpZ2VfcmFzdCwgbWFwcGluZyA9IGFlcyhmaWxsPXZhcjEucHJlZCksYWxwaGE9MC44KSArDQogIHNjYWxlX2ZpbGxfY29udGludW91cyh0eXBlID0gInZpcmlkaXMiLG5hbWU9IlByZWNpcC4gKG1tKSIsbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIA0KICBsYWJzKHRpdGxlPSJDQSBBbm51YWwgUHJlY2lwaXRhdGlvbiAoU3BoKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNwbG90IHZhcmlhbmNlDQpDQUtyaWdlX3NmJHZhcjEudmFyLnNxcnQgPC0gc3FydChDQUtyaWdlX3NmJHZhcjEudmFyKQ0KDQpDQUtyaWdlX3Jhc3QgPC0gc2ZfMl9yYXN0KENBS3JpZ2Vfc2YsdmFyaWFibGVJbmRleCA9IDQpDQoNCmIgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NwYXRyYXN0ZXIoZGF0YT1DQUtyaWdlX3Jhc3QsIG1hcHBpbmcgPSBhZXMoZmlsbD12YXIxLnZhci5zcXJ0ICksYWxwaGE9MC44KSArDQogIHNjYWxlX2ZpbGxfY29udGludW91cyh0eXBlID0gInZpcmlkaXMiLG5hbWU9InByZWNpcC4obW0pIixuYS52YWx1ZSA9ICJ0cmFuc3BhcmVudCIpICsgDQogIGxhYnModGl0bGU9IlZhcmlhbmNlIG9mIGFubnVhbCBwcmVjaXAuIChTcGgpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dhcnJhbmdlKGEsYikNCg0KDQojY3Jvc3MgdmFsaWRhdGUgd2l0aCBMT09DVg0KU3BoS3JpZ2VMT09DVl9zZiA8LSBrcmlnZS5jdihmb3JtdWxhID0gQU5OVUFMfjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25zID0gcHJjcENBc2YsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSBzcGguZml0LCB2ZXJib3NlID0gRkFMU0UpDQoNCg0KIyNSXjIgZm9yIHN0ZSBtb2RlbA0Kc3BocjJhIDwtIGNvcihTcGhLcmlnZUxPT0NWX3NmJG9ic2VydmVkLCBTcGhLcmlnZUxPT0NWX3NmJHZhcjEucHJlZCleMg0KDQojIFJNU0UNCnNwaHJtc2VhIDwtIHNxcnQobWVhbihTcGhLcmlnZUxPT0NWX3NmJHJlc2lkdWFsXjIpKQ0KDQojY3Jvc3MgdmFsaWRhdGUgd2l0aCBuZm9sZA0KU3BoS3JpZ2VuZm9sZF9zZiA8LSBrcmlnZS5jdihmb3JtdWxhID0gQU5OVUFMfjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZvbGQ9MTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbnMgPSBwcmNwQ0FzZiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9IHNwaC5maXQsIHZlcmJvc2UgPSBGQUxTRSkNCg0KIyNSXjIgZm9yIHN0ZSBtb2RlbA0KU3BocjJiIDwtIGNvcihTcGhLcmlnZW5mb2xkX3NmJG9ic2VydmVkLCBTcGhLcmlnZW5mb2xkX3NmJHZhcjEucHJlZCleMg0KDQojIFJNU0UNClNwaHJtc2ViIDwtIHNxcnQobWVhbihTcGhLcmlnZW5mb2xkX3NmJHJlc2lkdWFsXjIpKQ0KDQoNCg0KTW9kZWw0IDwtIGMoIlNwaGVyaWNhbCIpDQoNCiNtYWtlIGEgbmljZSB0YWJsZQ0KDQpzdGF0czQgPC0gYXMuZGF0YS5mcmFtZShjYmluZChNb2RlbDQsIHNwaHIyYSwgc3Bocm1zZWEsIFNwaHIyYiwgU3Bocm1zZWIpKQ0Kc3RhdHM0IDwtIHR5cGUuY29udmVydChzdGF0czQsIGFzLmlzID0gVFJVRSkgI21ha2VzIGl0IHNvIHRoYXQgbnVtZXJpYyBzdGF5cyBudW1lcmljIGFuZCBjaGFyYWN0ZXJzIHN0YXkgYXMgY2hhcmFjdGVycyBpbiBkZg0KDQoNCmNvbG5hbWVzKHN0YXRzNCkgPC0gYygiTW9kZWwiLCAiUi1TcSBMT09DViIsIlJNU0UgTE9PQ1YiLCAiUi1TcSAxMGZvbGQiLCAiUk1TRSBuZm9sZCIpICNyZW5hbWUgY29sdW1ucw0KDQoNCmd0KHN0YXRzNCl8Pg0KICB0YWJfb3B0aW9ucyh0YWJsZS5mb250LnNpemUgPSBweCgxMEwpKSU+JQ0KICBvcHRfdGFibGVfZm9udCgiQ29yYmVsIiklPiUNCiAgb3B0X3N0eWxpemUoc3R5bGU9NCklPiUNCiAgZm10X251bWJlcihjb2x1bW5zID0gd2hlcmUoaXMubnVtZXJpYyksIG5fc2lnZmlnID0gMywgZHJvcF90cmFpbGluZ196ZXJvcyA9IFQpDQoNCmBgYA0KDQoNCg0KfA0KfA0KfA0KDQojIyMgMTEgfCBBbGwgTW9kZWxzDQoNCmBgYHtyIHRhYmxlfQ0KDQpmaW5hbHN0YXRzIDwtIHJiaW5kKHN0YXRzMSwgc3RhdHMyLCBzdGF0czMsIHN0YXRzNCkNCg0KZmluYWxzdGF0cyA8LSB0eXBlLmNvbnZlcnQoZmluYWxzdGF0cywgYXMuaXMgPSBUUlVFKSAjbWFrZXMgaXQgc28gdGhhdCBudW1lcmljIHN0YXlzIG51bWVyaWMgYW5kIGNoYXJhY3RlcnMgc3RheSBhcyBjaGFyYWN0ZXJzIGluIGRmDQoNCg0KDQoNCg0KZ3QoZmluYWxzdGF0cyl8Pg0KICB0YWJfb3B0aW9ucyh0YWJsZS5mb250LnNpemUgPSBweCgxMEwpKSU+JQ0KICBvcHRfdGFibGVfZm9udCgiQ29yYmVsIiklPiUNCiAgb3B0X3N0eWxpemUoc3R5bGU9NCklPiUNCiAgZm10X251bWJlcihjb2x1bW5zID0gd2hlcmUoaXMubnVtZXJpYyksIG5fc2lnZmlnID0gMywgZHJvcF90cmFpbGluZ196ZXJvcyA9IFQpDQoNCg0KDQoNCmBgYA0KDQoqKkxPT0NWIE1ldGhvZCoqDQpKdXN0IGJ5IGxvb2tpbmcgYXQgdGhlIHZhcmlvZ3JhbXMsIEkgb3JpZ2luYWxseSB0aG91Z2h0IHRoZSBleHBvbmVudGlhbCBtb2RlbCB3YXMgYmVzdC4gSW4gc3RlcCAxMCBhYm92ZSwgSSBtYWRlIHN1cmZhY2UgcHJlZGljdGlvbiBhbmQgdmFyaWFuY2UgcGxvdHMgZm9yIGVhY2ggb2YgdGhlIG1vZGVscyB0byBoZWxwIHdpdGggdGhlIGludGVycHJldGF0aW9uLiBJIGZvdW5kIHRoYXQgdGhlIEV4cCBNb2RlbCBhbmQgU3RlIE1vZGVsIGhhZCB0aGUgc2FtZSBMT09DViAkUl4yJCB2YWx1ZS4gQWZ0ZXIgcmVhc3Nlc3NpbmcgdGhlIHZhcmlvZ3JhbSBtb2RlbHMgLS0gdGhleSBlYWNoIHNlZW1lZCB0byBmaXQgbGluZXMgb2Ygc2ltaWxhciBkaXN0YW5jZXMgYmV0d2VlbiB0aGUgMTE2NDkgYW5kIDEwNTA0IHBvaW50cyBvbiB0aGUgdmFyaW9ncmFtLiBJIHRoaW5rIHRoYXQgZWFjaCBvZiB0aGVzZSBtb2RlbHMgaGFzIGEgc2ltaWxhciBjb21wcm9taXNlIGJldHdlZW4gdGhlIHR3byBwb2ludHMsIHJlbmRlcmluZyB0aGVtIHRoZSBzYW1lIGZpdCBvdmVyYWxsLiANCg0KKioxMGZvbGQgTWV0aG9kKioNClVzaW5nIHRoaXMgbWV0aG9kLCB0aGUgZXhwb25lbnRpYWwgbW9kZWwgc2VlbXMgdG8gaGF2ZSB0aGUgYmVzdCBmaXQuIEl0IGlzIHN0aWxsIG5vdCBhcyBnb29kIGFzIHRoZSBmaXQgZm9yIHRoZSBMT09DViBtZXRob2QsIHRob3VnaC4gQWZ0ZXIgcmVhZGluZyB0aHJvdWdoIHRoZSBTdGFja292ZXJmbG93IGFib3V0IHRoZXNlIG1ldGhvZHMsIGl0IHNlZW1zIHRoYXQgdGhlIExPT0NWIGhhcyBsZXNzIGJpYXMgKmFuZCogaW4gdGhlIHRhYmxlIGFib3ZlLCB5b3UgY2FuIHNlZSBpdCBhbHNvIGhhcyBsZXNzIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3IuIEkgdGhpbmsgdGhpcyBtZWFucyBMT09DViBpcyB0aGUgYmVzdCBtZXRob2Q/DQoNCg0KKipRdWVzdGlvbiBJIHN0aWxsIGhhdmU6KiogV2hhdCBtYWtlcyBhIGRhdGFzZXQgInNtYWxsIiBvciAibGFyZ2UiPyBJcyB0aGVyZSBhIHN0YW5kYXJkIHNpemU/IE1heWJlIG49MTAwMDAgaXMgbGFyZ2UgYW5kIGFueXRoaW5nIGJlbG93IHRoYXQgaXMgc21hbGw/IEFuZCBob3cgZG8geW91IGNob29zZSB0aGUgbnVtYmVyIG9mIGJlc3QgZm9sZHMgd2hlbiB1c2luZyB0aGUgbmZvbGQgZnVuY3Rpb24/DQoNCg0KDQoNCnwNCnwNCnwNCg0KIyMgKirinI/vuI8gVHV0b3JpYWwqKiANCg0KIyMjIEFib3V0IEtyaWdpbmcNCg0KSURXIGhhcyBubyBlcnJvciB0ZXJtIGFzc29jaWF0ZWQgd2l0aCBpdC4gIEtyaWdpbmcgaXMgYSBnZW9zdGF0aXN0aWNhbCBtZXRob2Qgb2YgaW50ZXJwb2xhdGlvbiB0aGF0IHVzZXMgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24g4oCTIGFuZCBpdCB3b3JrcyBiZXN0IHdoZW4gdGhlcmUgaXMgc3Ryb25nIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgZGF0YS4gV2hlbiBrcmlnaW5nIHdlIHVzZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBzYW1wbGUgcG9pbnRzIChhbmQgc29tZXRpbWVzIGRpcmVjdGlvbikgdG8gZXhwbGFpbiB2YXJpYXRpb24gaW4gYSB2YXJpYWJsZS4gDQoNCldpdGggSURXIHRoZSB3ZWlnaHQgd2FzIGEgZGV0ZXJtaW5pc3RpYyBmdW5jdGlvbiBvZiBkaXN0YW5jZS4gQnV0IHdpdGgga3JpZ2luZywgd2UgdXNlIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBtZWFzdXJlZCBwb2ludHMgYW5kIHRoZSBwcmVkaWN0aW9uIGxvY2F0aW9uIGFzIHdlbGwgYXMgdGhlIG92ZXJhbGwgc3BhdGlhbCBzdHJ1Y3R1cmUgb2YgdGhlIG1lYXN1cmVkIHBvaW50cyB0aGVtc2VsdmVzLiANCg0KIyMjIFZhcmlvZ3JhbSBSZWR1eA0KDQpIZXJlLCB3ZSBkZW1vbnN0cmF0ZSBhIHZhcmlvZ3JhbSB3aXRoIHRoZSBtZXVzZSBkYXRhc2V0LiANCmBgYHtyIG1ldXNlMX0NCmRhdGEobWV1c2UuYWxsKQ0KZGF0YShtZXVzZS5ncmlkLCBwYWNrYWdlPSJzcCIpDQoNCiNtYWtlIGEgdmFyaWFibGUgdG8gd29yayB3aXRoDQptZXVzZS5hbGwkbG9nTGVhZCA8LSBsb2cobWV1c2UuYWxsJGxlYWQpDQoNCiNtYWtlIGludG8gYW4gc2YNCm1ldXNlX3NmIDwtIHN0X2FzX3NmKG1ldXNlLmFsbCwgDQogICAgICAgICAgICAgICAgICAgICBjb29yZHM9YygieCIsICJ5IikpJT4lDQogIHN0X3NldF9jcnModmFsdWU9Mjg5OTIpDQoNCm1ldXNlLmdyaWQgPC0gc3RfYXNfc2YobWV1c2UuZ3JpZCwgDQogICAgICAgICAgICAgICAgICAgICAgIGNvb3Jkcz1jKCJ4IiwgInkiKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGNycz1zdF9jcnMobWV1c2Vfc2YpKQ0KDQojcHJpbnQgbWV1c2VncmlkIGRhdGEgdG8gc2VlIHRoYXQgaXQgaXMgbm93IGEgc2ltcGxlIGZlYXR1cmUgY29sbGVjdGlvbiB3aXRoIDUgZmllbGRzLCB4IGFuZCB5IHBvaW50cywgQW1lcnNmb29ydCBDUlMsIGV0Yy4uLg0KDQojbm93IHBsb3QgaXQgaW4gZ2dwbG90DQoNCnAxIDwtIGdncGxvdChkYXRhPW1ldXNlX3NmKSsNCiAgZ2VvbV9zZihhZXMoZmlsbD1sb2dMZWFkKSwgc2l6ZT00LCANCiAgICAgICAgICBzaGFwZT0yMSwgY29sb3I9IndoaXRlIiwgYWxwaGE9MC44KSsNCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKHR5cGU9InZpcmlkaXMiLCBuYW1lPSJsb2cocHBtKSIpKw0KICBsYWJzKHRpdGxlPSJMZWFkIGNvbmNlbnRyYXRpb25zIChESVNDUkVURSBWQVJJT0dSQU0iKQ0KDQpwMQ0KDQojbm93IHVzZSB0aGUgdmFyaW9ncmFtIGZ1bmN0aW9uIGZyb20gZ3N0YXQgdG8gZGV0ZXJtaW5lIGF1dG9jb3JyZWxhdGlvbg0KDQojc2FtcGxlIHZhcmlvZ3JhbSwgb2JzZXJ2ZWQgdmFyaW9ncmFtLCBldGMuLi5pcyB0aGUgbmFtZSBvZiB0aGUgdmFyaW9ncmFtIHdpdGhvdXQgdGhlIGxpbmUNCg0KbGVhZFZhciA8LSB2YXJpb2dyYW0obG9nTGVhZH4xLCBtZXVzZV9zZikNCnBsb3QobGVhZFZhciwgcGNoPTIwLCBjZXg9MS41LCBjb2w9ImJsYWNrIiwgDQogICAgIHlsYWI9ZXhwcmVzc2lvbigiU2VtaXZhcmlhbmNlICgiKmdhbW1hKiIpIiksIHhsYWI9IkRpc3RhbmNlIChtKSIsIG1haW49IkxlYWQgY29uY2VudHJhdGlvbnMgKGxvZyhwcG0pKSIpDQogICAgIA0KYGBgDQoNCkJlbG93IGlzIGEgZGlhZ3JhbSBvZiBob3cgdG8gaW50ZXJwcmV0IHRoZXNlIHZhcmlvZ3JhbXM6IA0KDQoNClRoZSAqKnJhbmdlKiogaXMgdGhlIGRpc3RhbmNlIGJleW9uZCB3aGljaCB0aGUgZGF0YSBhcmUgbm8gbG9uZ2VyIGNvcnJlbGF0ZWQuDQoNClRoZSAqKnNpbGwqKiBpcyB0aGUgdmFyaWFuY2Ugb2YgdGhlIHZhcmlhYmxlLg0KDQpUaGUgKipudWdnZXQqKiBpcyB0aGUgYXV0b2NvcnJlbGF0aW9uIGF0IHZlcnkgc21hbGwgc2NhbGVzLiBUaGUgbnVnZ2V0IHJlcHJlc2VudHMgaW5kZXBlbmRlbnQgZXJyb3IsIG1lYXN1cmVtZW50IGVycm9yIGFuZC9vciBtaWNyby1zY2FsZSB2YXJpYXRpb24gYXQgZmluZSBzcGF0aWFsIHNjYWxlcy4gSW4gYSBjb250aW51b3VzIHZhcmlhYmxlIHdlIHdvdWxkIGV4cGVjdCB0aGF0IHRoZSBudWdnZXQgZWZmZWN0IHdpbGwgYmUgemVybyBiZWNhdXNlIGF0IGRpc3RhbmNlIHplcm8gdGhlIHZhbHVlcyB3aWxsIGJlIHRoZSBzYW1lLiBIb3dldmVyIHNvbWUgdmFyaWFibGVzIGNhbiBjaGFuZ2UgaW4gYW4gYWJydXB0IG1hbm5lciBhbmQgc28gaW4gdmVyeSBzaG9ydCBkaXN0YW5jZSB0aGVyZSBpcyBhIGRpZmZlcmVuY2UuDQoNCklmIHRoZXJlIGlzIG5vIHN0cnVjdHVyZSAobm8gY29ycmVsYXRpb24pIGFuZCB0aGUgdmFyaWFibGUgaXMgdGhlcmVmb3JlIGEgcHVyZWx5IHJhbmRvbSB2YXJpYWJsZSwgdGhlIHZhcmlvZ3JhbSB3aWxsIGJlIGZsYXQgd2l0aCBhIG51Z2dldCBlZmZlY3QuDQoNCiMjIyBWYXJpb2dyYW0gTW9kZWwNCg0KQWZ0ZXIgcGxvdHRpbmcgdGhlIHNhbXBsZSB2YXJpb2dyYW0gd2UgY2FuIHRoZW4gZml0IGEgc3RhdGlzdGljYWwgbW9kZWwgdG8gdGhlIHBvaW50cy4gVGhlIG1vZGVsIGlzIGEgY29udGludW91cyBlc3RpbWF0ZSBvZiAkXGdhbW1hJCBhcyBhIGZ1bmN0aW9uIG9mIGRpc3RhbmNlOiAkXGhhdHtcZ2FtbWF9PWYoZCkkLg0KDQpXZSB3aWxsIGZpdCB0d28gZGlmZmVyZW50IGZ1bmN0aW9ucyBhIHNwaGVyaWNhbCBtb2RlbCBhbmQgYW4gZXhwb25lbnRpYWwgbW9kZWwuIFRoZSB0ZXh0IGhhcyBkZXNjcmlwdGlvbnMgb2YgdGhlIHZhcmlvdXMgZnVuY3Rpb25zLiBJbiBwcmFjdGljZSwgd2UgdGVuZCB0byBsb29rIGF0IHRoZSBnZW5lcmFsIHNoYXBlIG9mIHRoZSBwbG90IGFuZCB0aGVuIGNob29zZSBhIG1hdGhlbWF0aWNhbCBtb2RlbCB0aGF0IGZpdHMuIA0KDQojIyMjIFNwaGVyaWNhbCBNb2RlbA0KDQpgYGB7ciB2YXJpb30NCiMgbm90ZSBvdXQgaW5pdGlhbCBlc3RpbWF0ZXMgZm9yIHRoZSBzaWxsLCByYW5nZSwgYW5kIG51Z2dldA0Kc3BoLm1vZGVsIDwtIHZnbShwc2lsbD0wLjYsIG1vZGVsPSJTcGgiLCByYW5nZT03NTAsIG51Z2dldD0wLjA1KQ0Kc3BoLmZpdCA8LSBmaXQudmFyaW9ncmFtKG9iamVjdCA9IGxlYWRWYXIsIG1vZGVsID0gc3BoLm1vZGVsKQ0KDQpzcGguZml0DQoNCnBsb3QobGVhZFZhcixtb2RlbD1zcGguZml0LHBjaD0yMCxjZXg9MS41LGNvbD0iYmxhY2siLA0KICAgICB5bGFiPWV4cHJlc3Npb24oIlNlbWl2YXJpYW5jZSAoIipnYW1tYSoiKSIpLA0KICAgICB4bGFiPSJEaXN0YW5jZSAobSkiLCBtYWluID0gIkxlYWQgY29uY2VudHJhdGlvbnMgKGxvZyhwcG0pKSIsDQogICAgIHN1Yj0iUG9pbnRzOiBFbXBpcmljYWwsIExpbmU6IFNwaGVyaWNhbCBNb2RlbC9USEVPUkVUSUNBTCBWQVJJT0dSQU0iKQ0KDQojdGhpcyBpcyBhIG5vbmxpbmVhciBmdW5jdGlvbi4gVGhpcyBpcyBhIDMgdGVybSBtb2RlbC4gVGhlIG51Z2dldCA9IHkgaW50ZXJjZXB0IChiIGluIHk9bXgrYikNCiN0aGUgcmFuZ2UgeCBheGlzDQojdGhlIHNpbGwgdGVsc3lvdSB0aGUgdmFsdWVzIG9mIHNlbWl2YXJpYW5jZSB3aGVuIHRoZXJlIGlzIG5vIGF1dG9jb3JyZWxhdGlvbi4gdGhlIHkgYXhpcw0KDQojdGhlc2UgdGVybXMgZ2l2ZSB5b3UgdGhlIGRpc3RhbmNlIGF0IHdoaWNoIHBvaW50IHRoZSBjdXJ2ZSBmbGF0ZW5zIG91dC4gVmFsdWVzIHBhc3QgdGhlIHJhbmdlIGFyZSBub3QgYXV0b2NvcnJlbGF0ZWQNCg0KI3RoZSBzaGFwZSBvZiB0ZWggdmFyaW9ncmFtIGJlbG93DQoNCg0KYGBgDQoNClRoZSBzb2xpZCBsaW5lIGFib3ZlIGlzIGEgZml0dGVkLCB0aGVvcmV0aWNhbCwgdmFyaW9ncmFtIG1vZGVsLiBUaGlzIGlzIGRvbmUgYnkgaXRlcmF0aW9ucy4gV2UgaGF2ZSBnb25lIGZyb20gb3VyIG9ic2VydmVkIGRhdGEgKGVtcGlyaWNhbCBjYXRlZ29yaWNhbCBkYXRhKSBhbmQgd2UgaGF2ZSBmaXR0ZWQgYSBsaW5lIHNvIHRoYXQgaXQgaXMgbm93IGEgY29udGludW91cyBtb2RlbCEgQ09PTC4gVGhlIHNwaGVyaWNhbCBtb2RlbCBpczogDQoNCiRcaGF0e1xnYW1tYX0oZCk9YitDXzAoMS41KFxmcmFje2R9e3J9KS0wLjUoXGZyYWN7ZF4zfXtyfSkpJA0KDQoqKmIqKiBpcyB0aGUgbnVnZ2V0DQoNCioqJENfMCQqKiBpcyB0aGUgc2lsbA0KDQoqKmQqKiBpcyB0aGUgZGlzdGFuY2UNCg0KKipyKiogaXMgdGhlIHJhbmdlDQoNCg0KIyMjIyBFeHBvbmVudGlhbCBNb2RlbA0KDQpUaGVyZSBhcmUgYWxzbyBleHBvbmVudGlhbCBtb2RlbHM6DQoNCiRcaGF0e1xnYW1tYX0oZCk9IGIrQ18wKDEtZV57LWQvYX0pJCB3aGVyZSAkYSA9IHIvMyQgYW5kIGFsbCBvdGhlciB0ZXJtcyBhcmUgdGhlIHNhbWUgYXMgYWJvdmUuDQoNCiMjIyBUb3kgRXhhbXBsZQ0KDQpJbWFnaW5lIHlvdSBoYXZlIGEgZGF0YWZyYW1lIHdpdGggeCwgeSwgYW5kIHo6DQoNCmBgYHtyIHRveTF9DQojYW55IGRhdGEgZnJhbWUgd2l0aCB4eXoNCmZvbyA8LSBkYXRhLmZyYW1lKHg9YygxLCAzLCAxLCA0LCA1KSwgDQogICAgICAgICAgICAgICAgICB5PWMoNSwgNCwgMywgNSwgMSksIA0KICAgICAgICAgICAgICAgICAgej1jKDEwMCwgMTA1LCAxMDUsIDEwMCwgMTE1KSkNCg0KI3Bsb3QgaXQNCg0KcDIgPC0gZ2dwbG90KCkgKyANCiAgZ2VvbV9wb2ludChkYXRhPWZvbyxhZXMoeD14LHk9eSxzaXplPXopKSArIA0KICBsaW1zKHg9YygwLDYpLHk9YygwLDYpKQ0KcDINCmBgYA0KDQpOb3csIGltYWdpbmUgeW91IGRvbid0IGtub3cgeiBhdCAoMiw0KToNCg0KYGBge3Igbm96fQ0KcDIgPC0gcDIgKyANCiAgZ2VvbV9wb2ludChhZXMoeD0yLHk9NCksY29sb3I9InBsdW0iLHNpemU9MTAsc2hhcGU9MCkgKw0KICBnZW9tX3BvaW50KGFlcyh4PTIseT00KSxjb2xvcj0icGx1bSIsc2l6ZT02LHNoYXBlPTYzKQ0KcDINCmBgYA0KDQoNCiMjIyMgRmluZCA/DQoNCkludGVycG9sYXRlIHdpdGggKipLcmlnaW5nKiogdG8gZmluZCB6IGF0ICgyLDQpLiBUaGUgRXVjbGlkZWFuIGRpc3RhbmNlIHRvIG91ciB1bmtub3duIHBvaW50IGZyb20gZWFjaCBvZiBvdXIga25vd24gcG9pbnRzIGlzIGEgdmVjdG9yOiANCg0KJGRfaT0kIA0KXGJlZ2lue2JtYXRyaXh9DQogIDEgJiAxLjQxNCBcXA0KICAyICYgMS4wMDAgXFwNCiAgMyAmIDEuNDE0IFxcDQogIDQgJiAyLjIzNiAgXFwNCiAgNSAmIDQuMjQzIFxcDQogIFxlbmR7Ym1hdHJpeH0NCiAgDQpUbyBnZXQgJFxoYXR7Wn0oc18wKSQgeW91IG5lZWQgdG8gY2FsY3VsYXRlICRcbGFtYmRhJC4gVG8gZG8gdGhpcywgZml0IGEgdmFyaW9ncmFtIGJ5IHBsb3R0aW5nIHNlbWl2YXJpYW5jZSAkKFxnYW1tYSkkIGFnYWluc3QgZGlzdGFuY2UgJChkKSQgYW5kIGdldCB0aGUgZnVuY3Rpb24gdGhhdCBwcmVkaWN0cyAkXGdhbW1hJC4gDQoNCkltYWdpbmUgeW91IGRpZCB0aGlzIGFuZCBmb3VuZCB0aGUgdmFyaW9ncmFtIG1vZGVsIGlzOiANCg0KJFxoYXR7XGdhbW1hfT0wKzEzLjUoZCkkDQoNCkhvdyB3aWxsIHdlIGdldCB0aGUgd2VpZ2h0cyAkKFxsYW1iZGEpJD8gDQoNCiRcbGFtYmRhPWckJFxHYW1tYV57LTF9JA0KDQokXEdhbW1hJCA9IG1hdHJpeCBvZiBzZW1pdmFyaWFuY2Ugb2YgYWxsIHNhbXBsZWQgcG9pbnQgcGFpcnMgcHJlZGljdGVkIGFzIGEgZnVuY3Rpb24gb2YgZXVjbGlkaWFuIGRpc3RhbmNlIHVzaW5nIHRoZSBmaXR0ZWQgdmFyaW9ncmFtIG1vZGVsDQoNCiRnJCA9IGEgdmVjdG9yIG9mIHByZWRpY3RlZCBzZW1pdmFyaWFuY2UgZm9yIHVua25vd24gcG9pbnRzIHVzaW5nIGV1Y2xpZGlhbiBkaXN0YW5jZXMgZnJvbSBrbm93biBwb2ludHMNCg0KLi4uYW5kIG9uY2Ugd2UgaGF2ZSAkXGxhbWJkYSQgd2UgY2FuIGludGVycG9sYXRlIChwcmVkaWN0KSBvdmVyIGEgc3VyZmFjZS4gDQoNCiMjIyMgU3RlcCAxDQoNCkNhbGN1bGF0ZSAkZyQgZm9yIHRoZSBwb2ludCB5b3Ugd2FudCB0byBwcmVkaWN0LiANCg0KJGc9MCsxMy41KGRfaSkkOg0KDQpgYGB7ciBnfQ0KDQojZXVjbGlkaWFuIGRpc3RhbmNlIGZyb20gZWFjaCBwb2ludCB0byBvdXIgdW5rbm93biBwb2ludDogDQpkX2kgPC0gc3FydCgoMi1mb28keCleMisoNC1mb28keSleMikNCg0KI25vdyB1c2UgeW91ciB2YXJpb2dyYW0gbW9kZWwgZXF1YXRpb24gKGdpdmVuIHRvIHlvdSBieSBBbmR5KSBhbmQgcGx1ZyBpbiB5b3VyIGRfaSB2YWx1ZXMgdG8gc29sdmUgZm9yIGcNCmcgPC0gZF9pKjEzLjUNCg0KZw0KDQpgYGANCg0KJCRnPSQkDQpcYmVnaW57Ym1hdHJpeH0NCiAgMSAmIDE5LjA5IFxcDQogIDIgJiAxMy41MCBcXA0KICAzICYgMTkuMDkgXFwNCiAgNCAmIDMwLjE5ICBcXA0KICA1ICYgNTcuMjggXFwNCiAgXGVuZHtibWF0cml4fQ0KICANCiMjIyMgU3RlcCAyDQoNCkZpbmQgJFxHYW1tYSQuIA0KDQpIZXJlIGlzIHRoZSBkaXN0YW5jZSBtYXRyaXggYmV0d2VlbiBhbGwgbWVhc3VyZWQgcG9pbnRzICh1c2luZyB0aGUgYGBgcHJveHlgYGAgbGlicmFyeSk6IA0KDQpgYGB7ciBMYW1iZGF9DQoNCmV1Y2xpZCA8LSBkaXN0KGZvb1sxOjJdLCBtZXRob2Q9ImV1Y2xpZGVhbiIpDQpldWNsaWQNCg0KYGBgDQoNCg0KTm93IG11bHRpcGx5IHRoaXMgYnkgdGhlIHZhcmlvZ3JhbSBtb2RlbCB0byBnZXQgJFxHYW1tYSQ6DQoNCmBgYHtyfQ0KR2FtbWEgPC0gMTMuNSpldWNsaWQNCg0KR2FtbWENCmBgYA0KDQojIyMjIFN0ZXAgMw0KDQpOb3cgdGFrZSB0aGUgaW52ZXJzZSBvZiB0aGUgbWF0cml4IGZvciAkXEdhbW1hJCB1c2luZyBgYGBzb2x2ZWBgYCBmdW5jdGlvbjoNCg0KYGBge3IgaW52ZXJzZX0NCmludmVyc2UgPC0gc29sdmUoR2FtbWEpDQppbnZlcnNlDQpgYGANCg0KIyMjIyBTdGVwIDQNCg0KRmluZCAkXGxhbWJkYSQgd2hlcmUgJFxsYW1iZGE9ZyQkXEdhbW1hXnstMX0kOg0KDQpgYGB7ciBiYWJ5bGFtYmRhfQ0KDQpiYWJ5X2xhbWJkYSA8LSBnICUqJSBpbnZlcnNlDQoNCmJhYnlfbGFtYmRhDQoNCmBgYA0KDQojIyMjIFN0ZXAgNQ0KDQpVc2UgJFxsYW1iZGFfaSQgdG8gY3JlYXRlIHRoZSBmaW5hbCBwcmVkaWN0aW9uIGVxdWF0aW9uOg0KDQokXGhhdHtafShzXzApPTAuMjQ0KDEwMCkrMC40OTEoMTA1KSswLjI1NigxMDUpLTAuMDEzKDEwMCktMC4wMjgoMTE1KT05OC4zJA0KDQoNCiMjIyBMZWFkIEV4YW1wbGUNCg0KIyMjIyBQcmVkaWN0DQoNCmBgYHtyIGxlYWQxfQ0KbWV1c2UuZ3JpZA0KDQptZXVzZV9ncmlkX3NmIDwtIHN0X2FzX3NmKG1ldXNlLmdyaWQpDQoNCmxlYWRWYXIgPC0gdmFyaW9ncmFtKGxvZ0xlYWR+MSwgbWV1c2Vfc2YpDQoNCiN1c2UgdGhlIHBsb3QgdG8gZGV0ZXJtaW5lIHRoZSBwYXJhbWV0ZXJzIGZvciB2Z20oKQ0KcGxvdChsZWFkVmFyKQ0KDQpsZWFkTW9kZWwgPC0gdmdtKHBzaWxsPTAuNiwgbW9kZWw9IlNwaCIsIHJhbmdlPTc1MCwgbnVnZ2V0PTAuMDUpDQojdGhlcmUgYXJlIGxvdHMgb2YgZGlmZmVyZW50IG1vZGVscy4gV2UgYXJlIHVzaW5nIHNwaGVyaWNhbCBiYXNlZCBvbiB0aGUgdmFyaW9ncmFtIGxvb2sNCg0KbGVhZEZpdCA8LSBmaXQudmFyaW9ncmFtKG9iamVjdCA9IGxlYWRWYXIsIG1vZGVsID0gbGVhZE1vZGVsKQ0KDQpsZWFkR3N0YXQgPC0gZ3N0YXQoZm9ybXVsYSA9IGxvZ0xlYWR+MSwgbG9jYXRpb25zID0gbWV1c2Vfc2YsIA0KICAgICAgICAgICAgICAgICAgIG1vZGVsID0gbGVhZEZpdCkNCmxlYWRLcmlnZV9zZiA8LSBwcmVkaWN0KGxlYWRHc3RhdCxuZXdkYXRhID0gbWV1c2VfZ3JpZF9zZikNCg0KbGVhZEtyaWdlX3NmDQpgYGANCg0KQWJvdmUgeW91IGNhbiBzZWUgdGhhdCB0aGUgYGBgbGVhZEtyaWdlYGBgIGlzIGFuIGBgYHNmYGBgIG9iamVjdCB0aGF0IGhhcyBib3RoIHByZWRpY3RlZCBzdXJmYWNlIChgYGB2YXIxLnByZWRgYGApIGFuZCB0aGUgdmFyaWFuY2UgYXJvdW5kIHRob3NlIHByZWRpY3Rpb25zIChgYGB2YXIxLnZhcmBgYCkuIA0KDQpDb252ZXJ0IGBgYHNmYGBgIGludG8gYGBgcmFzdGBgYDoNCmBgYHtyIGNvbnZlcnR9DQojIHVzZSB0aGUgaG9tZW1hZGUgZnVuY3Rpb246DQpzZl8yX3Jhc3QgPC1mdW5jdGlvbihzZk9iamVjdCx2YXJpYWJsZUluZGV4ID0gMSl7DQogICMgY29lcmNlIHNmIHRvIGEgZGF0YS5mcmFtZQ0KICBkZk9iamVjdCA8LSBkYXRhLmZyYW1lKHN0X2Nvb3JkaW5hdGVzKHNmT2JqZWN0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB6PWFzLmRhdGEuZnJhbWUoc2ZPYmplY3QpWyx2YXJpYWJsZUluZGV4XSkNCiAgIyBjb2VyY2UgZGF0YS5mcmFtZSB0byBTcGF0UmFzdGVyDQogIHJhc3RPYmplY3QgPC0gcmFzdChkZk9iamVjdCxjcnM9Y3JzKHNmT2JqZWN0KSkNCiAgDQogIG5hbWVzKHJhc3RPYmplY3QpIDwtIG5hbWVzKHNmT2JqZWN0KVt2YXJpYWJsZUluZGV4XQ0KICANCiAgcmV0dXJuKHJhc3RPYmplY3QpDQp9DQoNCmxlYWRLcmlnZV9yYXN0IDwtIHNmXzJfcmFzdChsZWFkS3JpZ2Vfc2YpDQoNCmxlYWRLcmlnZV9yYXN0DQoNCmBgYA0KDQpOaWNlISBOb3csIGxldCdzIHBsb3QgdGhlIHByZWRpY3Rpb24gc3VyZmFjZToNCmBgYHtyIHByZWRzdXJmLCBtZXNzYWdlPUZ9DQoNCiMgYW5kIHBsb3QNCmdncGxvdCgpICsNCiAgZ2VvbV9zcGF0cmFzdGVyKGRhdGE9bGVhZEtyaWdlX3Jhc3QsIG1hcHBpbmcgPSBhZXMoZmlsbD12YXIxLnByZWQpLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJsb2cocHBtKSIsbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIA0KICBsYWJzKHRpdGxlPSJMZWFkIGNvbmNlbnRyYXRpb25zIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCk5vdywgdG8gbG9vayBhdCB0aGUgdmFyaWFuY2UgYXJvdW5kIHRob3NlIHByZWRpY3Rpb25zLCB5b3UgbmVlZCB0byB0YWtlIHRoZSBzcXVhcmUgcm9vdCBvZiBgYGB2YXIxLnZhcmBgYCBzbyB0aGF0IHRoZSB1bml0cyBtYXRjaCB0aGUgYGBgdmFyMS5wcmVkYGBgIHVuaXRzLiANCg0KYGBge3IgdmFyaWFuY2UxfQ0KbGVhZEtyaWdlX3NmJHZhcjEudmFyLnNxcnQgPC0gc3FydChsZWFkS3JpZ2Vfc2YkdmFyMS52YXIpDQoNCmxlYWRLcmlnZV9yYXN0IDwtIHNmXzJfcmFzdChsZWFkS3JpZ2Vfc2YsdmFyaWFibGVJbmRleCA9IDQpDQpsZWFkS3JpZ2VfcmFzdA0KDQpgYGANCg0KQW5kIHBsb3QgaXQ6IA0KYGBge3IgZ2d9DQojIGFuZCBwbG90DQpnZ3Bsb3QoKSArDQogIGdlb21fc3BhdHJhc3RlcihkYXRhPWxlYWRLcmlnZV9yYXN0LCBtYXBwaW5nID0gYWVzKGZpbGw9dmFyMS52YXIuc3FydCApLGFscGhhPTAuOCkgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXModHlwZSA9ICJ2aXJpZGlzIixuYW1lPSJsb2cocHBtKSIsbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArIA0KICBsYWJzKHRpdGxlPSJWYXJpYW5jZSBvZiBsZWFkIGNvbmNlbnRyYXRpb25zIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyMjIENyb3NzIFZhbGlkYXRlDQoNClVzaW5nIGBgYGtyaWdlLmN2YGBgIHdlIGNhbiBjcm9zcyB2YWxpZGF0ZS4gVGhlIGRlZmF1bHQgaXMgdG8gbGVhdmUtb25lLW91dCBjcm9zcyB2YWxpZGF0ZSAoTE9PQ1YpLCBidXQgeW91IGNhbiB0ZWxsIGl0IHRvIGstZm9sZCB0b286DQoNCmBgYHtyIGNyb3NzIHZhbGlkYXRlfQ0KbGVhZEtyaWdlTE9PQ1Zfc2YgPC0ga3JpZ2UuY3YoZm9ybXVsYSA9IGxvZ0xlYWR+MSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbnMgPSBtZXVzZV9zZiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9IGxlYWRGaXQsIHZlcmJvc2UgPSBGQUxTRSkNCmxlYWRLcmlnZUxPT0NWX3NmDQpgYGANCg0KTm93IGNhbGN1bGF0ZSB0aGUgJFJeMiQ6DQpgYGB7ciByMn0NCmNvcihsZWFkS3JpZ2VMT09DVl9zZiRvYnNlcnZlZCwgbGVhZEtyaWdlTE9PQ1Zfc2YkdmFyMS5wcmVkKV4yDQpgYGANClRoZSBMT09DViAkUl4yJCBpcyAwLjYxOTMgd2hpY2ggaXMgb2J0YWluZWQgZnJvbSB0aGUgb2JzZXJ2ZWQgdnMuIHByZWRpY3RlZCB2YWx1ZXMuIEFsc28gbm90ZSwgYWxsIHRoZSBvdGhlciBjb29sIHZhbHVlcyB5b3UgZ2V0IGZyb20gdGhlIGBgYGtyaWdlLmN2YGBgIGZ1bmNpb24uIA0KDQoNCiMjIyMgUG9zdHNjcmlwdCBBdXRvbWFwDQoNCllvdSBjYW4gZWFzaWx5IGdldCB0aGUgbnVnZ2V0LCBzaWxsLCBhbmQgcmFuZ2UgdXNpbmcgdGhpcyBjb2RlOg0KDQpgYGB7ciBhdXRvfQ0KQ0FWYXJBdXRvIDwtIGF1dG9maXRWYXJpb2dyYW0oZm9ybXVsYSA9IEFOTlVBTH4xLGlucHV0X2RhdGEgPSBwcmNwQ0FzZikNCnN1bW1hcnkoQ0FWYXJBdXRvKQ0KDQpwbG90KENBVmFyQXV0bykNCmBgYA0KDQpZb3UgY2FuIGFsc28gcXVpY2tseSBnZXQgY3Jvc3MtdmFsaWRhdGlvbjoNCg0KYGBge3IgY3Jvc3N2YWx9DQpDQUF1dG9LcmlnZUxPT0NWIDwtIGF1dG9LcmlnZS5jdihmb3JtdWxhID0gQU5OVUFMfjEsIGlucHV0X2RhdGEgPSBwcmNwQ0FzZiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IGMoRkFMU0UsRkFMU0UpKQ0KDQpzdW1tYXJ5KENBQXV0b0tyaWdlTE9PQ1YpDQoNCmNvcihDQUF1dG9LcmlnZUxPT0NWJGtyaWdlLmN2X291dHB1dCRvYnNlcnZlZCwgQ0FBdXRvS3JpZ2VMT09DViRrcmlnZS5jdl9vdXRwdXQkdmFyMS5wcmVkKV4yDQoNCg0KYGBgDQoNCkhlcmUgd2UgZ2V0IGEgTE9PQ1YgJFJeMiQgb2YgMC45MDMwIHdoaWNoIGlzIHZlcnkgKHZlcnkpIGNsb3NlIHRvIHdoYXQgd2UgZ290IHdpdGggdGhlIGNyb3NzIHZhbGlkYXRpb24gd2UgZGlkIG1hbnVhbGx5IGFib3ZlLg0KDQoNCjxkaXYgY2xhc3M9InRvY2lmeS1leHRlbmQtcGFnZSIgZGF0YS11bmlxdWU9InRvY2lmeS1leHRlbmQtcGFnZSIgc3R5bGU9ImhlaWdodDogMDsiPjwvZGl2Pg0K