Recap
So, last time we learned all about the unconstrained spatial interaction model; how we can use it to estimate flows in a system using values for origin emissiveness or destination attractiveness; how we can tweak the estimates the model produces through adjusting either the parameters associated with the predictor variables, or though using different predictor variables or updating their values; and how we can improve the fits of the model further by calibrating the parameters through using a Poisson regression model.
We saw that even after calibration, our model still only explained around 60% of the variation in the flows that we observed in our system, so can we do any better? Well yes, yes we can!
Constrained Models
If we return to Alan Wilson’s 1971 paper, he introduces a full family of spatial interaction models of which the unconstrained model is just the start. And indeed since then, there have been all number of incremental advances and alternatives (such as Stewart Fotheringham’s Competing Destinations models, Pooler’s production/attraction/cost relaxed models, Stillwell’s origin/destination parameter specific models and mine and Alan’s own multi-level model (to name just a few).
In this session we will explore the rest of Wilson’s family - the Production (origin) Constrained Model; the Attraction (destination) constrained model; and the Doubly Constrained Model.
We will see how we can, again, use a Poisson regression model in R to calibrate these models and how, once calibrated, we can use the models in different contexts, such as Land Use Transportation Interaction (LUTI) modelling, retail modelling and migration modelling.
1. Production and Attraction Constrained Models
Right, let’s get ourselves set up again…
Open up R Studio and set up a new project working directory. Open a new R script and save it into your new working directory.
Check the file is in your working directory and R can see it…
getwd()
list.files(getwd())
Library some packages (the four at the bottom you probably won’t need):
library(sp)
library(reshape2)
library(geojsonio)
library(rgdal)
library(downloader)
library(maptools)
library(dplyr)
library(broom)
library(stplanr)
library(ggplot2)
library(MASS)
library(sf)
library(tmap)
library(tmaptools)
library(stringr)
And check that you have our little test commuting data matrix from last time - cdatasub
- sitting in your global environment. If you don’t, then go back to the last practical here and follow the steps from the top until you have cdatasub in your global environment. It should look something like this:
cdatasubmat <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "Total", margins=c("Orig", "Dest"))
cdatasubmat
Wilson’s real contribution to the field was in noticing that the unconstrained gravity model was sub-optimal as it did not make use of all of the available data in the system we are studying.
If we recall the estimates from our unconstrained model, none of the estimates summed to the observed in and out-flow totals:
cdatasubmat2
Our estimates did sum to the grand total of flows, but this is because we were really fitting a ‘total constrained’ model which used \(k\) - our constant of proportionality - to ensure everything sort of added up (to within 1 commuter).
Where we have a full flow matrix to calibrate parameters, we can incorporate the row (origin) totals, column (destination) totals or both origina and destination totals to constrain our flow estimates to these known values.
There are various reasons for wanting to do this, for example:
If We are interested in flows of money into businesses or customers into shops, might have information on the amount of disposable income and shopping habits of the people living in different areas from loyalty card data. This is known information about our origins and so we could constrain our spatial interaction model to this known information - we can make the assumption that this level of disposable income remains the same. We can then use other information about the attractiveness of places these people might like to shop in (store size, variety / specialism of goods etc.), to estimate how much money a new store opening in the area might make, or if a new out-of-town shopping centre opens, how much it might affect the business of shops in the town centre. This is what is known in the literature as the ‘retail model’ and is perhaps the most common example of a Production (orign) Constrained Spatial Interaction Model
We might be interested in understanding the impact of a large new employer in an area on the flows of traffic in the vicinity or on the demand for new worker accommodation nearby. A good example of where this might be the case is with large new infrastructure developments like new airports. For example, before the go-ahead for the new third runway at Heathrow was given, one option being considered was a new runway in the Thames Estuary. If a new airport was built here, what would be the potential impact on transport flows in the area and where might workers commute from? This sort of scenario could be tested with an Attraction (destination) Constrained Spatial Interaction Model where the number of new jobs in a destination is known (as well as jobs in the surrounding area) and the model could be used to estimate where the workers will be drawn from (and their likely travel-to-work patterns). This model is exactly the sort of model Land Use Transport Interaction (LUTI) model that was constructed by the Mechanicity Team in CASA - details here if you are interested…
We might be interested in understanding the changing patterns of commuting or migration over time. Data from the Census allows us to know an accurate snap-shot of migrating and commuting patterns every 10 years. In these full data matrices, we know both the numbers of commuters/migrants leaving origins and arriving at destinations as well as the interactions between them. If we constrain our model estimates to this known information at origin and destination, we can examine various things, including:
- the ways that the patterns of commuting/migration differ from the model predictions - where we might get more migrant/commuter flows than we would expect
- how the model parameters vary over time - for example how does distance / cost of travel affect flows over time? Are people prepared to travel further or less far than before?
2. Production-constrained Model
- \[T_{ij} = A_i O_i W_j^\alpha d_{ij}^-\beta \]
where
- \[O_{i} = \sum_j T_{ij}\]
and
- \[ A_i = \frac{1}{\sum_j W_j^\alpha d_{ij}^-\beta}\]
In the production-constrained model, \(O_i\) does not have a parameter as it is a known constraint. \(A_i\) is known as a balancing factor and is a vector of values which relate to each origin, \(i\), which do the equivalent job to \(k\) in the unconstrained/total constrained model but ensure that flow estimates from each origin sum to the know totals, \(O_i\) rather than just the overall total.
Now at this point, we could calculate all of the \(O_i\)s and the \(A_i\)s by hand for our sample system and then set about guessing/estimating the parameter values for the rest of the model, but as you might have already suspected from last time, we can use R and glm
to make it really easy and do all of that for us - woo hoo!
We set about re-specifying the Production-Constrained model as a Poisson regression model in exactly the same way as we did before. We need to take logs of the right-hand side of equation and assume that these are logarithmially linked to the Poisson distributed mean (\(\lambda_{ij}\)) of the \(T_{ij}\) variable. As such, Equation (1) becomes:
- \[\lambda_{ij} = exp(k + \mu_{i} + \alpha \ln W_j - \beta \ln d_{ij})\]
In Equation (4) \(\mu_{i}\) is the equivalent of the vector of balancing factors \(A_i\), but in regression / log-linear modelling terminology can also be described as either dummy variables or fixed effects. In practical terms, what this means is that in our regression model, \(\mu_i\) is modelled as a categorical predictor and therefore in the Poisson regression model, we don’t use the numeric values of \(O_i\), we use a categorical identifier for the origin. In terms of the example table above, for Barking and Dagenham we wouldn’t use 5675 as we would if we were fitting Equation 1, we would just use ‘Barking and Dagenham’. You might have noticed that \(k\) makes an appearance again - this is because now we have a regression equation again, we need an intercept value - this is estimated by fitting the model.
So, let’s give this model a whirl…
#run a production constrained SIM
prodSim <- glm(Total ~ Orig+log(wj2_destsal)+log(dist), na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
#let's have a look at it's summary...
summary(prodSim)
Call:
glm(formula = Total ~ Orig + log(wj2_destsal) + log(dist), family = poisson(link = "log"),
data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-71.314 -17.901 -6.669 9.587 58.776
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 7.99911 0.11380 70.29 <2e-16 ***
OrigBarnet 0.98222 0.01505 65.24 <2e-16 ***
OrigBexley 0.95836 0.01565 61.25 <2e-16 ***
OrigBrent 0.47875 0.01575 30.39 <2e-16 ***
OrigBromley 1.48752 0.01540 96.59 <2e-16 ***
OrigCamden -1.01944 0.01949 -52.30 <2e-16 ***
OrigCity of London -3.29114 0.05398 -60.97 <2e-16 ***
log(wj2_destsal) 2.04693 0.01015 201.76 <2e-16 ***
log(dist) -2.24218 0.01138 -197.08 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 176846 on 41 degrees of freedom
Residual deviance: 28783 on 33 degrees of freedom
AIC: 29115
Number of Fisher Scoring iterations: 5
So, what do we have?
Well, there are the elements of the model output that should be familiar from the unconstrained model:
the \(k\) parameter, or the intercept for the model - which in this case is 7.99911
the \(\alpha\) parameter related to the destination attractiveness: 2.04693
the \(\beta\) distance decay parameter: -2.24218
We can see from the standard outputs from the model that all of the explanatory variables are statistically significant (***) and the z-scores indicate that the destination salary is having the most influence on the model, with distance following closely behind.
And then we have a series of parameters which are the vector of \(\mu_i\) values associated with our origin constraints. The eagle eyed among you will notice that we are missing a parameter for Barking and Dagenham. No, the model hasn’t just taken a dislike to the home of Ford Motorcar production and right wing populism in the UK, it’s because dummy parameter values are presented in relation to a reference variable and in this case the default variable is the first one in our list - Barking and Dagenham.
The way that we can interpret this is that the City of London is around 3.3 times less likely to be an origin for commuters than the reference category (Barking and Dagenham), which by default is set to 0.
Now of course, it might be that Barking and Dagenham is not the most intuitive contrasting variable to use. Fortunately we can change the The particular way in which the model choses this variable using the contrasts
option. I won’t dwell much on contrasts, but if you would like to learn more, there are lots of good webpages out there including this one and you should consult the R help system - search for ?contrasts
. For now, let’s just swap the contrasting variable from the first to the last Origin and see what happens to the parameter values:
#set the contrasts
options(contrasts=c('contr.SAS','contr.SAS'))
prodSim2 <- glm(Total ~ Orig+log(wj2_destsal)+log(dist), na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
#let's have a look at it's summary...
summary(prodSim2)
Call:
glm(formula = Total ~ Orig + log(wj2_destsal) + log(dist), family = poisson(link = "log"),
data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-71.314 -17.901 -6.669 9.587 58.776
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 4.70796 0.11931 39.46 <2e-16 ***
OrigBarking and Dagenham 3.29114 0.05398 60.97 <2e-16 ***
OrigBarnet 4.27337 0.05240 81.55 <2e-16 ***
OrigBexley 4.24950 0.05294 80.28 <2e-16 ***
OrigBrent 3.76989 0.05251 71.79 <2e-16 ***
OrigBromley 4.77866 0.05310 90.00 <2e-16 ***
OrigCamden 2.27171 0.05319 42.71 <2e-16 ***
log(wj2_destsal) 2.04693 0.01015 201.76 <2e-16 ***
log(dist) -2.24218 0.01138 -197.08 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 176846 on 41 degrees of freedom
Residual deviance: 28783 on 33 degrees of freedom
AIC: 29115
Number of Fisher Scoring iterations: 5
Notice how the intercept value has changed and the dummy parameters for each origin now relate to how they compare to the City of London. As we could expect for this area (which is a commuting destination rather than an origin) all other origins in our system send between 2 and 4.25 times as many commuters.
The default contrast option is R is to use ‘Treatment’ or dummy coding, so if you would like to switch the contrasts back to treatment, it’s simple to do:
options(contrasts=c('contr.treatment','contr.treatment'))
2.1 Model Estimates
Now at this point you will be wanting to know what affect the constraints have had on the estimates produced by the model, so let’s plug the parameters back into Equation 4 and take a look…
First run the model again using OrigCodeNew instead of Orig… (note that because of the code order, the model runs like the )
prodSim <- glm(Total ~ OrigCodeNew+log(wj2_destsal)+log(dist), na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
summary(prodSim)
Call:
glm(formula = Total ~ OrigCodeNew + log(wj2_destsal) + log(dist),
family = poisson(link = "log"), data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-71.314 -17.901 -6.669 9.587 58.776
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 4.70796 0.11931 39.46 <2e-16 ***
OrigCodeNewE09000002 3.29114 0.05398 60.97 <2e-16 ***
OrigCodeNewE09000003 4.27337 0.05240 81.55 <2e-16 ***
OrigCodeNewE09000004 4.24950 0.05294 80.28 <2e-16 ***
OrigCodeNewE09000005 3.76989 0.05251 71.79 <2e-16 ***
OrigCodeNewE09000006 4.77866 0.05310 90.00 <2e-16 ***
OrigCodeNewE09000007 2.27171 0.05319 42.71 <2e-16 ***
log(wj2_destsal) 2.04693 0.01015 201.76 <2e-16 ***
log(dist) -2.24218 0.01138 -197.08 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 176846 on 41 degrees of freedom
Residual deviance: 28783 on 33 degrees of freedom
AIC: 29115
Number of Fisher Scoring iterations: 5
Now Create some \(O_i\) and \(D_j\) columns and store the total in and out flow matrix margins in them. Note, in the syntax below, I use the forward-pipe or %>% operator. This is probably something a bit new and warrents a bit of explaining. If you’d like to know more about how pipes can be used in R, visit the magrittr vignette page here - for now, just think of %>% as a bit like saying “then…” in your code
#create some Oi and Dj columns in the dataframe and store row and column totals in them:
#to create O_i, take cdatasub ...then... group by origcodenew ...then... summarise by calculating the sum of Total
O_i <- cdatasub %>% group_by(OrigCodeNew) %>% summarise(O_i = sum(Total))
cdatasub$O_i <- O_i$O_i[match(cdatasub$OrigCodeNew,O_i$OrigCodeNew)]
D_j <- cdatasub %>% group_by(DestCodeNew) %>% summarise(D_j = sum(Total))
cdatasub$D_j <- D_j$D_j[match(cdatasub$DestCodeNew,D_j$DestCodeNew)]
Now fish the coefficients out of the prodsim glm object
#You can do this in a number of ways, for example this prints everything:
prodSim_out <- tidy(prodSim)
prodSim_out
Or, more usefully, pull out the parameter values for \(\mu_i\) and store them back in the dataframe along with \(O_i\) and \(D_j\)
# or you can just pull out the coefficients and put them into an object
coefs <- as.data.frame(prodSim$coefficients)
#then once you have done this, you can join them back into the dataframe using a regular expression to match the bits of the identifier that you need - *note, this bit of code below took me about 2 hours to figure out!*
cdatasub$mu_i <- coefs$`prodSim$coefficients`[match(cdatasub$OrigCodeNew,sub(".*OrigCodeNew","", rownames(coefs)))]
#now, where we have missing values for our reference mu_i variable, fill those with 1s
cdatasub$mu_i <- ifelse(is.na(cdatasub$mu_i),0,cdatasub$mu_i)
head(cdatasub)
OK, now we can save our parameter values into some variables…
k <- prodSim$coefficients[1]
mu_i <- prodSim$coefficients[2:7]
alpha <- prodSim$coefficients[8]
beta <- prodSim$coefficients[9]
And we’re ready to generate our estimates:
cdatasub$prodsimest1 <- exp(k+(cdatasub$mu_i)+(alpha*log(cdatasub$wj2_destsal))+(beta*log(cdatasub$dist)))
Now of course we could also have done this the easy way, but again, I think it helps doing it the hard way, especially if we want to play with the parameters by hand or adjust any of the input values when running some what-if scenarios.
Here’s the easy way again for those who can’t remember:
cdatasub$prodsimFitted <- fitted(prodSim)
head(cdatasub)
2.2 Assessing the model output
So what do the outputs from our Production Constrained Model look like? How has the goodness-of-fit improved and how can we start to use this a bit like a retail model and assess the likely impacts of changing destination attractiveness etc.?
2.2.1 The flow matrix
#first round the estimates
cdatasub$prodsimFitted <- round(fitted(prodSim),0)
#now we can create pivot table to turn paired list into matrix (and compute the margins as well)
cdatasubmat3 <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "prodsimFitted", margins=c("Orig", "Dest"))
cdatasubmat3
And compared with the original observed data?
cdatasubmat
Here it is very easy to see the Origin Constraints working. The sum across all destinations for each origin in the estimated matrix is exactly the same as the same sum across the observed matrix - \(\sum_j T_{ij} = \sum_j \lambda_{ij} = O_i\), but clearly, the same is not true when you sum across all origins for each destination - \(\sum_i T_{ij} \neq \sum_i \lambda_{ij} \neq D_j\)
2.2.2 How do the fits compare with the unconstrained model from last time?
#use the functions from the last practical to calculate some goodness-of-fit statistics
CalcRSquared(cdatasub$Total,cdatasub$prodsimFitted)
[1] 0.8175094
CalcRMSE(cdatasub$Total,cdatasub$prodsimFitted)
[1] 1382.781
Clearly by constraining our model estimates to known origin totals, the fit of the model has improved quite considerably - from around 0.67 in the unconstrained model to around 0.82 in this model. The RMSE has also dropped quite noticably.
2.2.3 A ‘what if…’ scenario
Now that we have calibrated our parameters and produced some estimates, we can start to play around with some what-if scenarios.
For example - What if the government invested loads of money into a new Car Plant in Barking and Dagenham and as a result, average wages increased from a mere £16,200 to £25,000. A far fetched scenario, but one that could make a good experiment.
First create create a new variable with these altered salaries:
cdatasub$wj3_destsalScenario <- cdatasub$wj2_destsal
cdatasub$wj3_destsalScenario <- ifelse(cdatasub$wj3_destsalScenario == 16200,25000,cdatasub$wj3_destsalScenario)
Now let’s plug these new values into the model and see how this changes the flows in the system…
cdatasub$prodsimest2 <- exp(k+(cdatasub$mu_i)+(alpha*log(cdatasub$wj3_destsalScenario))+(beta*log(cdatasub$dist)))
cdatasub$prodsimest2 <- round(cdatasub$prodsimest2,0)
#now we can create pivot table to turn paired list into matrix (and compute the margins as well)
cdatasubmat4 <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "prodsimest2", margins=c("Orig", "Dest"))
cdatasubmat4
You will notice that by increasing the average salary in Barking and Dagenham, we’ve increased flows into Barking and Dagenham, but have not reduced the flows into other zones - the original constraints are still working on the other zones. One way to get around this, now that we have calibrated our parameters, is to return to the multiplicative model in Equation 1 and run this model after calculating our own \(A_i\) balancing factors.
#calculate some new wj^alpha and dij^beta values
wj2_alpha <- cdatasub$wj2_destsal^alpha
dist_beta <- cdatasub$dist^beta
#calculate the first stage of the Ai values
cdatasub$Ai1 <- wj2_alpha*dist_beta
#now do the sum over all js bit
A_i <- cdatasub %>% group_by(OrigCodeNew) %>% summarise(A_i = sum(Ai1))
#now divide in to 1
A_i[,2] <- 1/A_i[,2]
#and write the A_i values back into the data frame
cdatasub$A_i <- A_i$A_i[match(cdatasub$OrigCodeNew,A_i$OrigCodeNew)]
So that is it for calculating your \(A_i\) values. Now you have these, it’s very simple to plug everything back into Equation 1 and generate some estimates…
#To check everything works, recreate the original estimates
cdatasub$prodsimest3 <- cdatasub$A_i*cdatasub$O_i*wj2_alpha*dist_beta
You should see that your new estimates are exactly the same as your first estimates. If they’re not, then something has gone wrong. Now we have this though, we can keep messing around with some new estimates and keep the constraints. Remember, though, that you will need to recalculate \(A_i\) each time you want to create a new set of estimates. Let’s try with our new values for the destination salary in Barking and Dagenham
#calculate some new wj^alpha and dij^beta values
wj3_alpha <- cdatasub$wj3_destsalScenario^alpha
#calculate the first stage of the Ai values
cdatasub$Ai1 <- wj3_alpha*dist_beta
#now do the sum over all js bit
A_i <- cdatasub %>% group_by(OrigCodeNew) %>% summarise(A_i = sum(Ai1))
#now divide in to 1
A_i[,2] <- 1/A_i[,2]
#and write the A_i values back into the data frame
cdatasub$A_i <- A_i$A_i[match(cdatasub$OrigCodeNew,A_i$OrigCodeNew)]
Now we have some new \(A_i\)s, let’s generate some new scenario flow estimates…
#To check everything works, recreate the original estimates
cdatasub$prodsimest4_scenario <- cdatasub$A_i*cdatasub$O_i*wj3_alpha*dist_beta
cdatasub$prodsimest4_scenario <- round(cdatasub$prodsimest4_scenario,0)
#now we can create pivot table to turn paired list into matrix (and compute the margins as well)
cdatasubmat5 <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "prodsimest4_scenario", margins=c("Orig", "Dest"))
cdatasubmat5
There are a number of things to note here. Firstly, we flows into Barking and Dagenham have virtually doubled, while flows into other Boroughs have reduced.
Secondly, Barking and Dagenham was a poor estimate anyway - it model was very much over estimating flows into this Borough. Increasing the salary into this borough has significantly increased flows, so this indicates that there are probably lots of other things that might be discouraging people from working in this borough.
Thirdly, Our origin constraints are now holding again.
3. Attraction-Constrained Model
The attraction constrained Model is virtually the same as the Production constrained model:
- \[T_{ij} = D_j B_j V_i^\mu d_{ij}^-\beta \]
where
- \[D_{j} = \sum_i T_{ij}\]
and
- \[ B_j = \frac{1}{\sum_i V_i^\mu d_{ij}^-\beta}\]
I won’t dwell on the attraction constrained model, except to say that it can be run in R as you would expect:
- \[\lambda_{ij} = exp(k + \mu \ln V_i + \alpha_{i} - \beta \ln d_{ij})\]
or in R:
attrSim <- glm(Total ~ DestCodeNew+log(vi1_origpop)+log(dist), na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
summary(attrSim)
Call:
glm(formula = Total ~ DestCodeNew + log(vi1_origpop) + log(dist),
family = poisson(link = "log"), data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-39.914 -21.614 -5.529 2.778 51.241
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 1.923776 0.134636 14.29 <2e-16 ***
DestCodeNewE09000002 -3.583256 0.038940 -92.02 <2e-16 ***
DestCodeNewE09000003 -1.224440 0.012171 -100.61 <2e-16 ***
DestCodeNewE09000004 -1.925497 0.018018 -106.87 <2e-16 ***
DestCodeNewE09000005 -1.540332 0.012743 -120.88 <2e-16 ***
DestCodeNewE09000006 -1.096912 0.015221 -72.06 <2e-16 ***
DestCodeNewE09000007 -0.299522 0.007840 -38.20 <2e-16 ***
log(vi1_origpop) 1.557941 0.011372 137.00 <2e-16 ***
log(dist) -1.203269 0.007017 -171.47 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 176846 on 41 degrees of freedom
Residual deviance: 23500 on 33 degrees of freedom
AIC: 23832
Number of Fisher Scoring iterations: 6
we can examine how the constraints hold for desintations this time:
#first round the estimates
cdatasub$attrsimFitted <- round(fitted(attrSim),0)
#now we can create pivot table to turn paired list into matrix (and compute the margins as well)
cdatasubmat6 <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "attrsimFitted", margins=c("Orig", "Dest"))
cdatasubmat6
compared to…
cdatasubmat
and we can test the goodness-of-fit in exactly the same way as before:
#use the functions from the last practical to calculate some goodness-of-fit statistics
CalcRSquared(cdatasub$Total,cdatasub$attrsimFitted)
[1] 0.8443004
CalcRMSE(cdatasub$Total,cdatasub$attrsimFitted)
[1] 1310.26
OK, that’s where I’ll leave singly constrained models for now. There are, of course, plenty of things you could try out. For example:
You could try mapping the coefficients or the residual values from the model to see if there is any patterning in either the over or under prediction of flows.
You could try running your own version of a LUTI model by first calibrating the model parameters and plugging these into a multiplicative version of the model, adjusting the destination constraints to see which origins are likely to generate more trips.
4. Doubly Constrained Model
Now, the model in the family you have all been waiting for - the big boss, the daddy, the doubly constrained model!
Let’s begin with the formula:
- \[T_{ij} = A_i O_i B_j D_j d_{ij}^-\beta \]
where
\[O_{i} = \sum_j T_{ij}\]
\[D_{j} = \sum_i T_{ij}\]
and
\[A_i = \frac{1}{\sum_j B_j D_j d_{ij}^-\beta}\]
\[B_j = \frac{1}{\sum_i A_i O_i d_{ij}^-\beta}\]
Now, the astute will have noticed that the calculation of \(A_i\) relies on knowing \(B_j\) and the calculation of \(B_j\) relies on knowing \(A_i\). A conundrum!! If I don’t know \(A_i\) how can I calcuate \(B_j\) and then in turn \(A_i\) and then \(B_j\) ad infinitum???!!
Well, I wrestled with that for a while until I came across this paper by Martyn Senior where he sketches out a very useful algorithm for iteratively arriving at values for \(A_i\) and \(B_j\) by setting each to equal 1 initially and then continuing to calculate each in turn until the difference between each value is small enough not to matter.
We will return to this later, but for now, we will once again use the awesome power of R to deal with all of this difficulty for us!
We can run the doubly constrained model in exactly the same way as we ran the singly constrained models:
- \[\lambda_{ij} = exp(k + \mu_i + \alpha_{i} - \beta \ln d_{ij})\]
or in R…
#run a production constrained SIM
doubSim <- glm(Total ~ Orig+Dest+log(dist), na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
#let's have a look at it's summary...
summary(doubSim)
Call:
glm(formula = Total ~ Orig + Dest + log(dist), family = poisson(link = "log"),
data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-30.9202 -7.0946 -0.8203 7.7356 17.1904
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 28.15423 0.14329 196.48 <2e-16 ***
OrigBarnet 0.42341 0.01743 24.29 <2e-16 ***
OrigBexley 1.00050 0.01635 61.19 <2e-16 ***
OrigBrent -0.19467 0.01881 -10.35 <2e-16 ***
OrigBromley 1.60132 0.01547 103.53 <2e-16 ***
OrigCamden -1.42027 0.02296 -61.86 <2e-16 ***
OrigCity of London -4.25918 0.05568 -76.50 <2e-16 ***
DestBarnet 2.96200 0.04139 71.56 <2e-16 ***
DestBexley 1.45898 0.04313 33.83 <2e-16 ***
DestBrent 2.39091 0.04129 57.90 <2e-16 ***
DestBromley 2.65156 0.04100 64.67 <2e-16 ***
DestCamden 3.55784 0.03995 89.06 <2e-16 ***
DestCity of London 4.10797 0.03977 103.30 <2e-16 ***
log(dist) -2.50306 0.01457 -171.83 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 176845.8 on 41 degrees of freedom
Residual deviance: 5296.4 on 28 degrees of freedom
AIC: 5638.8
Number of Fisher Scoring iterations: 5
And the various flows and goodness-of-fit statistics?
#then round the estimates
cdatasub$doubsimFitted <- round(fitted(doubSim),0)
#now we can create pivot table to turn paired list into matrix (and compute the margins as well)
cdatasubmat7 <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "doubsimFitted", margins=c("Orig", "Dest"))
cdatasubmat7
compared to…
cdatasubmat
and we can test the goodness-of-fit in exactly the same way as before:
#use the functions from the last practical to calculate some goodness-of-fit statistics
CalcRSquared(cdatasub$Total,cdatasub$doubsimFitted)
[1] 0.9824559
CalcRMSE(cdatasub$Total,cdatasub$doubsimFitted)
[1] 440.484
So the goodness of fit has shot up and we can clearly see the origin and destination constraints working, and for most sets of flows, the model is now producing some good estimates. However, there are still some errors in the flows, particularly for estimates between Barking and Dagenham and Bexley or Barnet and Camden.
Is there anything more we can do? Yes, of course there is.
4.1 Tweaking our Model
4.1.1 Distance Decay
Now, all of the way through these practicals, we have assumed that the distance decay parameter follows a negative power law. Well, it doesn’t need to.
In Wilson’s original paper, he generalised the distance decay parameter to:
\[f(d_{ij})\]
Where \(f\) represents some function of distance describing the rate at which the flow interactions change as distance increases. Lots of people have written about this, including Tayor (1971) and more recently Robin Lovelace in a transport context, here.
For the inverse power law that we have been using is one possible function of distance, the other common one that is used is the negative exponential function:
\[exp(-\beta d_{ij})\]
We can get a feel for how different distance decay parameters work by plotting some sample data:
xdistance <- seq(1,20,by=1)
InvPower2 <- xdistance^-2
NegExp0.3 <- exp(-0.3*xdistance)
df <- cbind(InvPower2,NegExp0.3)
meltdf <- melt(df)
ggplot(meltdf,aes(Var1,value, colour = Var2)) + geom_line()

Here you can see quite clearly that the inverse power function has a far more rapid distance decay effect than the negative exponential function. In real life, what this means is that if the observed interactions drop off very rapidly with distance, then they might be more likely to follow an inverse power law. This might be the case when looking at trips to the local convenience store by walking, for example. On the other hand, if the effect of distance is less severe (for example migration across the country for a new job) then the negative exponential funtion might be more appropriate.
There is no hard and fast rule as to which function to pick, it will just come down to which fits the data better…
As Tayor Oshan points out in his excellent Primer what this means in our Poisson regression model is that we simply substitute \(-\beta \ln d_{ij}\) for \(-\beta d_{ij}\) in our model:
#run a production constrained SIM
doubSim1 <- glm(Total ~ Orig+Dest+dist, na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
#let's have a look at it's summary...
summary(doubSim1)
Call:
glm(formula = Total ~ Orig + Dest + dist, family = poisson(link = "log"),
data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-27.6694 -5.7499 -0.4354 6.4668 14.4928
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 6.840e+00 4.368e-02 156.607 < 2e-16 ***
OrigBarnet 4.209e-01 1.768e-02 23.800 < 2e-16 ***
OrigBexley 1.025e+00 1.640e-02 62.482 < 2e-16 ***
OrigBrent -1.227e-01 1.876e-02 -6.541 6.13e-11 ***
OrigBromley 1.702e+00 1.554e-02 109.554 < 2e-16 ***
OrigCamden -8.486e-01 2.088e-02 -40.637 < 2e-16 ***
OrigCity of London -3.882e+00 5.502e-02 -70.556 < 2e-16 ***
DestBarnet 3.129e+00 4.162e-02 75.160 < 2e-16 ***
DestBexley 1.341e+00 4.320e-02 31.049 < 2e-16 ***
DestBrent 2.627e+00 4.151e-02 63.292 < 2e-16 ***
DestBromley 2.590e+00 4.100e-02 63.181 < 2e-16 ***
DestCamden 3.714e+00 4.013e-02 92.558 < 2e-16 ***
DestCity of London 4.017e+00 3.974e-02 101.083 < 2e-16 ***
dist -1.746e-04 1.090e-06 -160.241 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 176846 on 41 degrees of freedom
Residual deviance: 3402 on 28 degrees of freedom
AIC: 3744.4
Number of Fisher Scoring iterations: 5
cdatasub$doubsimFitted1 <- round(fitted(doubSim1),0)
CalcRSquared(cdatasub$Total,cdatasub$doubsimFitted1)
[1] 0.9842882
CalcRMSE(cdatasub$Total,cdatasub$doubsimFitted1)
[1] 413.444
So, we can see that using a negative exponential function in our model actually improves the fit and
4.1.2 Bunging some more variables in
Yes, the nice thing about doing all of this in a regression modelling framework is we can just keep adding predictor variables into the mix and seeing whether they have an effect.
You can’t add origin or destination specific predictors into a doubly constrained model like this, however, you could add some interaction predictors. For example, instead of modelling total flows, we could try and model motorbike commuters using information on car and underground commuters:
kitchensinkSIM <- glm(Motobike ~ Orig+Dest+dist+CarDrive+Underground, na.action = na.exclude, family = poisson(link = "log"), data = cdatasub)
#let's have a look at it's summary...
summary(kitchensinkSIM)
Call:
glm(formula = Motobike ~ Orig + Dest + dist + CarDrive + Underground,
family = poisson(link = "log"), data = cdatasub, na.action = na.exclude)
Deviance Residuals:
Min 1Q Median 3Q Max
-4.3608 -1.4674 -0.0085 0.7353 3.0146
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 2.563e+00 3.030e-01 8.457 < 2e-16 ***
OrigBarnet 3.215e-01 1.929e-01 1.666 0.09564 .
OrigBexley 1.019e+00 1.319e-01 7.722 1.15e-14 ***
OrigBrent -2.825e-01 1.609e-01 -1.756 0.07909 .
OrigBromley 1.172e+00 1.421e-01 8.247 < 2e-16 ***
OrigCamden -1.418e-01 2.396e-01 -0.592 0.55403
OrigCity of London -2.081e+01 1.398e+03 -0.015 0.98812
DestBarnet 1.584e+00 3.350e-01 4.728 2.27e-06 ***
DestBexley 7.608e-01 2.842e-01 2.677 0.00744 **
DestBrent 1.236e+00 3.274e-01 3.775 0.00016 ***
DestBromley 1.267e+00 3.243e-01 3.907 9.34e-05 ***
DestCamden 3.231e+00 2.664e-01 12.126 < 2e-16 ***
DestCity of London 3.435e+00 2.385e-01 14.404 < 2e-16 ***
dist -1.129e-04 1.457e-05 -7.748 9.31e-15 ***
CarDrive 2.305e-04 7.135e-05 3.231 0.00123 **
Underground -9.759e-05 3.982e-05 -2.451 0.01426 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 2890.39 on 41 degrees of freedom
Residual deviance: 104.87 on 26 degrees of freedom
AIC: 273.27
Number of Fisher Scoring iterations: 16
So now it gets quite interesting. Some of the dummy / constraint origins become statistically insignificant when car and tube commuters are added into the mix.
How can we interpret this?
Well, the kind of things that might influence commuting by motorbike patterns (lack of access to public transport, distance from workplace etc.) that also influence travel to work by car, aren’t applicable to centrally-located Camden with lots of public transport links. Camden is just a proxy for these factors (being centrally-located Camden with lots of public transport links), but of-course doesn’t capture the subtle variation in access to public transport and distance that car travel does. Camden’s influence is confounded by these better explanatory variables and becomes insignificant.
The parameter values give an indication of exactly how much of a change in commuting flows by motorcycle you might expect either for beginning or ending in a borough or for a one person change in people travelling by Car or Tube.
If you would like some more useful information on how to interpret the parameters (logged or otherwise) the emerge from a Poisson Regression model, again, Taylor Oshan’s primer is an excellent place to turn.
4.2 From Poisson Regression back to Entropy
As with the earlier models, I have shown you how you can plug the parameter estimates back into Wilson’s entropy maximising multiplicative models in order to generate estimates and tweak things still further.
If you remember from Equations 11 and 12 above, the key to the doubly constrained models is the \(A_i\) and \(B_j\) balancing factors and as they rely on each other, they need to be calculated iteratively. We can do this using Senior’s algorthm also mentioned earlier.
Here is the algorithm in R… (I wrote this code way back in 2012, so it’s a bit ropey, but it works…) To run it, hightlight the whole thing and run it all at once.
After it works, you can spend some time working out what is going on. I find the best solution is to try and run elements of the code individually to see what is happening…
##########################################################################
#This block of code will calculate balancing factors for an entropy
#maximising model (doubly constrained)
#set beta to the appropriate value according to whether exponential or power
if(tail(names(coef(doubSim)),1)=="dist"){
cdatasub$beta <- coef(doubSim)["dist"]
disdecay = 0
} else {
cdatasub$beta <- coef(doubSim)["log(dist)"]
disdecay = 1
}
#Create some new Ai and Bj columns and fill them with starting values
cdatasub$Ai <- 1
cdatasub$Bj <- 1
cdatasub$OldAi <- 10
cdatasub$OldBj <- 10
cdatasub$diff <- abs((cdatasub$OldAi-cdatasub$Ai)/cdatasub$OldAi)
#create convergence and iteration variables and give them initial values
cnvg = 1
its = 0
#This is a while-loop which will calculate Orig and Dest balancing
#factors until the specified convergence criteria is met
while(cnvg > 0.001){
print(paste0("iteration ", its))
its = its + 1 #increment the iteration counter by 1
#First some initial calculations for Ai...
if(disdecay==0){
cdatasub$Ai <- (cdatasub$Bj*cdatasub$D_j*exp(cdatasub$dist*cdatasub$beta))
} else {
cdatasub$Ai <- (cdatasub$Bj*cdatasub$D_j*exp(log(cdatasub$dist)*cdatasub$beta))
}
#aggregate the results by your Origs and store in a new dataframe
AiBF <- aggregate(Ai ~ Orig, data = cdatasub, sum)
#now divide by 1
AiBF$Ai <- 1/AiBF$Ai
#and replace the initial values with the new balancing factors
updates = AiBF[match(cdatasub$Orig,AiBF$Orig),"Ai"]
cdatasub$Ai = ifelse(!is.na(updates), updates, cdatasub$Ai)
#now, if not the first iteration, calculate the difference between the new Ai values and the old Ai values and once done, overwrite the old Ai values with the new ones.
if(its==1){
cdatasub$OldAi <- cdatasub$Ai
} else {
cdatasub$diff <- abs((cdatasub$OldAi-cdatasub$Ai)/cdatasub$OldAi)
cdatasub$OldAi <- cdatasub$Ai
}
#Now some similar calculations for Bj...
if(disdecay==0){
cdatasub$Bj <- (cdatasub$Ai*cdatasub$O_i*exp(cdatasub$dist*cdatasub$beta))
} else {
cdatasub$Bj <- (cdatasub$Ai*cdatasub$O_i*exp(log(cdatasub$dist)*cdatasub$beta))
}
#aggregate the results by your Dests and store in a new dataframe
BjBF <- aggregate(Bj ~ Dest, data = cdatasub, sum)
#now divide by 1
BjBF$Bj <- 1/BjBF$Bj
#and replace the initial values by the balancing factor
updates = BjBF[match(cdatasub$Dest,BjBF$Dest),"Bj"]
cdatasub$Bj = ifelse(!is.na(updates), updates, cdatasub$Bj)
#now, if not the first iteration, calculate the difference between the new Bj values and the old Bj values and once done, overwrite the old Bj values with the new ones.
if(its==1){
cdatasub$OldBj <- cdatasub$Bj
} else {
cdatasub$diff <- abs((cdatasub$OldBj-cdatasub$Bj)/cdatasub$OldBj)
cdatasub$OldBj <- cdatasub$Bj
}
#overwrite the convergence variable with
cnvg = sum(cdatasub$diff)
}
[1] "iteration 0"
[1] "iteration 1"
[1] "iteration 2"
[1] "iteration 3"
[1] "iteration 4"
[1] "iteration 5"
[1] "iteration 6"
[1] "iteration 7"
[1] "iteration 8"
So, we’ve calculated our \(A_i\) and \(B_j\) values using out iterative routine - now we can plug them back into our model, to prove, once again, that the Poisson Model is exactly the same as the multiplicative Entropy Maximising Model…
########################################################################
#Now create some SIM estimates
if(disdecay==0){
cdatasub$SIM_Estimates <- (cdatasub$O_i*cdatasub$Ai*cdatasub$D_j*cdatasub$Bj*exp(cdatasub$dist*cdatasub$beta))
} else{
cdatasub$SIM_Estimates_pow <- (cdatasub$O_i*cdatasub$Ai*cdatasub$D_j*cdatasub$Bj*exp(log(cdatasub$dist)*cdatasub$beta))
}
########################################################################
cdatasub$SIM_Estimates <- round(cdatasub$SIM_Estimates,0)
cdatasubmat8 <- dcast(cdatasub, Orig ~ Dest, sum, value.var = "SIM_Estimates", margins=c("Orig", "Dest"))
cdatasubmat8
Are the the same? Yes!! Woo hoo.
5. Conclusions, further notes and ideas for additional activities
Hopefully you have now seen how it is extremely straight-forward to run and calibrate Wilson’s full family of Spatial Interaction Models in R using GLM and Poisson Regression.
5.1 Some Further Notes
Now might be the time to mention that despite everything I’ve shown you, there has been some discussion in the literature as to whether the Poisson Model is actually a misspecification, especially for modelling migration flows. If you have the stomach for it, this paper by Congdon goes into a lot of detail.
The issue is a thing called ‘overdispersion’ which, translated, essentially relates to the model not being able to capture all of the things that could be explaining the flows in the independent variables that are supplied to the model. The details are tedious and only really intelligible to those with a statistics background. If you want a starter, try here, but in practical terms, we can get around this problem by fitting a very similar sort of regression model called the negative binomial regression model.
If you wish, you can read up and experiment with this model - you can fit it in exactly the same way as the glm
model but using a function called glm.nb
which is part of the mass
package. The negative binomial model has an extra parameter in the model for overdispersion. You you do try this, you will almost certainly discover that your results barely change - but hell, you might keep a pedantic reviewer at bay if you submit this to a journal (not that I’m speaking from experience or anything).
5.2 Further Activities
- Testing these models out on the whole of London and for different years
- You’ve been playing around with just a small 7 borough sample, why not try the full London system. +You can also try and download some similar data from the 2011 Census from Wicid - see if using \(Oi\) and \(Dj\) totals and the parameters you calibrated on the 2001 data, whether you can get reasonable estimates of the 2011 flows. +How have the model parameters changed between 2001 and 2011 - what does this mean
- Visualising your flow estimates
- try using the methods in
stplanr
and leaflet
from the last practical to visualise some of your flow estimates or flow residuals…
LS0tDQp0aXRsZTogJ0RyIERzIElkaW90cyBHdWlkZSB0byBTcGF0aWFsIEludGVyYWN0aW9uIE1vZGVsbGluZyBmb3IgRHVtbWllcyAtIFBhcnQgMjoNCiAgQ29uc3RyYWluZWQgTW9kZWxzJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQo8ZGl2IHN0eWxlPSJ3aWR0aDoxMDBweDsgaGVpZ2h0PTEwMHB4Ij4NCiFbXShDOlxcVXNlcnNcXEFkYW1cXERyb3Bib3hcXExlY3R1cmVzXFxTcGF0aWFsSW50ZXJhY3Rpb25Nb2RlbGxpbmdcXGNhc2FfNDgwLnBuZykNCjwvZGl2Pg0KDQojIyNSZWNhcA0KDQpTbywgW2xhc3QgdGltZV0oaHR0cHM6Ly9ycHVicy5jb20vYWRhbV9kZW5uZXR0LzI1NzIzMSkgd2UgbGVhcm5lZCBhbGwgYWJvdXQgdGhlIHVuY29uc3RyYWluZWQgc3BhdGlhbCBpbnRlcmFjdGlvbiBtb2RlbDsgaG93IHdlIGNhbiB1c2UgaXQgdG8gZXN0aW1hdGUgZmxvd3MgaW4gYSBzeXN0ZW0gdXNpbmcgdmFsdWVzIGZvciBvcmlnaW4gZW1pc3NpdmVuZXNzIG9yIGRlc3RpbmF0aW9uIGF0dHJhY3RpdmVuZXNzOyBob3cgd2UgY2FuIHR3ZWFrIHRoZSBlc3RpbWF0ZXMgdGhlIG1vZGVsIHByb2R1Y2VzIHRocm91Z2ggYWRqdXN0aW5nIGVpdGhlciB0aGUgcGFyYW1ldGVycyBhc3NvY2lhdGVkIHdpdGggdGhlIHByZWRpY3RvciB2YXJpYWJsZXMsIG9yIHRob3VnaCB1c2luZyBkaWZmZXJlbnQgcHJlZGljdG9yIHZhcmlhYmxlcyBvciB1cGRhdGluZyB0aGVpciB2YWx1ZXM7IGFuZCBob3cgd2UgY2FuIGltcHJvdmUgdGhlIGZpdHMgb2YgdGhlIG1vZGVsIGZ1cnRoZXIgYnkgY2FsaWJyYXRpbmcgdGhlIHBhcmFtZXRlcnMgdGhyb3VnaCB1c2luZyBhIFBvaXNzb24gcmVncmVzc2lvbiBtb2RlbC4gDQoNCldlIHNhdyB0aGF0IGV2ZW4gYWZ0ZXIgY2FsaWJyYXRpb24sIG91ciBtb2RlbCBzdGlsbCBvbmx5IGV4cGxhaW5lZCBhcm91bmQgNjAlIG9mIHRoZSB2YXJpYXRpb24gaW4gdGhlIGZsb3dzIHRoYXQgd2Ugb2JzZXJ2ZWQgaW4gb3VyIHN5c3RlbSwgc28gY2FuIHdlIGRvIGFueSBiZXR0ZXI/IFdlbGwgeWVzLCB5ZXMgd2UgY2FuIQ0KDQojI0NvbnN0cmFpbmVkIE1vZGVscw0KDQpJZiB3ZSByZXR1cm4gdG8gW0FsYW4gV2lsc29uJ3MgMTk3MSBwYXBlcl0oaHR0cDovL2pvdXJuYWxzLnNhZ2VwdWIuY29tL2RvaS9hYnMvMTAuMTA2OC9hMDMwMDAxKSwgaGUgaW50cm9kdWNlcyBhIGZ1bGwgKmZhbWlseSogb2Ygc3BhdGlhbCBpbnRlcmFjdGlvbiBtb2RlbHMgb2Ygd2hpY2ggdGhlIHVuY29uc3RyYWluZWQgbW9kZWwgaXMganVzdCB0aGUgc3RhcnQuIEFuZCBpbmRlZWQgc2luY2UgdGhlbiwgdGhlcmUgaGF2ZSBiZWVuIGFsbCBudW1iZXIgb2YgaW5jcmVtZW50YWwgYWR2YW5jZXMgYW5kIGFsdGVybmF0aXZlcyAoc3VjaCBhcyBbU3Rld2FydCBGb3RoZXJpbmdoYW0ncyBDb21wZXRpbmcgRGVzdGluYXRpb25zIG1vZGVsc10oaHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wdWJsaWNhdGlvbi8yMzUzNzExN19BX05ld19TZXRfb2ZfU3BhdGlhbC1JbnRlcmFjdGlvbl9Nb2RlbHNfVGhlX1RoZW9yeV9vZl9Db21wZXRpbmdfRGVzdGluYXRpb25zKSwgW1Bvb2xlcidzIHByb2R1Y3Rpb24vYXR0cmFjdGlvbi9jb3N0IHJlbGF4ZWQgbW9kZWxzXShodHRwOi8vam91cm5hbHMuc2FnZXB1Yi5jb20vZG9pL2Ficy8xMC4xMTc3LzAzMDkxMzI1OTQwMTgwMDEwMiksIFtTdGlsbHdlbGwncyBvcmlnaW4vZGVzdGluYXRpb24gcGFyYW1ldGVyIHNwZWNpZmljIG1vZGVsc10oaHR0cDovL2pvdXJuYWxzLnNhZ2VwdWIuY29tL2RvaS9wZGYvMTAuMTA2OC9hMTAxMTg3KSBhbmQgW21pbmUgYW5kIEFsYW4ncyBvd24gbXVsdGktbGV2ZWwgbW9kZWxdKGh0dHA6Ly9qb3VybmFscy5zYWdlcHViLmNvbS9kb2kvcGRmLzEwLjEwNjgvYTQ1Mzk4KSAodG8gbmFtZSBqdXN0IGEgZmV3KS4NCg0KSW4gdGhpcyBzZXNzaW9uIHdlIHdpbGwgZXhwbG9yZSB0aGUgcmVzdCBvZiBXaWxzb24ncyBmYW1pbHkgLSB0aGUgUHJvZHVjdGlvbiAob3JpZ2luKSBDb25zdHJhaW5lZCBNb2RlbDsgdGhlIEF0dHJhY3Rpb24gKGRlc3RpbmF0aW9uKSBjb25zdHJhaW5lZCBtb2RlbDsgYW5kIHRoZSBEb3VibHkgQ29uc3RyYWluZWQgTW9kZWwuIA0KDQpXZSB3aWxsIHNlZSBob3cgd2UgY2FuLCBhZ2FpbiwgdXNlIGEgUG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsIGluIFIgdG8gY2FsaWJyYXRlIHRoZXNlIG1vZGVscyBhbmQgaG93LCBvbmNlIGNhbGlicmF0ZWQsIHdlIGNhbiB1c2UgdGhlIG1vZGVscyBpbiBkaWZmZXJlbnQgY29udGV4dHMsIHN1Y2ggYXMgTGFuZCBVc2UgVHJhbnNwb3J0YXRpb24gSW50ZXJhY3Rpb24gKExVVEkpIG1vZGVsbGluZywgcmV0YWlsIG1vZGVsbGluZyBhbmQgbWlncmF0aW9uIG1vZGVsbGluZy4gDQoNCiMjMS4gUHJvZHVjdGlvbiBhbmQgQXR0cmFjdGlvbiBDb25zdHJhaW5lZCBNb2RlbHMNCg0KUmlnaHQsIGxldCdzIGdldCBvdXJzZWx2ZXMgc2V0IHVwIGFnYWluLi4uDQoNCk9wZW4gdXAgUiBTdHVkaW8gYW5kIHNldCB1cCBhIG5ldyBwcm9qZWN0IHdvcmtpbmcgZGlyZWN0b3J5LiBPcGVuIGEgbmV3IFIgc2NyaXB0IGFuZCBzYXZlIGl0IGludG8geW91ciBuZXcgd29ya2luZyBkaXJlY3RvcnkuIA0KDQpDaGVjayB0aGUgZmlsZSBpcyBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IGFuZCBSIGNhbiBzZWUgaXQuLi4NCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0NCmdldHdkKCkNCmxpc3QuZmlsZXMoZ2V0d2QoKSkNCmBgYA0KDQpMaWJyYXJ5IHNvbWUgcGFja2FnZXMgKHRoZSBmb3VyIGF0IHRoZSBib3R0b20geW91IHByb2JhYmx5IHdvbid0IG5lZWQpOg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShzcCkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGdlb2pzb25pbykNCmxpYnJhcnkocmdkYWwpDQpsaWJyYXJ5KGRvd25sb2FkZXIpDQpsaWJyYXJ5KG1hcHRvb2xzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoYnJvb20pIA0KbGlicmFyeShzdHBsYW5yKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShNQVNTKQ0KbGlicmFyeShzZikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkodG1hcHRvb2xzKQ0KbGlicmFyeShzdHJpbmdyKQ0KYGBgDQoNCkFuZCBjaGVjayB0aGF0IHlvdSBoYXZlIG91ciBsaXR0bGUgdGVzdCBjb21tdXRpbmcgZGF0YSBtYXRyaXggZnJvbSBsYXN0IHRpbWUgLSBgY2RhdGFzdWJgIC0gc2l0dGluZyBpbiB5b3VyIGdsb2JhbCBlbnZpcm9ubWVudC4gSWYgeW91IGRvbid0LCB0aGVuIFtnbyBiYWNrIHRvIHRoZSBsYXN0IHByYWN0aWNhbCBoZXJlXShodHRwczovL3JwdWJzLmNvbS9hZGFtX2Rlbm5ldHQvMjU1MzE0KSBhbmQgZm9sbG93IHRoZSBzdGVwcyBmcm9tIHRoZSB0b3AgdW50aWwgeW91IGhhdmUgY2RhdGFzdWIgaW4geW91ciBnbG9iYWwgZW52aXJvbm1lbnQuIEl0IHNob3VsZCBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KI0ZldGNoIGEgR2VvSnNvbiBvZiBzb21lIGRpc3RyaWN0LWxldmVsIGJvdW5kYXJpZXMgZnJvbSB0aGUgT05TIEdlb3BvcnRhbC4gRmlyc3QgYWRkIHRoZSBVUkwgdG8gYW4gb2JqZWN0DQp1ZyA8LSAiaHR0cDovL2dlb3BvcnRhbC5zdGF0aXN0aWNzLmdvdi51ay9kYXRhc2V0cy84ZWRhZmJlMzI3NmQ0YjU2YWVjNjA5OTFjYmRkZGE1MF8yLmdlb2pzb24iDQoNCiNub3cgZmV0Y2ggdGhlIGRhdGEgdXNpbmcgdGhlIGRvd25sb2FkZXIgcGFja2FnZSAtIGNyZWF0ZSBhIHRlbXBvcmFyeSBkaXJlY3RvcnkgdG8gcHV0IHRoZSBkYXRhIGludG8NCmRpci5jcmVhdGUoIi4vdG1wIikNCmRvd25sb2FkZXI6OmRvd25sb2FkKHVybCA9IHVnLCBkZXN0ZmlsZSA9ICIuL3RtcC9FVy5HZW9KU09OIikNCiN1bnBhY2sgdGhlIGZpbGUgaW50byBhIG5ldyBzcGF0aWFsUG9seWdvbnMgZGF0YWZyYW1lDQpFVyA8LSByZWFkT0dSKGRzbiA9ICIuL3RtcC9FVy5HZW9KU09OIiwgbGF5ZXIgPSAiT0dSR2VvSlNPTiIpDQoNCkxvbmRvbiA8LSBFV1tncmVwKCJeRTA5IixFV0BkYXRhJGxhZDE1Y2QpLF0NCg0KQk5HID0gIitpbml0PWVwc2c6Mjc3MDAiDQpMb25kb25CTkcgPC0gc3BUcmFuc2Zvcm0oTG9uZG9uLCBCTkcpDQojbm93LCBvcmRlciBieSBib3JvdWdoIGNvZGUgLSAqdGhpcyBzdGVwIHdpbGwgYmUgaW1wb3JhbnQgbGF0ZXIgb24qDQpMb25kb25CTkcgPC0gTG9uZG9uQk5HW29yZGVyKExvbmRvbkJORyRsYWQxNWNkKSxdDQojbm93IHVzZSBzcERpc3RzIHRvIGdlbmVyYXRlIGEgYmlnIGRpc3RhbmNlIG1hdHJpeCBvZiBhbGwgZGlzdGFuY2VzIGJldHdlZW4gYm9yb3VnaHMgaW4gTG9uZG9uDQpkaXN0IDwtIHNwRGlzdHMoTG9uZG9uQk5HKQ0KI21lbHQgdGhpcyBtYXRyaXggaW50byBhIGxpc3Qgb2Ygb3JpZ2luL2Rlc3RpbmF0aW9uIHBhaXJzIHVzaW5nIG1lbHQuIE1lbHQgaW4gaW4gdGhlIHJlc2hhcGUyIHBhY2thZ2UuIFJlc2hhcGUyLCBkcGx5ciBhbmQgZ2dwbG90LCB0b2dldGhlciwgYXJlIHNvbWUgb2YgdGhlIGJlc3QgcGFja2FnZXMgaW4gUiwgc28gaWYgeW91IGFyZSBub3QgZmFtaWxpYXIgd2l0aCB0aGVtLCBnZXQgZ29vZ2xpbmcgYW5kIHlvdXIgbGlmZSB3aWxsIGJlIG11Y2ggYmV0dGVyIQ0KZGlzdFBhaXIgPC0gbWVsdChkaXN0KQ0KDQojcmVhZCBpbiB5b3VyIExvbmRvbiBDb21tdXRpbmcgRGF0YQ0KY2RhdGEgPC0gcmVhZC5jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvN2MxZmkxdHhidmhkcWJ5L0xvbmRvbkNvbW11dGluZzIwMDEuY3N2P3Jhdz0xIikNCiNyZWFkIGluIGEgbG9va3VwIHRhYmxlIGZvciB0cmFuc2xhdGluZyBiZXR3ZWVuIG9sZCBib3JvdWdoIGNvZGVzIGFuZCBuZXcgYm9yb3VnaCBjb2Rlcw0KQ29kZUxvb2t1cCA8LSByZWFkLmNzdigiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9oOG1wdm5lcGRrd2ExYWMvQ29kZUxvb2t1cC5jc3Y/cmF3PTEiKQ0KI3JlYWQgaW4gc29tZSBwb3B1bGF0aW9uIGFuZCBpbmNvbWUgZGF0YQ0KcG9waW5jb21lIDwtIHJlYWQuY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zLzg0ejIyYTR3bzN4MnA4Ni9wb3BpbmNvbWUuY3N2P3Jhdz0xIikNCg0KI25vdyBtZXJnZSB0aGVzZSBzdXBwbGltZW50YXJ5IGRhdGEgaW50byB5b3VyIGZsb3cgZGF0YSBkYXRhZnJhbWUNCmNkYXRhJE9yaWdDb2RlTmV3IDwtIENvZGVMb29rdXAkTmV3Q29kZVttYXRjaChjZGF0YSRPcmlnQ29kZSwgQ29kZUxvb2t1cCRPbGRDb2RlKV0NCmNkYXRhJERlc3RDb2RlTmV3IDwtIENvZGVMb29rdXAkTmV3Q29kZVttYXRjaChjZGF0YSREZXN0Q29kZSwgQ29kZUxvb2t1cCRPbGRDb2RlKV0NCmNkYXRhJHZpMV9vcmlncG9wIDwtIHBvcGluY29tZSRwb3BbbWF0Y2goY2RhdGEkT3JpZ0NvZGVOZXcsIHBvcGluY29tZSRjb2RlKV0NCmNkYXRhJHZpMl9vcmlnc2FsIDwtIHBvcGluY29tZSRtZWRfaW5jb21lW21hdGNoKGNkYXRhJE9yaWdDb2RlTmV3LCBwb3BpbmNvbWUkY29kZSldDQpjZGF0YSR3ajFfZGVzdHBvcCA8LSBwb3BpbmNvbWUkcG9wW21hdGNoKGNkYXRhJERlc3RDb2RlTmV3LCBwb3BpbmNvbWUkY29kZSldDQpjZGF0YSR3ajJfZGVzdHNhbCA8LSBwb3BpbmNvbWUkbWVkX2luY29tZVttYXRjaChjZGF0YSREZXN0Q29kZU5ldywgcG9waW5jb21lJGNvZGUpXQ0KDQojRGF0YSBuZWVkcyB0byBiZSBvcmRlcmVkIGJ5IGJvcm91Z2ggY29kZSwgaWYgaXQncyBub3QsIHdlIHdpbGwgcnVuIGludG8gcHJvYmxlbXMgd2hlbiB3ZSB0cnkgdG8gbWVyZ2Ugb3VyIGRpc3RhbmNlIGRhdGEgYmFjayBpbiBsYXRlciwgc28gdG8gbWFrZSBzdXJlLCB3ZSBjYW4gYXJyYW5nZSBieSBvcmlnbiBhbmQgdGhlbiBkZXN0aW5hdGlvbiB1c2luZyBkcGx5cidzICdhcnJhbmdlJyBmdW5jdGlvbg0KDQpjZGF0YSA8LSBhcnJhbmdlKGNkYXRhLCBPcmlnQ29kZU5ldywgRGVzdENvZGVOZXcpDQpjZGF0YSRUb3RhbE5vSW50cmEgPC0gaWZlbHNlKGNkYXRhJE9yaWdDb2RlID09IGNkYXRhJERlc3RDb2RlLDAsY2RhdGEkVG90YWwpDQpjZGF0YSRvZmZzZXQgPC0gaWZlbHNlKGNkYXRhJE9yaWdDb2RlID09IGNkYXRhJERlc3RDb2RlLDAuMDAwMDAwMDAwMSwxKQ0KIyBub3cgYWRkIHRoZSBkaXN0YW5jZSBjb2x1bW4gaW50byB0aGUgZGF0YWZyYW1lDQpjZGF0YSRkaXN0IDwtIGRpc3RQYWlyJHZhbHVlDQojV2UnbGwganVzdCB1c2UgdGhlIGZpcnN0IDcgYm9yb3VnaHMgYnkgY29kZSwgc28gZmlyc3QsIGNyZWF0ZSBhIHZlY3RvciBvZiB0aGVzZSA3IHRvIG1hdGNoIHdpdGggb3VyIGRhdGENCnRvTWF0Y2g8LWMoIjAwQUEiLCAiMDBBQiIsICIwMEFDIiwgIjAwQUQiLCAiMDBBRSIsICIwMEFGIiwgIjAwQUciKQ0KI3N1YnNldCB0aGUgZGF0YSBieSB0aGUgNyBzYW1wbGUgYm9yb3VnaHMNCiNmaXJzdCB0aGUgb3JpZ2lucw0KY2RhdGFzdWIgPC0gY2RhdGFbZ3JlcChwYXN0ZSh0b01hdGNoLGNvbGxhcHNlID0gInwiKSwgY2RhdGEkT3JpZ0NvZGUpLF0NCiN0aGVuIHRoZSBkZXN0aW5hdGlvbnMNCmNkYXRhc3ViIDwtIGNkYXRhc3ViW2dyZXAocGFzdGUodG9NYXRjaCxjb2xsYXBzZSA9ICJ8IiksIGNkYXRhJERlc3RDb2RlKSxdDQojbm93IGNob3Agb3V0IHRoZSBpbnRyYS1ib3JvdWdoIGZsb3dzDQpjZGF0YXN1YiA8LSBjZGF0YXN1YltjZGF0YXN1YiRPcmlnQ29kZSE9Y2RhdGFzdWIkRGVzdENvZGUsXQ0KI25vdyB1bmZvcnR1bmF0ZWx5IGlmIHlvdSBsb29rIGF0IHRoZSBmaWxlLCBmb3Igc29tZSByZWFzb24gdGhlIGdyZXAgcHJvY2VzcyBoYXMgbGVmdCBhIGxvdCBvZiBlbXB0eSBkYXRhIGNlbGxzIGluIHRoZSBkYXRhZnJhbWUsIHNvIGxldCdzIGp1c3QgY2hvcCBvdXQgZXZlcnl0aGluZyBhZnRlciB0aGUgNyo3IC0gNyAoNDIpIHBhaXJzIHdlIGFyZSBpbnRlcmVzdGVkIGluLi4uDQpjZGF0YXN1YiA8LSBjZGF0YXN1YlsxOjQyLF0NCiNub3cgcmUtb3JkZXIgc28gdGhhdCBPcmlnQ29kZU5ldywgRGVzdENvZGVOZXcgYW5kIFRvdGFsTm9JbnRyYSBhcmUgdGhlIGZpcnN0IHRocmVlIGNvbHVtbnMgKm5vdGUgdGhhdCB5b3UgaGF2ZSB0byBiZSBleHBsaWNpdCBhYm91dCB0aGUgc2VsZWN0IGZ1bmN0aW9uIGluIHRoZSBkcGx5ciBwYWNrYWdlIGFzIE1BU1MgYWxzbyBoYXMgYSAnc2VsZWN0JyBmdW5jdGlvbiBhbmQgUiB3aWxsIHRyeSBhbmQgdXNlIHRoaXMgYnkgZGVmYXVsdC4gV2UgY2FuIGJlIGV4cGxpY3QgYnkgdXNpbmcgdGhlIHN5bnRheCBwYWNrYWdlOjpmdW5jdGlvbg0KY2RhdGFzdWIgPC0gZHBseXI6OnNlbGVjdChjZGF0YXN1YiwgT3JpZ0NvZGVOZXcsIERlc3RDb2RlTmV3LCBUb3RhbCwgZXZlcnl0aGluZygpKQ0KDQp1bmNvc2ltIDwtIGdsbShUb3RhbCB+IGxvZyh2aTFfb3JpZ3BvcCkrbG9nKHdqMl9kZXN0c2FsKStsb2coZGlzdCksIG5hLmFjdGlvbiA9IG5hLmV4Y2x1ZGUsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSwgZGF0YSA9IGNkYXRhc3ViKQ0KDQpjZGF0YXN1YiRmaXR0ZWQgPC0gZml0dGVkKHVuY29zaW0pDQoNCmNkYXRhc3ViJGZpdHRlZCA8LSByb3VuZChjZGF0YXN1YiRmaXR0ZWQsMCkNCnN1bShjZGF0YXN1YiRmaXR0ZWQpDQojdHVybiBpdCBpbnRvIGEgbGl0dGxlIG1hdHJpeCBhbmQgaGF2ZSBhIGxvb2sgYXQgeW91ciBoYW5keSB3b3JrDQpjZGF0YXN1Ym1hdDIgPC0gZGNhc3QoY2RhdGFzdWIsIE9yaWcgfiBEZXN0LCBzdW0sIHZhbHVlLnZhciA9ICJmaXR0ZWQiLCBtYXJnaW5zPWMoIk9yaWciLCAiRGVzdCIpKQ0KY2RhdGFzdWJtYXQyDQoNCkNhbGNSU3F1YXJlZCA8LSBmdW5jdGlvbihvYnNlcnZlZCxlc3RpbWF0ZWQpew0KICByIDwtIGNvcihvYnNlcnZlZCxlc3RpbWF0ZWQpDQogIFIyIDwtIHJeMg0KICBSMg0KfQ0KDQpDYWxjUk1TRSA8LSBmdW5jdGlvbihvYnNlcnZlZCxlc3RpbWF0ZWQpew0KICByZXMgPC0gKG9ic2VydmVkIC0gZXN0aW1hdGVkKV4yDQogIFJNU0UgPC0gcm91bmQoc3FydChtZWFuKHJlcykpLDMpDQogIFJNU0UNCn0NCmBgYA0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjZGF0YXN1Ym1hdCA8LSBkY2FzdChjZGF0YXN1YiwgT3JpZyB+IERlc3QsIHN1bSwgdmFsdWUudmFyID0gIlRvdGFsIiwgbWFyZ2lucz1jKCJPcmlnIiwgIkRlc3QiKSkNCmNkYXRhc3VibWF0DQpgYGANCg0KDQpXaWxzb24ncyByZWFsIGNvbnRyaWJ1dGlvbiB0byB0aGUgZmllbGQgd2FzIGluIG5vdGljaW5nIHRoYXQgdGhlIHVuY29uc3RyYWluZWQgZ3Jhdml0eSBtb2RlbCB3YXMgc3ViLW9wdGltYWwgYXMgaXQgZGlkIG5vdCBtYWtlIHVzZSBvZiBhbGwgb2YgdGhlIGF2YWlsYWJsZSBkYXRhIGluIHRoZSBzeXN0ZW0gd2UgYXJlIHN0dWR5aW5nLiANCg0KSWYgd2UgcmVjYWxsIHRoZSBlc3RpbWF0ZXMgZnJvbSBvdXIgdW5jb25zdHJhaW5lZCBtb2RlbCwgbm9uZSBvZiB0aGUgZXN0aW1hdGVzIHN1bW1lZCB0byB0aGUgb2JzZXJ2ZWQgaW4gYW5kIG91dC1mbG93IHRvdGFsczoNCg0KYGBge3J9DQpjZGF0YXN1Ym1hdDINCmBgYA0KDQoNCg0KT3VyIGVzdGltYXRlcyBkaWQgc3VtIHRvIHRoZSBncmFuZCB0b3RhbCBvZiBmbG93cywgYnV0IHRoaXMgaXMgYmVjYXVzZSB3ZSB3ZXJlIHJlYWxseSBmaXR0aW5nIGEgJ3RvdGFsIGNvbnN0cmFpbmVkJyBtb2RlbCB3aGljaCB1c2VkICRrJCAtIG91ciBjb25zdGFudCBvZiBwcm9wb3J0aW9uYWxpdHkgLSB0byBlbnN1cmUgZXZlcnl0aGluZyBzb3J0IG9mIGFkZGVkIHVwICh0byB3aXRoaW4gMSBjb21tdXRlcikuIA0KDQpXaGVyZSB3ZSBoYXZlIGEgZnVsbCBmbG93IG1hdHJpeCB0byBjYWxpYnJhdGUgcGFyYW1ldGVycywgd2UgY2FuIGluY29ycG9yYXRlIHRoZSByb3cgKG9yaWdpbikgdG90YWxzLCBjb2x1bW4gKGRlc3RpbmF0aW9uKSB0b3RhbHMgb3IgYm90aCBvcmlnaW5hIGFuZCBkZXN0aW5hdGlvbiB0b3RhbHMgdG8gKmNvbnN0cmFpbiogb3VyIGZsb3cgZXN0aW1hdGVzIHRvIHRoZXNlIGtub3duIHZhbHVlcy4gDQoNClRoZXJlIGFyZSB2YXJpb3VzIHJlYXNvbnMgZm9yIHdhbnRpbmcgdG8gZG8gdGhpcywgZm9yIGV4YW1wbGU6DQoNCmEpIElmIFdlIGFyZSBpbnRlcmVzdGVkIGluIGZsb3dzIG9mIG1vbmV5IGludG8gYnVzaW5lc3NlcyBvciBjdXN0b21lcnMgaW50byBzaG9wcywgbWlnaHQgaGF2ZSBpbmZvcm1hdGlvbiBvbiB0aGUgYW1vdW50IG9mIGRpc3Bvc2FibGUgaW5jb21lIGFuZCBzaG9wcGluZyBoYWJpdHMgb2YgdGhlIHBlb3BsZSBsaXZpbmcgaW4gZGlmZmVyZW50IGFyZWFzIGZyb20gbG95YWx0eSBjYXJkIGRhdGEuIFRoaXMgaXMga25vd24gaW5mb3JtYXRpb24gYWJvdXQgb3VyIG9yaWdpbnMgYW5kIHNvIHdlIGNvdWxkICpjb25zdHJhaW4qICBvdXIgc3BhdGlhbCBpbnRlcmFjdGlvbiBtb2RlbCB0byB0aGlzIGtub3duIGluZm9ybWF0aW9uIC0gd2UgY2FuIG1ha2UgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGlzIGxldmVsIG9mIGRpc3Bvc2FibGUgaW5jb21lIHJlbWFpbnMgdGhlIHNhbWUuIFdlIGNhbiB0aGVuIHVzZSBvdGhlciBpbmZvcm1hdGlvbiBhYm91dCB0aGUgYXR0cmFjdGl2ZW5lc3Mgb2YgcGxhY2VzIHRoZXNlIHBlb3BsZSBtaWdodCBsaWtlIHRvIHNob3AgaW4gKHN0b3JlIHNpemUsIHZhcmlldHkgLyBzcGVjaWFsaXNtIG9mIGdvb2RzIGV0Yy4pLCB0byBlc3RpbWF0ZSBob3cgbXVjaCBtb25leSBhIG5ldyBzdG9yZSBvcGVuaW5nIGluIHRoZSBhcmVhIG1pZ2h0IG1ha2UsIG9yIGlmIGEgbmV3IG91dC1vZi10b3duIHNob3BwaW5nIGNlbnRyZSBvcGVucywgaG93IG11Y2ggaXQgbWlnaHQgYWZmZWN0IHRoZSBidXNpbmVzcyBvZiBzaG9wcyBpbiB0aGUgdG93biBjZW50cmUuIFRoaXMgaXMgd2hhdCBpcyBrbm93biBpbiB0aGUgbGl0ZXJhdHVyZSBhcyB0aGUgJ3JldGFpbCBtb2RlbCcgYW5kIGlzIHBlcmhhcHMgdGhlIG1vc3QgY29tbW9uIGV4YW1wbGUgb2YgYSAqKlByb2R1Y3Rpb24gKG9yaWduKSBDb25zdHJhaW5lZCBTcGF0aWFsIEludGVyYWN0aW9uIE1vZGVsKioNCg0KYikgV2UgbWlnaHQgYmUgaW50ZXJlc3RlZCBpbiB1bmRlcnN0YW5kaW5nIHRoZSBpbXBhY3Qgb2YgYSBsYXJnZSBuZXcgZW1wbG95ZXIgaW4gYW4gYXJlYSBvbiB0aGUgZmxvd3Mgb2YgdHJhZmZpYyBpbiB0aGUgdmljaW5pdHkgb3Igb24gdGhlIGRlbWFuZCBmb3IgbmV3IHdvcmtlciBhY2NvbW1vZGF0aW9uIG5lYXJieS4gQSBnb29kIGV4YW1wbGUgb2Ygd2hlcmUgdGhpcyBtaWdodCBiZSB0aGUgY2FzZSBpcyB3aXRoIGxhcmdlIG5ldyBpbmZyYXN0cnVjdHVyZSBkZXZlbG9wbWVudHMgbGlrZSBuZXcgYWlycG9ydHMuIEZvciBleGFtcGxlLCBiZWZvcmUgdGhlIGdvLWFoZWFkIGZvciB0aGUgbmV3IHRoaXJkIHJ1bndheSBhdCBIZWF0aHJvdyB3YXMgZ2l2ZW4sIG9uZSBvcHRpb24gYmVpbmcgY29uc2lkZXJlZCB3YXMgYSBuZXcgcnVud2F5IGluIHRoZSBUaGFtZXMgRXN0dWFyeS4gSWYgYSBuZXcgYWlycG9ydCB3YXMgYnVpbHQgaGVyZSwgd2hhdCB3b3VsZCBiZSB0aGUgcG90ZW50aWFsIGltcGFjdCBvbiB0cmFuc3BvcnQgZmxvd3MgaW4gdGhlIGFyZWEgYW5kIHdoZXJlIG1pZ2h0IHdvcmtlcnMgY29tbXV0ZSBmcm9tPyBUaGlzIHNvcnQgb2Ygc2NlbmFyaW8gY291bGQgYmUgdGVzdGVkIHdpdGggYW4gKipBdHRyYWN0aW9uIChkZXN0aW5hdGlvbikgQ29uc3RyYWluZWQgU3BhdGlhbCBJbnRlcmFjdGlvbiBNb2RlbCoqIHdoZXJlIHRoZSBudW1iZXIgb2YgbmV3IGpvYnMgaW4gYSBkZXN0aW5hdGlvbiBpcyBrbm93biAoYXMgd2VsbCBhcyBqb2JzIGluIHRoZSBzdXJyb3VuZGluZyBhcmVhKSBhbmQgdGhlIG1vZGVsIGNvdWxkIGJlIHVzZWQgdG8gZXN0aW1hdGUgd2hlcmUgdGhlIHdvcmtlcnMgd2lsbCBiZSBkcmF3biBmcm9tIChhbmQgdGhlaXIgbGlrZWx5IHRyYXZlbC10by13b3JrIHBhdHRlcm5zKS4gVGhpcyBtb2RlbCBpcyBleGFjdGx5IHRoZSBzb3J0IG9mIG1vZGVsIExhbmQgVXNlIFRyYW5zcG9ydCBJbnRlcmFjdGlvbiAoTFVUSSkgbW9kZWwgdGhhdCB3YXMgY29uc3RydWN0ZWQgYnkgdGhlIE1lY2hhbmljaXR5IFRlYW0gaW4gQ0FTQSAtIGRldGFpbHMgW2hlcmVdKGh0dHA6Ly93d3cubWVjaGFuaWNpdHkuaW5mby9yZXNlYXJjaC9sYW5kLXVzZS10cmFuc3BvcnQtaW50ZXJhY3Rpb24tbW9kZWxsaW5nLyN0cmFuc3BvcnQpIGlmIHlvdSBhcmUgaW50ZXJlc3RlZC4uLg0KDQpjKSBXZSBtaWdodCBiZSBpbnRlcmVzdGVkIGluIHVuZGVyc3RhbmRpbmcgdGhlIGNoYW5naW5nIHBhdHRlcm5zIG9mIGNvbW11dGluZyBvciBtaWdyYXRpb24gb3ZlciB0aW1lLiBEYXRhIGZyb20gdGhlIENlbnN1cyBhbGxvd3MgdXMgdG8ga25vdyBhbiBhY2N1cmF0ZSBzbmFwLXNob3Qgb2YgbWlncmF0aW5nIGFuZCBjb21tdXRpbmcgcGF0dGVybnMgZXZlcnkgMTAgeWVhcnMuIEluIHRoZXNlIGZ1bGwgZGF0YSBtYXRyaWNlcywgd2Uga25vdyBib3RoIHRoZSBudW1iZXJzIG9mIGNvbW11dGVycy9taWdyYW50cyBsZWF2aW5nIG9yaWdpbnMgYW5kIGFycml2aW5nIGF0IGRlc3RpbmF0aW9ucyBhcyB3ZWxsIGFzIHRoZSBpbnRlcmFjdGlvbnMgYmV0d2VlbiB0aGVtLiBJZiB3ZSBjb25zdHJhaW4gb3VyIG1vZGVsIGVzdGltYXRlcyB0byB0aGlzIGtub3duIGluZm9ybWF0aW9uIGF0IG9yaWdpbiBhbmQgZGVzdGluYXRpb24sIHdlIGNhbiBleGFtaW5lIHZhcmlvdXMgdGhpbmdzLCBpbmNsdWRpbmc6DQoNCiAgICAgaSkgdGhlIHdheXMgdGhhdCB0aGUgcGF0dGVybnMgb2YgY29tbXV0aW5nL21pZ3JhdGlvbiBkaWZmZXIgZnJvbSB0aGUgbW9kZWwgcHJlZGljdGlvbnMgLSB3aGVyZSB3ZSBtaWdodCBnZXQgbW9yZSBtaWdyYW50L2NvbW11dGVyIGZsb3dzIHRoYW4gd2Ugd291bGQgZXhwZWN0DQogICAgIGlpKSBob3cgdGhlIG1vZGVsIHBhcmFtZXRlcnMgdmFyeSBvdmVyIHRpbWUgLSBmb3IgZXhhbXBsZSBob3cgZG9lcyBkaXN0YW5jZSAvIGNvc3Qgb2YgdHJhdmVsIGFmZmVjdCBmbG93cyBvdmVyIHRpbWU/IEFyZSBwZW9wbGUgcHJlcGFyZWQgdG8gdHJhdmVsIGZ1cnRoZXIgb3IgbGVzcyBmYXIgdGhhbiBiZWZvcmU/DQoNCg0KIyMyLiBQcm9kdWN0aW9uLWNvbnN0cmFpbmVkIE1vZGVsDQoNCigxKSAkJFRfe2lqfSA9IEFfaSBPX2kgV19qXlxhbHBoYSBkX3tpan1eLVxiZXRhICQkDQoNCndoZXJlDQoNCigyKSAkJE9fe2l9ID0gXHN1bV9qIFRfe2lqfSQkDQoNCmFuZCANCg0KKDMpICQkIEFfaSA9IFxmcmFjezF9e1xzdW1faiBXX2peXGFscGhhIGRfe2lqfV4tXGJldGF9JCQNCg0KSW4gdGhlIHByb2R1Y3Rpb24tY29uc3RyYWluZWQgbW9kZWwsICRPX2kkIGRvZXMgbm90IGhhdmUgYSBwYXJhbWV0ZXIgYXMgaXQgaXMgYSBrbm93biBjb25zdHJhaW50LiAkQV9pJCBpcyBrbm93biBhcyBhICpiYWxhbmNpbmcgZmFjdG9yKiBhbmQgaXMgYSB2ZWN0b3Igb2YgdmFsdWVzIHdoaWNoIHJlbGF0ZSB0byBlYWNoIG9yaWdpbiwgJGkkLCB3aGljaCBkbyB0aGUgZXF1aXZhbGVudCBqb2IgdG8gJGskIGluIHRoZSB1bmNvbnN0cmFpbmVkL3RvdGFsIGNvbnN0cmFpbmVkIG1vZGVsIGJ1dCBlbnN1cmUgdGhhdCBmbG93IGVzdGltYXRlcyBmcm9tIGVhY2ggb3JpZ2luIHN1bSB0byB0aGUga25vdyB0b3RhbHMsICRPX2kkIHJhdGhlciB0aGFuIGp1c3QgdGhlIG92ZXJhbGwgdG90YWwuDQoNCk5vdyBhdCB0aGlzIHBvaW50LCB3ZSBjb3VsZCBjYWxjdWxhdGUgYWxsIG9mIHRoZSAkT19pJHMgYW5kIHRoZSAkQV9pJHMgYnkgaGFuZCBmb3Igb3VyIHNhbXBsZSBzeXN0ZW0gYW5kIHRoZW4gc2V0IGFib3V0IGd1ZXNzaW5nL2VzdGltYXRpbmcgdGhlIHBhcmFtZXRlciB2YWx1ZXMgZm9yIHRoZSByZXN0IG9mIHRoZSBtb2RlbCwgYnV0IGFzIHlvdSBtaWdodCBoYXZlIGFscmVhZHkgc3VzcGVjdGVkIGZyb20gbGFzdCB0aW1lLCB3ZSBjYW4gdXNlIFIgYW5kIGBnbG1gIHRvIG1ha2UgaXQgcmVhbGx5IGVhc3kgYW5kIGRvIGFsbCBvZiB0aGF0IGZvciB1cyAtIHdvbyBob28hDQoNCldlIHNldCBhYm91dCByZS1zcGVjaWZ5aW5nIHRoZSBQcm9kdWN0aW9uLUNvbnN0cmFpbmVkIG1vZGVsIGFzIGEgUG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsIGluIGV4YWN0bHkgdGhlIHNhbWUgd2F5IGFzIHdlIGRpZCBiZWZvcmUuIFdlIG5lZWQgdG8gdGFrZSBsb2dzIG9mIHRoZSByaWdodC1oYW5kIHNpZGUgb2YgZXF1YXRpb24gYW5kIGFzc3VtZSB0aGF0IHRoZXNlIGFyZSBsb2dhcml0aG1pYWxseSBsaW5rZWQgdG8gdGhlIFBvaXNzb24gZGlzdHJpYnV0ZWQgbWVhbiAoJFxsYW1iZGFfe2lqfSQpIG9mIHRoZSAkVF97aWp9JCB2YXJpYWJsZS4gQXMgc3VjaCwgRXF1YXRpb24gKDEpIGJlY29tZXM6DQoNCig0KSAkJFxsYW1iZGFfe2lqfSA9IGV4cChrICsgXG11X3tpfSArIFxhbHBoYSBcbG4gV19qIC0gXGJldGEgXGxuIGRfe2lqfSkkJA0KDQpJbiBFcXVhdGlvbiAoNCkgJFxtdV97aX0kIGlzIHRoZSBlcXVpdmFsZW50IG9mIHRoZSB2ZWN0b3Igb2YgYmFsYW5jaW5nIGZhY3RvcnMgJEFfaSQsIGJ1dCBpbiByZWdyZXNzaW9uIC8gbG9nLWxpbmVhciBtb2RlbGxpbmcgdGVybWlub2xvZ3kgY2FuIGFsc28gYmUgZGVzY3JpYmVkIGFzIGVpdGhlciAqKmR1bW15IHZhcmlhYmxlcyoqIG9yICoqZml4ZWQgZWZmZWN0cyoqLiBJbiBwcmFjdGljYWwgdGVybXMsIHdoYXQgdGhpcyBtZWFucyBpcyB0aGF0IGluIG91ciByZWdyZXNzaW9uIG1vZGVsLCAkXG11X2kkIGlzIG1vZGVsbGVkIGFzIGEgW2NhdGVnb3JpY2FsIHByZWRpY3Rvcl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2F0ZWdvcmljYWxfdmFyaWFibGUpIGFuZCB0aGVyZWZvcmUgaW4gdGhlIFBvaXNzb24gcmVncmVzc2lvbiBtb2RlbCwgd2UgZG9uJ3QgdXNlIHRoZSBudW1lcmljIHZhbHVlcyBvZiAkT19pJCwgd2UgdXNlIGEgY2F0ZWdvcmljYWwgaWRlbnRpZmllciBmb3IgdGhlIG9yaWdpbi4gSW4gdGVybXMgb2YgdGhlIGV4YW1wbGUgdGFibGUgYWJvdmUsIGZvciBCYXJraW5nIGFuZCBEYWdlbmhhbSB3ZSB3b3VsZG4ndCB1c2UgNTY3NSBhcyB3ZSB3b3VsZCBpZiB3ZSB3ZXJlIGZpdHRpbmcgRXF1YXRpb24gMSwgd2Ugd291bGQganVzdCB1c2UgJ0JhcmtpbmcgYW5kIERhZ2VuaGFtJy4gWW91IG1pZ2h0IGhhdmUgbm90aWNlZCB0aGF0ICRrJCBtYWtlcyBhbiBhcHBlYXJhbmNlIGFnYWluIC0gdGhpcyBpcyBiZWNhdXNlIG5vdyB3ZSBoYXZlIGEgcmVncmVzc2lvbiBlcXVhdGlvbiBhZ2Fpbiwgd2UgbmVlZCBhbiBpbnRlcmNlcHQgdmFsdWUgLSB0aGlzIGlzIGVzdGltYXRlZCBieSBmaXR0aW5nIHRoZSBtb2RlbC4gDQoNClNvLCBsZXQncyBnaXZlIHRoaXMgbW9kZWwgYSB3aGlybC4uLg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3J1biBhIHByb2R1Y3Rpb24gY29uc3RyYWluZWQgU0lNDQpwcm9kU2ltIDwtIGdsbShUb3RhbCB+IE9yaWcrbG9nKHdqMl9kZXN0c2FsKStsb2coZGlzdCksIG5hLmFjdGlvbiA9IG5hLmV4Y2x1ZGUsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSwgZGF0YSA9IGNkYXRhc3ViKQ0KI2xldCdzIGhhdmUgYSBsb29rIGF0IGl0J3Mgc3VtbWFyeS4uLg0Kc3VtbWFyeShwcm9kU2ltKQ0KYGBgDQoNClNvLCB3aGF0IGRvIHdlIGhhdmU/DQoNCldlbGwsIHRoZXJlIGFyZSB0aGUgZWxlbWVudHMgb2YgdGhlIG1vZGVsIG91dHB1dCB0aGF0IHNob3VsZCBiZSBmYW1pbGlhciBmcm9tIHRoZSB1bmNvbnN0cmFpbmVkIG1vZGVsOg0KDQp0aGUgJGskIHBhcmFtZXRlciwgb3IgdGhlIGludGVyY2VwdCBmb3IgdGhlIG1vZGVsIC0gd2hpY2ggaW4gdGhpcyBjYXNlIGlzIDcuOTk5MTENCg0KdGhlICRcYWxwaGEkIHBhcmFtZXRlciByZWxhdGVkIHRvIHRoZSBkZXN0aW5hdGlvbiBhdHRyYWN0aXZlbmVzczogMi4wNDY5Mw0KDQp0aGUgJFxiZXRhJCBkaXN0YW5jZSBkZWNheSBwYXJhbWV0ZXI6IC0yLjI0MjE4DQoNCldlIGNhbiBzZWUgZnJvbSB0aGUgc3RhbmRhcmQgb3V0cHV0cyBmcm9tIHRoZSBtb2RlbCB0aGF0IGFsbCBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50ICgqKiopIGFuZCB0aGUgei1zY29yZXMgaW5kaWNhdGUgdGhhdCB0aGUgZGVzdGluYXRpb24gc2FsYXJ5IGlzIGhhdmluZyB0aGUgbW9zdCBpbmZsdWVuY2Ugb24gdGhlIG1vZGVsLCB3aXRoIGRpc3RhbmNlIGZvbGxvd2luZyBjbG9zZWx5IGJlaGluZC4gDQoNCkFuZCB0aGVuIHdlIGhhdmUgYSBzZXJpZXMgb2YgcGFyYW1ldGVycyB3aGljaCBhcmUgdGhlIHZlY3RvciBvZiAkXG11X2kkIHZhbHVlcyBhc3NvY2lhdGVkIHdpdGggb3VyIG9yaWdpbiBjb25zdHJhaW50cy4gVGhlIGVhZ2xlIGV5ZWQgYW1vbmcgeW91IHdpbGwgbm90aWNlIHRoYXQgd2UgYXJlIG1pc3NpbmcgYSBwYXJhbWV0ZXIgZm9yIEJhcmtpbmcgYW5kIERhZ2VuaGFtLiBObywgdGhlIG1vZGVsIGhhc24ndCBqdXN0IHRha2VuIGEgZGlzbGlrZSB0byB0aGUgaG9tZSBvZiBGb3JkIE1vdG9yY2FyIHByb2R1Y3Rpb24gYW5kIHJpZ2h0IHdpbmcgcG9wdWxpc20gaW4gdGhlIFVLLCBpdCdzIGJlY2F1c2UgZHVtbXkgcGFyYW1ldGVyIHZhbHVlcyBhcmUgcHJlc2VudGVkIGluIHJlbGF0aW9uIHRvIGEgcmVmZXJlbmNlIHZhcmlhYmxlIGFuZCBpbiB0aGlzIGNhc2UgdGhlIGRlZmF1bHQgdmFyaWFibGUgaXMgdGhlIGZpcnN0IG9uZSBpbiBvdXIgbGlzdCAtIEJhcmtpbmcgYW5kIERhZ2VuaGFtLg0KDQpUaGUgd2F5IHRoYXQgd2UgY2FuIGludGVycHJldCB0aGlzIGlzIHRoYXQgdGhlIENpdHkgb2YgTG9uZG9uIGlzIGFyb3VuZCAzLjMgdGltZXMgbGVzcyBsaWtlbHkgdG8gYmUgYW4gb3JpZ2luIGZvciBjb21tdXRlcnMgdGhhbiB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5IChCYXJraW5nIGFuZCBEYWdlbmhhbSksIHdoaWNoIGJ5IGRlZmF1bHQgaXMgc2V0IHRvIDAuDQoNCk5vdyBvZiBjb3Vyc2UsIGl0IG1pZ2h0IGJlIHRoYXQgQmFya2luZyBhbmQgRGFnZW5oYW0gaXMgbm90IHRoZSBtb3N0IGludHVpdGl2ZSBjb250cmFzdGluZyB2YXJpYWJsZSB0byB1c2UuIEZvcnR1bmF0ZWx5IHdlIGNhbiBjaGFuZ2UgdGhlIFRoZSBwYXJ0aWN1bGFyIHdheSBpbiB3aGljaCB0aGUgbW9kZWwgY2hvc2VzIHRoaXMgdmFyaWFibGUgdXNpbmcgdGhlIGBjb250cmFzdHNgIG9wdGlvbi4gSSB3b24ndCBkd2VsbCBtdWNoIG9uIGNvbnRyYXN0cywgYnV0IGlmIHlvdSB3b3VsZCBsaWtlIHRvIGxlYXJuIG1vcmUsIHRoZXJlIGFyZSBsb3RzIG9mIGdvb2Qgd2VicGFnZXMgb3V0IHRoZXJlIGluY2x1ZGluZyBbdGhpcyBvbmVdKGh0dHA6Ly9zdGF0aXN0aWNzLmF0cy51Y2xhLmVkdS9zdGF0L3IvbW9kdWxlcy9kdW1teV92YXJzLmh0bSkgYW5kIHlvdSBzaG91bGQgY29uc3VsdCB0aGUgUiBoZWxwIHN5c3RlbSAtIHNlYXJjaCBmb3IgYD9jb250cmFzdHNgLiBGb3Igbm93LCBsZXQncyBqdXN0IHN3YXAgdGhlIGNvbnRyYXN0aW5nIHZhcmlhYmxlIGZyb20gdGhlIGZpcnN0IHRvIHRoZSBsYXN0IE9yaWdpbiBhbmQgc2VlIHdoYXQgaGFwcGVucyB0byB0aGUgcGFyYW1ldGVyIHZhbHVlczoNCg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3NldCB0aGUgY29udHJhc3RzDQpvcHRpb25zKGNvbnRyYXN0cz1jKCdjb250ci5TQVMnLCdjb250ci5TQVMnKSkNCg0KcHJvZFNpbTIgPC0gZ2xtKFRvdGFsIH4gT3JpZytsb2cod2oyX2Rlc3RzYWwpK2xvZyhkaXN0KSwgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpLCBkYXRhID0gY2RhdGFzdWIpDQojbGV0J3MgaGF2ZSBhIGxvb2sgYXQgaXQncyBzdW1tYXJ5Li4uDQpzdW1tYXJ5KHByb2RTaW0yKQ0KDQpgYGANCg0KDQpOb3RpY2UgaG93IHRoZSBpbnRlcmNlcHQgdmFsdWUgaGFzIGNoYW5nZWQgYW5kIHRoZSBkdW1teSBwYXJhbWV0ZXJzIGZvciBlYWNoIG9yaWdpbiBub3cgcmVsYXRlIHRvIGhvdyB0aGV5IGNvbXBhcmUgdG8gdGhlIENpdHkgb2YgTG9uZG9uLiBBcyB3ZSBjb3VsZCBleHBlY3QgZm9yIHRoaXMgYXJlYSAod2hpY2ggaXMgYSBjb21tdXRpbmcgZGVzdGluYXRpb24gcmF0aGVyIHRoYW4gYW4gb3JpZ2luKSBhbGwgb3RoZXIgb3JpZ2lucyBpbiBvdXIgc3lzdGVtIHNlbmQgYmV0d2VlbiAyIGFuZCA0LjI1IHRpbWVzIGFzIG1hbnkgY29tbXV0ZXJzLiANCg0KVGhlIGRlZmF1bHQgY29udHJhc3Qgb3B0aW9uIGlzIFIgaXMgdG8gdXNlICdUcmVhdG1lbnQnIG9yIGR1bW15IGNvZGluZywgc28gaWYgeW91IHdvdWxkIGxpa2UgdG8gc3dpdGNoIHRoZSBjb250cmFzdHMgYmFjayB0byB0cmVhdG1lbnQsIGl0J3Mgc2ltcGxlIHRvIGRvOg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kb3B0aW9ucyhjb250cmFzdHM9YygnY29udHIudHJlYXRtZW50JywnY29udHIudHJlYXRtZW50JykpDQpgYGANCg0KIyMjMi4xIE1vZGVsIEVzdGltYXRlcw0KDQpOb3cgYXQgdGhpcyBwb2ludCB5b3Ugd2lsbCBiZSB3YW50aW5nIHRvIGtub3cgd2hhdCBhZmZlY3QgdGhlIGNvbnN0cmFpbnRzIGhhdmUgaGFkIG9uIHRoZSBlc3RpbWF0ZXMgcHJvZHVjZWQgYnkgdGhlIG1vZGVsLCBzbyBsZXQncyBwbHVnIHRoZSBwYXJhbWV0ZXJzIGJhY2sgaW50byBFcXVhdGlvbiA0IGFuZCB0YWtlIGEgbG9vay4uLg0KDQpGaXJzdCBydW4gdGhlIG1vZGVsIGFnYWluIHVzaW5nIE9yaWdDb2RlTmV3IGluc3RlYWQgb2YgT3JpZy4uLiAobm90ZSB0aGF0IGJlY2F1c2Ugb2YgdGhlIGNvZGUgb3JkZXIsIHRoZSBtb2RlbCBydW5zIGxpa2UgdGhlICkNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnByb2RTaW0gPC0gZ2xtKFRvdGFsIH4gT3JpZ0NvZGVOZXcrbG9nKHdqMl9kZXN0c2FsKStsb2coZGlzdCksIG5hLmFjdGlvbiA9IG5hLmV4Y2x1ZGUsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSwgZGF0YSA9IGNkYXRhc3ViKQ0Kc3VtbWFyeShwcm9kU2ltKQ0KYGBgDQoNCk5vdyBDcmVhdGUgc29tZSAkT19pJCBhbmQgJERfaiQgY29sdW1ucyBhbmQgc3RvcmUgdGhlIHRvdGFsIGluIGFuZCBvdXQgZmxvdyBtYXRyaXggbWFyZ2lucyBpbiB0aGVtLiAqTm90ZSwgaW4gdGhlIHN5bnRheCBiZWxvdywgSSB1c2UgdGhlIGZvcndhcmQtcGlwZSBvciAlPiUgb3BlcmF0b3IuIFRoaXMgaXMgcHJvYmFibHkgc29tZXRoaW5nIGEgYml0IG5ldyBhbmQgd2FycmVudHMgYSBiaXQgb2YgZXhwbGFpbmluZy4gSWYgeW91J2QgbGlrZSB0byBrbm93IG1vcmUgYWJvdXQgaG93IHBpcGVzIGNhbiBiZSB1c2VkIGluIFIsIHZpc2l0IHRoZSBbbWFncml0dHIgdmlnbmV0dGUgcGFnZSBoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbWFncml0dHIvdmlnbmV0dGVzL21hZ3JpdHRyLmh0bWwpIC0gZm9yIG5vdywganVzdCB0aGluayBvZiAlPiUgYXMgYSBiaXQgbGlrZSBzYXlpbmcgInRoZW4uLi4iIGluIHlvdXIgY29kZSoNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNjcmVhdGUgc29tZSBPaSBhbmQgRGogY29sdW1ucyBpbiB0aGUgZGF0YWZyYW1lIGFuZCBzdG9yZSByb3cgYW5kIGNvbHVtbiB0b3RhbHMgaW4gdGhlbToNCiN0byBjcmVhdGUgT19pLCB0YWtlIGNkYXRhc3ViIC4uLnRoZW4uLi4gZ3JvdXAgYnkgb3JpZ2NvZGVuZXcgLi4udGhlbi4uLiBzdW1tYXJpc2UgYnkgY2FsY3VsYXRpbmcgdGhlIHN1bSBvZiBUb3RhbA0KT19pIDwtIGNkYXRhc3ViICU+JSBncm91cF9ieShPcmlnQ29kZU5ldykgJT4lIHN1bW1hcmlzZShPX2kgPSBzdW0oVG90YWwpKQ0KY2RhdGFzdWIkT19pIDwtIE9faSRPX2lbbWF0Y2goY2RhdGFzdWIkT3JpZ0NvZGVOZXcsT19pJE9yaWdDb2RlTmV3KV0NCkRfaiA8LSBjZGF0YXN1YiAlPiUgZ3JvdXBfYnkoRGVzdENvZGVOZXcpICU+JSBzdW1tYXJpc2UoRF9qID0gc3VtKFRvdGFsKSkNCmNkYXRhc3ViJERfaiA8LSBEX2okRF9qW21hdGNoKGNkYXRhc3ViJERlc3RDb2RlTmV3LERfaiREZXN0Q29kZU5ldyldDQpgYGANCg0KTm93IGZpc2ggdGhlIGNvZWZmaWNpZW50cyBvdXQgb2YgdGhlIHByb2RzaW0gZ2xtIG9iamVjdCANCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNZb3UgY2FuIGRvIHRoaXMgaW4gYSBudW1iZXIgb2Ygd2F5cywgZm9yIGV4YW1wbGUgdGhpcyBwcmludHMgZXZlcnl0aGluZzoNCnByb2RTaW1fb3V0IDwtIHRpZHkocHJvZFNpbSkNCnByb2RTaW1fb3V0DQpgYGANCg0KT3IsIG1vcmUgdXNlZnVsbHksIHB1bGwgb3V0IHRoZSBwYXJhbWV0ZXIgdmFsdWVzIGZvciAkXG11X2kkIGFuZCBzdG9yZSB0aGVtIGJhY2sgaW4gdGhlIGRhdGFmcmFtZSBhbG9uZyB3aXRoICRPX2kkIGFuZCAkRF9qJA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBvciB5b3UgY2FuIGp1c3QgcHVsbCBvdXQgdGhlIGNvZWZmaWNpZW50cyBhbmQgcHV0IHRoZW0gaW50byBhbiBvYmplY3QNCmNvZWZzIDwtIGFzLmRhdGEuZnJhbWUocHJvZFNpbSRjb2VmZmljaWVudHMpDQojdGhlbiBvbmNlIHlvdSBoYXZlIGRvbmUgdGhpcywgeW91IGNhbiBqb2luIHRoZW0gYmFjayBpbnRvIHRoZSBkYXRhZnJhbWUgdXNpbmcgYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gbWF0Y2ggdGhlIGJpdHMgb2YgdGhlIGlkZW50aWZpZXIgdGhhdCB5b3UgbmVlZCAtICpub3RlLCB0aGlzIGJpdCBvZiBjb2RlIGJlbG93IHRvb2sgbWUgYWJvdXQgMiBob3VycyB0byBmaWd1cmUgb3V0ISoNCmNkYXRhc3ViJG11X2kgPC0gY29lZnMkYHByb2RTaW0kY29lZmZpY2llbnRzYFttYXRjaChjZGF0YXN1YiRPcmlnQ29kZU5ldyxzdWIoIi4qT3JpZ0NvZGVOZXciLCIiLCByb3duYW1lcyhjb2VmcykpKV0NCiNub3csIHdoZXJlIHdlIGhhdmUgbWlzc2luZyB2YWx1ZXMgZm9yIG91ciByZWZlcmVuY2UgbXVfaSB2YXJpYWJsZSwgZmlsbCB0aG9zZSB3aXRoIDFzDQpjZGF0YXN1YiRtdV9pIDwtIGlmZWxzZShpcy5uYShjZGF0YXN1YiRtdV9pKSwwLGNkYXRhc3ViJG11X2kpDQpoZWFkKGNkYXRhc3ViKQ0KYGBgDQoNCk9LLCBub3cgd2UgY2FuIHNhdmUgb3VyIHBhcmFtZXRlciB2YWx1ZXMgaW50byBzb21lIHZhcmlhYmxlcy4uLg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KayA8LSBwcm9kU2ltJGNvZWZmaWNpZW50c1sxXQ0KbXVfaSA8LSBwcm9kU2ltJGNvZWZmaWNpZW50c1syOjddDQphbHBoYSA8LSBwcm9kU2ltJGNvZWZmaWNpZW50c1s4XQ0KYmV0YSA8LSBwcm9kU2ltJGNvZWZmaWNpZW50c1s5XQ0KYGBgDQoNCkFuZCB3ZSdyZSByZWFkeSB0byBnZW5lcmF0ZSBvdXIgZXN0aW1hdGVzOg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2RhdGFzdWIkcHJvZHNpbWVzdDEgPC0gZXhwKGsrKGNkYXRhc3ViJG11X2kpKyhhbHBoYSpsb2coY2RhdGFzdWIkd2oyX2Rlc3RzYWwpKSsoYmV0YSpsb2coY2RhdGFzdWIkZGlzdCkpKQ0KYGBgDQoNCk5vdyBvZiBjb3Vyc2Ugd2UgY291bGQgYWxzbyBoYXZlIGRvbmUgdGhpcyB0aGUgZWFzeSB3YXksIGJ1dCBhZ2FpbiwgSSB0aGluayBpdCBoZWxwcyBkb2luZyBpdCB0aGUgaGFyZCB3YXksIGVzcGVjaWFsbHkgaWYgd2Ugd2FudCB0byBwbGF5IHdpdGggdGhlIHBhcmFtZXRlcnMgYnkgaGFuZCBvciBhZGp1c3QgYW55IG9mIHRoZSBpbnB1dCB2YWx1ZXMgd2hlbiBydW5uaW5nIHNvbWUgd2hhdC1pZiBzY2VuYXJpb3MuIA0KDQpIZXJlJ3MgdGhlIGVhc3kgd2F5IGFnYWluIGZvciB0aG9zZSB3aG8gY2FuJ3QgcmVtZW1iZXI6DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjZGF0YXN1YiRwcm9kc2ltRml0dGVkIDwtIGZpdHRlZChwcm9kU2ltKQ0KDQpoZWFkKGNkYXRhc3ViKQ0KYGBgDQoNCiMjIzIuMiBBc3Nlc3NpbmcgdGhlIG1vZGVsIG91dHB1dA0KDQpTbyB3aGF0IGRvIHRoZSBvdXRwdXRzIGZyb20gb3VyIFByb2R1Y3Rpb24gQ29uc3RyYWluZWQgTW9kZWwgbG9vayBsaWtlPyBIb3cgaGFzIHRoZSBnb29kbmVzcy1vZi1maXQgaW1wcm92ZWQgYW5kIGhvdyBjYW4gd2Ugc3RhcnQgdG8gdXNlIHRoaXMgYSBiaXQgbGlrZSBhIHJldGFpbCBtb2RlbCBhbmQgYXNzZXNzIHRoZSBsaWtlbHkgaW1wYWN0cyBvZiBjaGFuZ2luZyBkZXN0aW5hdGlvbiBhdHRyYWN0aXZlbmVzcyBldGMuPw0KDQojIyMjMi4yLjEgVGhlIGZsb3cgbWF0cml4DQoNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNmaXJzdCByb3VuZCB0aGUgZXN0aW1hdGVzDQpjZGF0YXN1YiRwcm9kc2ltRml0dGVkIDwtIHJvdW5kKGZpdHRlZChwcm9kU2ltKSwwKQ0KI25vdyB3ZSBjYW4gY3JlYXRlIHBpdm90IHRhYmxlIHRvIHR1cm4gcGFpcmVkIGxpc3QgaW50byBtYXRyaXggKGFuZCBjb21wdXRlIHRoZSBtYXJnaW5zIGFzIHdlbGwpDQpjZGF0YXN1Ym1hdDMgPC0gZGNhc3QoY2RhdGFzdWIsIE9yaWcgfiBEZXN0LCBzdW0sIHZhbHVlLnZhciA9ICJwcm9kc2ltRml0dGVkIiwgbWFyZ2lucz1jKCJPcmlnIiwgIkRlc3QiKSkNCmNkYXRhc3VibWF0Mw0KYGBgDQoNCkFuZCBjb21wYXJlZCB3aXRoIHRoZSBvcmlnaW5hbCBvYnNlcnZlZCBkYXRhPw0KDQpgYGB7cn0NCmNkYXRhc3VibWF0DQpgYGANCg0KSGVyZSBpdCBpcyB2ZXJ5IGVhc3kgdG8gc2VlIHRoZSBPcmlnaW4gQ29uc3RyYWludHMgd29ya2luZy4gVGhlIHN1bSBhY3Jvc3MgYWxsIGRlc3RpbmF0aW9ucyBmb3IgZWFjaCBvcmlnaW4gaW4gdGhlIGVzdGltYXRlZCBtYXRyaXggaXMgZXhhY3RseSB0aGUgc2FtZSBhcyB0aGUgc2FtZSBzdW0gYWNyb3NzIHRoZSBvYnNlcnZlZCBtYXRyaXggLSAkXHN1bV9qIFRfe2lqfSA9IFxzdW1faiBcbGFtYmRhX3tpan0gPSBPX2kkLCBidXQgY2xlYXJseSwgdGhlIHNhbWUgaXMgbm90IHRydWUgd2hlbiB5b3Ugc3VtIGFjcm9zcyBhbGwgb3JpZ2lucyBmb3IgZWFjaCBkZXN0aW5hdGlvbiAtICRcc3VtX2kgVF97aWp9IFxuZXEgXHN1bV9pIFxsYW1iZGFfe2lqfSBcbmVxIERfaiQNCg0KIyMjIzIuMi4yIEhvdyBkbyB0aGUgZml0cyBjb21wYXJlIHdpdGggdGhlIHVuY29uc3RyYWluZWQgbW9kZWwgZnJvbSBsYXN0IHRpbWU/DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojdXNlIHRoZSBmdW5jdGlvbnMgZnJvbSB0aGUgbGFzdCBwcmFjdGljYWwgdG8gY2FsY3VsYXRlIHNvbWUgZ29vZG5lc3Mtb2YtZml0IHN0YXRpc3RpY3MNCkNhbGNSU3F1YXJlZChjZGF0YXN1YiRUb3RhbCxjZGF0YXN1YiRwcm9kc2ltRml0dGVkKQ0KYGBgDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KQ2FsY1JNU0UoY2RhdGFzdWIkVG90YWwsY2RhdGFzdWIkcHJvZHNpbUZpdHRlZCkNCmBgYA0KDQpDbGVhcmx5IGJ5IGNvbnN0cmFpbmluZyBvdXIgbW9kZWwgZXN0aW1hdGVzIHRvIGtub3duIG9yaWdpbiB0b3RhbHMsIHRoZSBmaXQgb2YgdGhlIG1vZGVsIGhhcyBpbXByb3ZlZCBxdWl0ZSBjb25zaWRlcmFibHkgLSBmcm9tIGFyb3VuZCAwLjY3IGluIHRoZSB1bmNvbnN0cmFpbmVkIG1vZGVsIHRvIGFyb3VuZCAwLjgyIGluIHRoaXMgbW9kZWwuIFRoZSBSTVNFIGhhcyBhbHNvIGRyb3BwZWQgcXVpdGUgbm90aWNhYmx5LiANCg0KIyMjIzIuMi4zIEEgJ3doYXQgaWYuLi4nIHNjZW5hcmlvDQoNCk5vdyB0aGF0IHdlIGhhdmUgY2FsaWJyYXRlZCBvdXIgcGFyYW1ldGVycyBhbmQgcHJvZHVjZWQgc29tZSBlc3RpbWF0ZXMsIHdlIGNhbiBzdGFydCB0byBwbGF5IGFyb3VuZCB3aXRoIHNvbWUgd2hhdC1pZiBzY2VuYXJpb3MuDQoNCkZvciBleGFtcGxlIC0gV2hhdCBpZiB0aGUgZ292ZXJubWVudCBpbnZlc3RlZCBsb2FkcyBvZiBtb25leSBpbnRvIGEgbmV3IENhciBQbGFudCBpbiBCYXJraW5nIGFuZCBEYWdlbmhhbSBhbmQgYXMgYSByZXN1bHQsIGF2ZXJhZ2Ugd2FnZXMgaW5jcmVhc2VkIGZyb20gYSBtZXJlIMKjMTYsMjAwIHRvIMKjMjUsMDAwLiBBIGZhciBmZXRjaGVkIHNjZW5hcmlvLCBidXQgb25lIHRoYXQgY291bGQgbWFrZSBhIGdvb2QgZXhwZXJpbWVudC4gDQoNCkZpcnN0IGNyZWF0ZSBjcmVhdGUgYSBuZXcgdmFyaWFibGUgd2l0aCB0aGVzZSBhbHRlcmVkIHNhbGFyaWVzOg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2RhdGFzdWIkd2ozX2Rlc3RzYWxTY2VuYXJpbyA8LSBjZGF0YXN1YiR3ajJfZGVzdHNhbA0KY2RhdGFzdWIkd2ozX2Rlc3RzYWxTY2VuYXJpbyA8LSBpZmVsc2UoY2RhdGFzdWIkd2ozX2Rlc3RzYWxTY2VuYXJpbyA9PSAxNjIwMCwyNTAwMCxjZGF0YXN1YiR3ajNfZGVzdHNhbFNjZW5hcmlvKQ0KYGBgDQoNCk5vdyBsZXQncyBwbHVnIHRoZXNlIG5ldyB2YWx1ZXMgaW50byB0aGUgbW9kZWwgYW5kIHNlZSBob3cgdGhpcyBjaGFuZ2VzIHRoZSBmbG93cyBpbiB0aGUgc3lzdGVtLi4uDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNkYXRhc3ViJHByb2RzaW1lc3QyIDwtIGV4cChrKyhjZGF0YXN1YiRtdV9pKSsoYWxwaGEqbG9nKGNkYXRhc3ViJHdqM19kZXN0c2FsU2NlbmFyaW8pKSsoYmV0YSpsb2coY2RhdGFzdWIkZGlzdCkpKQ0KDQpjZGF0YXN1YiRwcm9kc2ltZXN0MiA8LSByb3VuZChjZGF0YXN1YiRwcm9kc2ltZXN0MiwwKQ0KI25vdyB3ZSBjYW4gY3JlYXRlIHBpdm90IHRhYmxlIHRvIHR1cm4gcGFpcmVkIGxpc3QgaW50byBtYXRyaXggKGFuZCBjb21wdXRlIHRoZSBtYXJnaW5zIGFzIHdlbGwpDQpjZGF0YXN1Ym1hdDQgPC0gZGNhc3QoY2RhdGFzdWIsIE9yaWcgfiBEZXN0LCBzdW0sIHZhbHVlLnZhciA9ICJwcm9kc2ltZXN0MiIsIG1hcmdpbnM9YygiT3JpZyIsICJEZXN0IikpDQpjZGF0YXN1Ym1hdDQNCmBgYA0KDQpZb3Ugd2lsbCBub3RpY2UgdGhhdCBieSBpbmNyZWFzaW5nIHRoZSBhdmVyYWdlIHNhbGFyeSBpbiBCYXJraW5nIGFuZCBEYWdlbmhhbSwgd2UndmUgaW5jcmVhc2VkIGZsb3dzIGludG8gQmFya2luZyBhbmQgRGFnZW5oYW0sIGJ1dCBoYXZlIG5vdCByZWR1Y2VkIHRoZSBmbG93cyBpbnRvIG90aGVyIHpvbmVzIC0gdGhlIG9yaWdpbmFsIGNvbnN0cmFpbnRzIGFyZSBzdGlsbCB3b3JraW5nIG9uIHRoZSBvdGhlciB6b25lcy4gT25lIHdheSB0byBnZXQgYXJvdW5kIHRoaXMsIG5vdyB0aGF0IHdlIGhhdmUgY2FsaWJyYXRlZCBvdXIgcGFyYW1ldGVycywgaXMgdG8gcmV0dXJuIHRvIHRoZSBtdWx0aXBsaWNhdGl2ZSBtb2RlbCBpbiBFcXVhdGlvbiAxIGFuZCBydW4gdGhpcyBtb2RlbCBhZnRlciBjYWxjdWxhdGluZyBvdXIgb3duICRBX2kkIGJhbGFuY2luZyBmYWN0b3JzLiANCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNjYWxjdWxhdGUgc29tZSBuZXcgd2peYWxwaGEgYW5kIGRpal5iZXRhIHZhbHVlcw0Kd2oyX2FscGhhIDwtIGNkYXRhc3ViJHdqMl9kZXN0c2FsXmFscGhhDQpkaXN0X2JldGEgPC0gY2RhdGFzdWIkZGlzdF5iZXRhDQojY2FsY3VsYXRlIHRoZSBmaXJzdCBzdGFnZSBvZiB0aGUgQWkgdmFsdWVzDQpjZGF0YXN1YiRBaTEgPC0gd2oyX2FscGhhKmRpc3RfYmV0YQ0KI25vdyBkbyB0aGUgc3VtIG92ZXIgYWxsIGpzIGJpdA0KQV9pIDwtIGNkYXRhc3ViICU+JSBncm91cF9ieShPcmlnQ29kZU5ldykgJT4lIHN1bW1hcmlzZShBX2kgPSBzdW0oQWkxKSkNCiNub3cgZGl2aWRlIGluIHRvIDENCkFfaVssMl0gPC0gMS9BX2lbLDJdDQojYW5kIHdyaXRlIHRoZSBBX2kgdmFsdWVzIGJhY2sgaW50byB0aGUgZGF0YSBmcmFtZQ0KY2RhdGFzdWIkQV9pIDwtIEFfaSRBX2lbbWF0Y2goY2RhdGFzdWIkT3JpZ0NvZGVOZXcsQV9pJE9yaWdDb2RlTmV3KV0NCmBgYA0KDQpTbyB0aGF0IGlzIGl0IGZvciBjYWxjdWxhdGluZyB5b3VyICRBX2kkIHZhbHVlcy4gTm93IHlvdSBoYXZlIHRoZXNlLCBpdCdzIHZlcnkgc2ltcGxlIHRvIHBsdWcgZXZlcnl0aGluZyBiYWNrIGludG8gRXF1YXRpb24gMSBhbmQgZ2VuZXJhdGUgc29tZSBlc3RpbWF0ZXMuLi4NCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNUbyBjaGVjayBldmVyeXRoaW5nIHdvcmtzLCByZWNyZWF0ZSB0aGUgb3JpZ2luYWwgZXN0aW1hdGVzDQpjZGF0YXN1YiRwcm9kc2ltZXN0MyA8LSBjZGF0YXN1YiRBX2kqY2RhdGFzdWIkT19pKndqMl9hbHBoYSpkaXN0X2JldGENCmBgYA0KDQpZb3Ugc2hvdWxkIHNlZSB0aGF0IHlvdXIgbmV3IGVzdGltYXRlcyBhcmUgZXhhY3RseSB0aGUgc2FtZSBhcyB5b3VyIGZpcnN0IGVzdGltYXRlcy4gSWYgdGhleSdyZSBub3QsIHRoZW4gc29tZXRoaW5nIGhhcyBnb25lIHdyb25nLiBOb3cgd2UgaGF2ZSB0aGlzIHRob3VnaCwgd2UgY2FuIGtlZXAgbWVzc2luZyBhcm91bmQgd2l0aCBzb21lIG5ldyBlc3RpbWF0ZXMgYW5kIGtlZXAgdGhlIGNvbnN0cmFpbnRzLiANClJlbWVtYmVyLCB0aG91Z2gsIHRoYXQgeW91IHdpbGwgbmVlZCB0byByZWNhbGN1bGF0ZSAkQV9pJCBlYWNoIHRpbWUgeW91IHdhbnQgdG8gY3JlYXRlIGEgbmV3IHNldCBvZiBlc3RpbWF0ZXMuIExldCdzIHRyeSB3aXRoIG91ciBuZXcgdmFsdWVzIGZvciB0aGUgZGVzdGluYXRpb24gc2FsYXJ5IGluIEJhcmtpbmcgYW5kIERhZ2VuaGFtDQoNCmBgYHtyfQ0KI2NhbGN1bGF0ZSBzb21lIG5ldyB3al5hbHBoYSBhbmQgZGlqXmJldGEgdmFsdWVzDQp3ajNfYWxwaGEgPC0gY2RhdGFzdWIkd2ozX2Rlc3RzYWxTY2VuYXJpb15hbHBoYQ0KI2NhbGN1bGF0ZSB0aGUgZmlyc3Qgc3RhZ2Ugb2YgdGhlIEFpIHZhbHVlcw0KY2RhdGFzdWIkQWkxIDwtIHdqM19hbHBoYSpkaXN0X2JldGENCiNub3cgZG8gdGhlIHN1bSBvdmVyIGFsbCBqcyBiaXQNCkFfaSA8LSBjZGF0YXN1YiAlPiUgZ3JvdXBfYnkoT3JpZ0NvZGVOZXcpICU+JSBzdW1tYXJpc2UoQV9pID0gc3VtKEFpMSkpDQojbm93IGRpdmlkZSBpbiB0byAxDQpBX2lbLDJdIDwtIDEvQV9pWywyXQ0KI2FuZCB3cml0ZSB0aGUgQV9pIHZhbHVlcyBiYWNrIGludG8gdGhlIGRhdGEgZnJhbWUNCmNkYXRhc3ViJEFfaSA8LSBBX2kkQV9pW21hdGNoKGNkYXRhc3ViJE9yaWdDb2RlTmV3LEFfaSRPcmlnQ29kZU5ldyldDQpgYGANCg0KTm93IHdlIGhhdmUgc29tZSBuZXcgJEFfaSRzLCBsZXQncyBnZW5lcmF0ZSBzb21lIG5ldyBzY2VuYXJpbyBmbG93IGVzdGltYXRlcy4uLg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI1RvIGNoZWNrIGV2ZXJ5dGhpbmcgd29ya3MsIHJlY3JlYXRlIHRoZSBvcmlnaW5hbCBlc3RpbWF0ZXMNCmNkYXRhc3ViJHByb2RzaW1lc3Q0X3NjZW5hcmlvIDwtIGNkYXRhc3ViJEFfaSpjZGF0YXN1YiRPX2kqd2ozX2FscGhhKmRpc3RfYmV0YQ0KYGBgDQoNCmBgYHtyfQ0KY2RhdGFzdWIkcHJvZHNpbWVzdDRfc2NlbmFyaW8gPC0gcm91bmQoY2RhdGFzdWIkcHJvZHNpbWVzdDRfc2NlbmFyaW8sMCkNCiNub3cgd2UgY2FuIGNyZWF0ZSBwaXZvdCB0YWJsZSB0byB0dXJuIHBhaXJlZCBsaXN0IGludG8gbWF0cml4IChhbmQgY29tcHV0ZSB0aGUgbWFyZ2lucyBhcyB3ZWxsKQ0KY2RhdGFzdWJtYXQ1IDwtIGRjYXN0KGNkYXRhc3ViLCBPcmlnIH4gRGVzdCwgc3VtLCB2YWx1ZS52YXIgPSAicHJvZHNpbWVzdDRfc2NlbmFyaW8iLCBtYXJnaW5zPWMoIk9yaWciLCAiRGVzdCIpKQ0KY2RhdGFzdWJtYXQ1DQpgYGANCg0KDQpUaGVyZSBhcmUgYSBudW1iZXIgb2YgdGhpbmdzIHRvIG5vdGUgaGVyZS4gRmlyc3RseSwgd2UgZmxvd3MgaW50byBCYXJraW5nIGFuZCBEYWdlbmhhbSBoYXZlIHZpcnR1YWxseSBkb3VibGVkLCB3aGlsZSBmbG93cyBpbnRvIG90aGVyIEJvcm91Z2hzIGhhdmUgcmVkdWNlZC4gDQoNClNlY29uZGx5LCBCYXJraW5nIGFuZCBEYWdlbmhhbSB3YXMgYSBwb29yIGVzdGltYXRlIGFueXdheSAtIGl0IG1vZGVsIHdhcyB2ZXJ5IG11Y2ggb3ZlciBlc3RpbWF0aW5nIGZsb3dzIGludG8gdGhpcyBCb3JvdWdoLiBJbmNyZWFzaW5nIHRoZSBzYWxhcnkgaW50byB0aGlzIGJvcm91Z2ggaGFzIHNpZ25pZmljYW50bHkgaW5jcmVhc2VkIGZsb3dzLCBzbyB0aGlzIGluZGljYXRlcyB0aGF0IHRoZXJlIGFyZSBwcm9iYWJseSBsb3RzIG9mIG90aGVyIHRoaW5ncyB0aGF0IG1pZ2h0IGJlIGRpc2NvdXJhZ2luZyBwZW9wbGUgZnJvbSB3b3JraW5nIGluIHRoaXMgYm9yb3VnaC4gDQoNClRoaXJkbHksIE91ciBvcmlnaW4gY29uc3RyYWludHMgYXJlIG5vdyBob2xkaW5nIGFnYWluLiANCg0KIyMzLiBBdHRyYWN0aW9uLUNvbnN0cmFpbmVkIE1vZGVsDQoNClRoZSBhdHRyYWN0aW9uIGNvbnN0cmFpbmVkIE1vZGVsIGlzIHZpcnR1YWxseSB0aGUgc2FtZSBhcyB0aGUgUHJvZHVjdGlvbiBjb25zdHJhaW5lZCBtb2RlbDoNCg0KKDUpICQkVF97aWp9ID0gRF9qIEJfaiBWX2leXG11IGRfe2lqfV4tXGJldGEgJCQNCg0Kd2hlcmUNCg0KKDYpICQkRF97an0gPSBcc3VtX2kgVF97aWp9JCQNCg0KYW5kIA0KDQooNykgJCQgQl9qID0gXGZyYWN7MX17XHN1bV9pIFZfaV5cbXUgZF97aWp9Xi1cYmV0YX0kJA0KDQoNCkkgd29uJ3QgZHdlbGwgb24gdGhlIGF0dHJhY3Rpb24gY29uc3RyYWluZWQgbW9kZWwsIGV4Y2VwdCB0byBzYXkgdGhhdCBpdCBjYW4gYmUgcnVuIGluIFIgYXMgeW91IHdvdWxkIGV4cGVjdDoNCg0KKDgpICQkXGxhbWJkYV97aWp9ID0gZXhwKGsgKyBcbXUgXGxuIFZfaSArIFxhbHBoYV97aX0gLSBcYmV0YSBcbG4gZF97aWp9KSQkDQoNCm9yIGluIFI6DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQphdHRyU2ltIDwtIGdsbShUb3RhbCB+IERlc3RDb2RlTmV3K2xvZyh2aTFfb3JpZ3BvcCkrbG9nKGRpc3QpLCBuYS5hY3Rpb24gPSBuYS5leGNsdWRlLCBmYW1pbHkgPSBwb2lzc29uKGxpbmsgPSAibG9nIiksIGRhdGEgPSBjZGF0YXN1YikNCnN1bW1hcnkoYXR0clNpbSkNCmBgYA0KDQp3ZSBjYW4gZXhhbWluZSBob3cgdGhlIGNvbnN0cmFpbnRzIGhvbGQgZm9yIGRlc2ludGF0aW9ucyB0aGlzIHRpbWU6DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojZmlyc3Qgcm91bmQgdGhlIGVzdGltYXRlcw0KY2RhdGFzdWIkYXR0cnNpbUZpdHRlZCA8LSByb3VuZChmaXR0ZWQoYXR0clNpbSksMCkNCiNub3cgd2UgY2FuIGNyZWF0ZSBwaXZvdCB0YWJsZSB0byB0dXJuIHBhaXJlZCBsaXN0IGludG8gbWF0cml4IChhbmQgY29tcHV0ZSB0aGUgbWFyZ2lucyBhcyB3ZWxsKQ0KY2RhdGFzdWJtYXQ2IDwtIGRjYXN0KGNkYXRhc3ViLCBPcmlnIH4gRGVzdCwgc3VtLCB2YWx1ZS52YXIgPSAiYXR0cnNpbUZpdHRlZCIsIG1hcmdpbnM9YygiT3JpZyIsICJEZXN0IikpDQpjZGF0YXN1Ym1hdDYNCmBgYA0KDQpjb21wYXJlZCB0by4uLg0KDQpgYGB7cn0NCmNkYXRhc3VibWF0DQpgYGANCg0KDQphbmQgd2UgY2FuIHRlc3QgdGhlIGdvb2RuZXNzLW9mLWZpdCBpbiBleGFjdGx5IHRoZSBzYW1lIHdheSBhcyBiZWZvcmU6DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojdXNlIHRoZSBmdW5jdGlvbnMgZnJvbSB0aGUgbGFzdCBwcmFjdGljYWwgdG8gY2FsY3VsYXRlIHNvbWUgZ29vZG5lc3Mtb2YtZml0IHN0YXRpc3RpY3MNCkNhbGNSU3F1YXJlZChjZGF0YXN1YiRUb3RhbCxjZGF0YXN1YiRhdHRyc2ltRml0dGVkKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpDYWxjUk1TRShjZGF0YXN1YiRUb3RhbCxjZGF0YXN1YiRhdHRyc2ltRml0dGVkKQ0KDQpgYGANCg0KT0ssIHRoYXQncyB3aGVyZSBJJ2xsIGxlYXZlIHNpbmdseSBjb25zdHJhaW5lZCBtb2RlbHMgZm9yIG5vdy4gVGhlcmUgYXJlLCBvZiBjb3Vyc2UsIHBsZW50eSBvZiB0aGluZ3MgeW91IGNvdWxkIHRyeSBvdXQuIEZvciBleGFtcGxlOg0KDQoxLiBZb3UgY291bGQgdHJ5IG1hcHBpbmcgdGhlIGNvZWZmaWNpZW50cyBvciB0aGUgcmVzaWR1YWwgdmFsdWVzIGZyb20gdGhlIG1vZGVsIHRvIHNlZSBpZiB0aGVyZSBpcyBhbnkgcGF0dGVybmluZyBpbiBlaXRoZXIgdGhlIG92ZXIgb3IgdW5kZXIgcHJlZGljdGlvbiBvZiBmbG93cy4NCg0KMi4gWW91IGNvdWxkIHRyeSBydW5uaW5nIHlvdXIgb3duIHZlcnNpb24gb2YgYSBMVVRJIG1vZGVsIGJ5IGZpcnN0IGNhbGlicmF0aW5nIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGFuZCBwbHVnZ2luZyB0aGVzZSBpbnRvIGEgbXVsdGlwbGljYXRpdmUgdmVyc2lvbiBvZiB0aGUgbW9kZWwsIGFkanVzdGluZyB0aGUgZGVzdGluYXRpb24gY29uc3RyYWludHMgdG8gc2VlIHdoaWNoIG9yaWdpbnMgYXJlIGxpa2VseSB0byBnZW5lcmF0ZSBtb3JlIHRyaXBzLg0KDQojIzQuIERvdWJseSBDb25zdHJhaW5lZCBNb2RlbA0KDQpOb3csIHRoZSBtb2RlbCBpbiB0aGUgZmFtaWx5IHlvdSBoYXZlIGFsbCBiZWVuIHdhaXRpbmcgZm9yIC0gdGhlIGJpZyBib3NzLCB0aGUgZGFkZHksIHRoZSAqKmRvdWJseSBjb25zdHJhaW5lZCBtb2RlbCEqKg0KDQpMZXQncyBiZWdpbiB3aXRoIHRoZSBmb3JtdWxhOg0KDQooOSkgJCRUX3tpan0gPSBBX2kgT19pIEJfaiBEX2ogZF97aWp9Xi1cYmV0YSAkJA0KDQp3aGVyZQ0KDQooMTApICQkT197aX0gPSBcc3VtX2ogVF97aWp9JCQNCg0KKDExKSAkJERfe2p9ID0gXHN1bV9pIFRfe2lqfSQkDQoNCmFuZCANCg0KKDEyKSAkJEFfaSA9IFxmcmFjezF9e1xzdW1faiBCX2ogRF9qIGRfe2lqfV4tXGJldGF9JCQNCg0KKDEzKSAkJEJfaiA9IFxmcmFjezF9e1xzdW1faSBBX2kgT19pIGRfe2lqfV4tXGJldGF9JCQNCg0KDQpOb3csIHRoZSBhc3R1dGUgd2lsbCBoYXZlIG5vdGljZWQgdGhhdCB0aGUgY2FsY3VsYXRpb24gb2YgJEFfaSQgcmVsaWVzIG9uIGtub3dpbmcgJEJfaiQgYW5kIHRoZSBjYWxjdWxhdGlvbiBvZiAkQl9qJCByZWxpZXMgb24ga25vd2luZyAkQV9pJC4gQSBjb251bmRydW0hISBJZiBJIGRvbid0IGtub3cgJEFfaSQgaG93IGNhbiBJIGNhbGN1YXRlICRCX2okIGFuZCB0aGVuIGluIHR1cm4gJEFfaSQgYW5kIHRoZW4gJEJfaiQgKmFkIGluZmluaXR1bSo/Pz8hIQ0KDQpXZWxsLCBJIHdyZXN0bGVkIHdpdGggdGhhdCBmb3IgYSB3aGlsZSB1bnRpbCBJIGNhbWUgYWNyb3NzIFt0aGlzIHBhcGVyIGJ5IE1hcnR5biBTZW5pb3JdKGh0dHA6Ly9qb3VybmFscy5zYWdlcHViLmNvbS9kb2kvYWJzLzEwLjExNzcvMDMwOTEzMjU3OTAwMzAwMjE4KSB3aGVyZSBoZSBza2V0Y2hlcyBvdXQgYSB2ZXJ5IHVzZWZ1bCBhbGdvcml0aG0gZm9yIGl0ZXJhdGl2ZWx5IGFycml2aW5nIGF0IHZhbHVlcyBmb3IgJEFfaSQgYW5kICRCX2okIGJ5IHNldHRpbmcgZWFjaCB0byBlcXVhbCAxIGluaXRpYWxseSBhbmQgdGhlbiBjb250aW51aW5nIHRvIGNhbGN1bGF0ZSBlYWNoIGluIHR1cm4gdW50aWwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBlYWNoIHZhbHVlIGlzIHNtYWxsIGVub3VnaCBub3QgdG8gbWF0dGVyLiANCg0KV2Ugd2lsbCByZXR1cm4gdG8gdGhpcyBsYXRlciwgYnV0IGZvciBub3csIHdlIHdpbGwgb25jZSBhZ2FpbiB1c2UgdGhlIGF3ZXNvbWUgcG93ZXIgb2YgUiB0byBkZWFsIHdpdGggYWxsIG9mIHRoaXMgZGlmZmljdWx0eSBmb3IgdXMhDQoNCldlIGNhbiBydW4gdGhlIGRvdWJseSBjb25zdHJhaW5lZCBtb2RlbCBpbiBleGFjdGx5IHRoZSBzYW1lIHdheSBhcyB3ZSByYW4gdGhlIHNpbmdseSBjb25zdHJhaW5lZCBtb2RlbHM6DQoNCigxNCkgJCRcbGFtYmRhX3tpan0gPSBleHAoayArIFxtdV9pICsgXGFscGhhX3tpfSAtIFxiZXRhIFxsbiBkX3tpan0pJCQNCg0Kb3IgaW4gUi4uLg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3J1biBhIHByb2R1Y3Rpb24gY29uc3RyYWluZWQgU0lNDQpkb3ViU2ltIDwtIGdsbShUb3RhbCB+IE9yaWcrRGVzdCtsb2coZGlzdCksIG5hLmFjdGlvbiA9IG5hLmV4Y2x1ZGUsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSwgZGF0YSA9IGNkYXRhc3ViKQ0KI2xldCdzIGhhdmUgYSBsb29rIGF0IGl0J3Mgc3VtbWFyeS4uLg0Kc3VtbWFyeShkb3ViU2ltKQ0KYGBgDQoNCkFuZCB0aGUgdmFyaW91cyBmbG93cyBhbmQgZ29vZG5lc3Mtb2YtZml0IHN0YXRpc3RpY3M/DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojdGhlbiByb3VuZCB0aGUgZXN0aW1hdGVzDQpjZGF0YXN1YiRkb3Vic2ltRml0dGVkIDwtIHJvdW5kKGZpdHRlZChkb3ViU2ltKSwwKQ0KI25vdyB3ZSBjYW4gY3JlYXRlIHBpdm90IHRhYmxlIHRvIHR1cm4gcGFpcmVkIGxpc3QgaW50byBtYXRyaXggKGFuZCBjb21wdXRlIHRoZSBtYXJnaW5zIGFzIHdlbGwpDQpjZGF0YXN1Ym1hdDcgPC0gZGNhc3QoY2RhdGFzdWIsIE9yaWcgfiBEZXN0LCBzdW0sIHZhbHVlLnZhciA9ICJkb3Vic2ltRml0dGVkIiwgbWFyZ2lucz1jKCJPcmlnIiwgIkRlc3QiKSkNCmNkYXRhc3VibWF0Nw0KYGBgDQoNCmNvbXBhcmVkIHRvLi4uDQoNCmBgYHtyfQ0KY2RhdGFzdWJtYXQNCmBgYA0KDQoNCmFuZCB3ZSBjYW4gdGVzdCB0aGUgZ29vZG5lc3Mtb2YtZml0IGluIGV4YWN0bHkgdGhlIHNhbWUgd2F5IGFzIGJlZm9yZToNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiN1c2UgdGhlIGZ1bmN0aW9ucyBmcm9tIHRoZSBsYXN0IHByYWN0aWNhbCB0byBjYWxjdWxhdGUgc29tZSBnb29kbmVzcy1vZi1maXQgc3RhdGlzdGljcw0KQ2FsY1JTcXVhcmVkKGNkYXRhc3ViJFRvdGFsLGNkYXRhc3ViJGRvdWJzaW1GaXR0ZWQpDQpgYGANCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkNhbGNSTVNFKGNkYXRhc3ViJFRvdGFsLGNkYXRhc3ViJGRvdWJzaW1GaXR0ZWQpDQpgYGANCg0KU28gdGhlIGdvb2RuZXNzIG9mIGZpdCBoYXMgc2hvdCB1cCBhbmQgd2UgY2FuIGNsZWFybHkgc2VlIHRoZSBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uIGNvbnN0cmFpbnRzIHdvcmtpbmcsIGFuZCBmb3IgbW9zdCBzZXRzIG9mIGZsb3dzLCB0aGUgbW9kZWwgaXMgbm93IHByb2R1Y2luZyBzb21lIGdvb2QgZXN0aW1hdGVzLiBIb3dldmVyLCB0aGVyZSBhcmUgc3RpbGwgc29tZSBlcnJvcnMgaW4gdGhlIGZsb3dzLCBwYXJ0aWN1bGFybHkgZm9yIGVzdGltYXRlcyBiZXR3ZWVuIEJhcmtpbmcgYW5kIERhZ2VuaGFtIGFuZCBCZXhsZXkgb3IgQmFybmV0IGFuZCBDYW1kZW4uIA0KDQpJcyB0aGVyZSBhbnl0aGluZyBtb3JlIHdlIGNhbiBkbz8gWWVzLCBvZiBjb3Vyc2UgdGhlcmUgaXMuIA0KDQojIyM0LjEgVHdlYWtpbmcgb3VyIE1vZGVsDQoNCiMjIyM0LjEuMSBEaXN0YW5jZSBEZWNheQ0KDQpOb3csIGFsbCBvZiB0aGUgd2F5IHRocm91Z2ggdGhlc2UgcHJhY3RpY2Fscywgd2UgaGF2ZSBhc3N1bWVkIHRoYXQgdGhlIGRpc3RhbmNlIGRlY2F5IHBhcmFtZXRlciBmb2xsb3dzIGEgbmVnYXRpdmUgcG93ZXIgbGF3LiBXZWxsLCBpdCBkb2Vzbid0IG5lZWQgdG8uIA0KDQpJbiBbV2lsc29uJ3Mgb3JpZ2luYWwgcGFwZXJdKGh0dHA6Ly9qb3VybmFscy5zYWdlcHViLmNvbS9kb2kvYWJzLzEwLjEwNjgvYTAzMDAwMSksIGhlIGdlbmVyYWxpc2VkIHRoZSBkaXN0YW5jZSBkZWNheSBwYXJhbWV0ZXIgdG86IA0KDQokJGYoZF97aWp9KSQkIA0KDQpXaGVyZSAkZiQgcmVwcmVzZW50cyBzb21lIGZ1bmN0aW9uIG9mIGRpc3RhbmNlIGRlc2NyaWJpbmcgdGhlIHJhdGUgYXQgd2hpY2ggdGhlIGZsb3cgaW50ZXJhY3Rpb25zIGNoYW5nZSBhcyBkaXN0YW5jZSBpbmNyZWFzZXMuIExvdHMgb2YgcGVvcGxlIGhhdmUgd3JpdHRlbiBhYm91dCB0aGlzLCBpbmNsdWRpbmcgW1RheW9yICgxOTcxKV0oaHR0cDovL29ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS8xMC4xMTExL2ouMTUzOC00NjMyLjE5NzEudGIwMDM2NC54L2Z1bGwpIGFuZCBtb3JlIHJlY2VudGx5IFJvYmluIExvdmVsYWNlIGluIGEgdHJhbnNwb3J0IGNvbnRleHQsIFtoZXJlXShodHRwczovL3d3dy5zbGlkZXNoYXJlLm5ldC9JVFNMZWVkcy9lc3RpbWF0aW5nLWRpc3RhbmNlLWRlY2F5LWZvci10aGUtbmF0aW9uYWwtcHJvcGVuc2l0eS10by1jeWNsZS10b29sKS4NCg0KRm9yIHRoZSBpbnZlcnNlIHBvd2VyIGxhdyB0aGF0IHdlIGhhdmUgYmVlbiB1c2luZyBpcyBvbmUgcG9zc2libGUgZnVuY3Rpb24gb2YgZGlzdGFuY2UsIHRoZSBvdGhlciBjb21tb24gb25lIHRoYXQgaXMgdXNlZCBpcyB0aGUgbmVnYXRpdmUgZXhwb25lbnRpYWwgZnVuY3Rpb246DQoNCiQkZXhwKC1cYmV0YSBkX3tpan0pJCQNCg0KV2UgY2FuIGdldCBhIGZlZWwgZm9yIGhvdyBkaWZmZXJlbnQgZGlzdGFuY2UgZGVjYXkgcGFyYW1ldGVycyB3b3JrIGJ5IHBsb3R0aW5nIHNvbWUgc2FtcGxlIGRhdGE6DQoNCmBgYHtyfQ0KeGRpc3RhbmNlIDwtIHNlcSgxLDIwLGJ5PTEpDQpJbnZQb3dlcjIgPC0geGRpc3RhbmNlXi0yDQpOZWdFeHAwLjMgPC0gZXhwKC0wLjMqeGRpc3RhbmNlKQ0KDQpkZiA8LSBjYmluZChJbnZQb3dlcjIsTmVnRXhwMC4zKQ0KbWVsdGRmIDwtIG1lbHQoZGYpDQpnZ3Bsb3QobWVsdGRmLGFlcyhWYXIxLHZhbHVlLCBjb2xvdXIgPSBWYXIyKSkgKyBnZW9tX2xpbmUoKQ0KDQpgYGANCg0KSGVyZSB5b3UgY2FuIHNlZSBxdWl0ZSBjbGVhcmx5IHRoYXQgdGhlIGludmVyc2UgcG93ZXIgZnVuY3Rpb24gaGFzIGEgZmFyIG1vcmUgcmFwaWQgZGlzdGFuY2UgZGVjYXkgZWZmZWN0IHRoYW4gdGhlIG5lZ2F0aXZlIGV4cG9uZW50aWFsIGZ1bmN0aW9uLiBJbiByZWFsIGxpZmUsIHdoYXQgdGhpcyBtZWFucyBpcyB0aGF0IGlmIHRoZSBvYnNlcnZlZCBpbnRlcmFjdGlvbnMgZHJvcCBvZmYgdmVyeSByYXBpZGx5IHdpdGggZGlzdGFuY2UsIHRoZW4gdGhleSBtaWdodCBiZSBtb3JlIGxpa2VseSB0byBmb2xsb3cgYW4gaW52ZXJzZSBwb3dlciBsYXcuIFRoaXMgbWlnaHQgYmUgdGhlIGNhc2Ugd2hlbiBsb29raW5nIGF0IHRyaXBzIHRvIHRoZSBsb2NhbCBjb252ZW5pZW5jZSBzdG9yZSBieSB3YWxraW5nLCBmb3IgZXhhbXBsZS4gT24gdGhlIG90aGVyIGhhbmQsIGlmIHRoZSBlZmZlY3Qgb2YgZGlzdGFuY2UgaXMgbGVzcyBzZXZlcmUgKGZvciBleGFtcGxlIG1pZ3JhdGlvbiBhY3Jvc3MgdGhlIGNvdW50cnkgZm9yIGEgbmV3IGpvYikgdGhlbiB0aGUgbmVnYXRpdmUgZXhwb25lbnRpYWwgZnVudGlvbiBtaWdodCBiZSBtb3JlIGFwcHJvcHJpYXRlLiANCg0KVGhlcmUgaXMgbm8gaGFyZCBhbmQgZmFzdCBydWxlIGFzIHRvIHdoaWNoIGZ1bmN0aW9uIHRvIHBpY2ssIGl0IHdpbGwganVzdCBjb21lIGRvd24gdG8gd2hpY2ggZml0cyB0aGUgZGF0YSBiZXR0ZXIuLi4NCg0KQXMgW1RheW9yIE9zaGFuIHBvaW50cyBvdXQgaW4gaGlzIGV4Y2VsbGVudCBQcmltZXJdKGh0dHA6Ly9vcGVuam91cm5hbHMud3UuYWMuYXQvcmVnaW9uL3BhcGVyXzE3NS8xNzUuaHRtbCkgd2hhdCB0aGlzIG1lYW5zIGluIG91ciBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwgaXMgdGhhdCB3ZSBzaW1wbHkgc3Vic3RpdHV0ZSAkLVxiZXRhIFxsbiBkX3tpan0kIGZvciAkLVxiZXRhIGRfe2lqfSQgaW4gb3VyIG1vZGVsOg0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3J1biBhIHByb2R1Y3Rpb24gY29uc3RyYWluZWQgU0lNDQpkb3ViU2ltMSA8LSBnbG0oVG90YWwgfiBPcmlnK0Rlc3QrZGlzdCwgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpLCBkYXRhID0gY2RhdGFzdWIpDQojbGV0J3MgaGF2ZSBhIGxvb2sgYXQgaXQncyBzdW1tYXJ5Li4uDQpzdW1tYXJ5KGRvdWJTaW0xKQ0KYGBgDQoNCmBgYHtyfQ0KY2RhdGFzdWIkZG91YnNpbUZpdHRlZDEgPC0gcm91bmQoZml0dGVkKGRvdWJTaW0xKSwwKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkNhbGNSU3F1YXJlZChjZGF0YXN1YiRUb3RhbCxjZGF0YXN1YiRkb3Vic2ltRml0dGVkMSkNCkNhbGNSTVNFKGNkYXRhc3ViJFRvdGFsLGNkYXRhc3ViJGRvdWJzaW1GaXR0ZWQxKQ0KYGBgDQoNClNvLCB3ZSBjYW4gc2VlIHRoYXQgdXNpbmcgYSBuZWdhdGl2ZSBleHBvbmVudGlhbCBmdW5jdGlvbiBpbiBvdXIgbW9kZWwgYWN0dWFsbHkgaW1wcm92ZXMgdGhlIGZpdCBhbmQgDQoNCiMjIyM0LjEuMiBCdW5naW5nIHNvbWUgbW9yZSB2YXJpYWJsZXMgaW4NCg0KWWVzLCB0aGUgbmljZSB0aGluZyBhYm91dCBkb2luZyBhbGwgb2YgdGhpcyBpbiBhIHJlZ3Jlc3Npb24gbW9kZWxsaW5nIGZyYW1ld29yayBpcyB3ZSBjYW4ganVzdCBrZWVwIGFkZGluZyBwcmVkaWN0b3IgdmFyaWFibGVzIGludG8gdGhlIG1peCBhbmQgc2VlaW5nIHdoZXRoZXIgdGhleSBoYXZlIGFuIGVmZmVjdC4gDQoNCllvdSBjYW4ndCBhZGQgb3JpZ2luIG9yIGRlc3RpbmF0aW9uIHNwZWNpZmljIHByZWRpY3RvcnMgaW50byBhIGRvdWJseSBjb25zdHJhaW5lZCBtb2RlbCBsaWtlIHRoaXMsIGhvd2V2ZXIsIHlvdSBjb3VsZCBhZGQgc29tZSBpbnRlcmFjdGlvbiBwcmVkaWN0b3JzLiBGb3IgZXhhbXBsZSwgaW5zdGVhZCBvZiBtb2RlbGxpbmcgdG90YWwgZmxvd3MsIHdlIGNvdWxkIHRyeSBhbmQgbW9kZWwgbW90b3JiaWtlIGNvbW11dGVycyB1c2luZyBpbmZvcm1hdGlvbiBvbiBjYXIgYW5kIHVuZGVyZ3JvdW5kIGNvbW11dGVyczoNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmtpdGNoZW5zaW5rU0lNIDwtIGdsbShNb3RvYmlrZSB+IE9yaWcrRGVzdCtkaXN0K0NhckRyaXZlK1VuZGVyZ3JvdW5kLCBuYS5hY3Rpb24gPSBuYS5leGNsdWRlLCBmYW1pbHkgPSBwb2lzc29uKGxpbmsgPSAibG9nIiksIGRhdGEgPSBjZGF0YXN1YikNCiNsZXQncyBoYXZlIGEgbG9vayBhdCBpdCdzIHN1bW1hcnkuLi4NCnN1bW1hcnkoa2l0Y2hlbnNpbmtTSU0pDQpgYGANCg0KU28gbm93IGl0IGdldHMgcXVpdGUgaW50ZXJlc3RpbmcuIFNvbWUgb2YgdGhlIGR1bW15IC8gY29uc3RyYWludCBvcmlnaW5zIGJlY29tZSBzdGF0aXN0aWNhbGx5IGluc2lnbmlmaWNhbnQgd2hlbiBjYXIgYW5kIHR1YmUgY29tbXV0ZXJzIGFyZSBhZGRlZCBpbnRvIHRoZSBtaXguIA0KDQpIb3cgY2FuIHdlIGludGVycHJldCB0aGlzPw0KDQpXZWxsLCB0aGUga2luZCBvZiB0aGluZ3MgdGhhdCBtaWdodCBpbmZsdWVuY2UgY29tbXV0aW5nIGJ5IG1vdG9yYmlrZSBwYXR0ZXJucyAobGFjayBvZiBhY2Nlc3MgdG8gcHVibGljIHRyYW5zcG9ydCwgZGlzdGFuY2UgZnJvbSB3b3JrcGxhY2UgZXRjLikgdGhhdCBhbHNvIGluZmx1ZW5jZSB0cmF2ZWwgdG8gd29yayBieSBjYXIsIGFyZW4ndCBhcHBsaWNhYmxlIHRvIGNlbnRyYWxseS1sb2NhdGVkIENhbWRlbiB3aXRoIGxvdHMgb2YgcHVibGljIHRyYW5zcG9ydCBsaW5rcy4gQ2FtZGVuIGlzIGp1c3QgYSBwcm94eSBmb3IgdGhlc2UgZmFjdG9ycyAoYmVpbmcgY2VudHJhbGx5LWxvY2F0ZWQgQ2FtZGVuIHdpdGggbG90cyBvZiBwdWJsaWMgdHJhbnNwb3J0IGxpbmtzKSwgYnV0IG9mLWNvdXJzZSBkb2Vzbid0IGNhcHR1cmUgdGhlIHN1YnRsZSB2YXJpYXRpb24gaW4gYWNjZXNzIHRvIHB1YmxpYyB0cmFuc3BvcnQgYW5kIGRpc3RhbmNlIHRoYXQgY2FyIHRyYXZlbCBkb2VzLiBDYW1kZW4ncyBpbmZsdWVuY2UgaXMgW2NvbmZvdW5kZWRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbmZvdW5kaW5nKSBieSB0aGVzZSBiZXR0ZXIgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGFuZCBiZWNvbWVzIGluc2lnbmlmaWNhbnQuIA0KDQpUaGUgcGFyYW1ldGVyIHZhbHVlcyBnaXZlIGFuIGluZGljYXRpb24gb2YgZXhhY3RseSBob3cgbXVjaCBvZiBhIGNoYW5nZSBpbiBjb21tdXRpbmcgZmxvd3MgYnkgbW90b3JjeWNsZSB5b3UgbWlnaHQgZXhwZWN0IGVpdGhlciBmb3IgYmVnaW5uaW5nIG9yIGVuZGluZyBpbiBhIGJvcm91Z2ggb3IgZm9yIGEgb25lIHBlcnNvbiBjaGFuZ2UgaW4gcGVvcGxlIHRyYXZlbGxpbmcgYnkgQ2FyIG9yIFR1YmUuIA0KDQpJZiB5b3Ugd291bGQgbGlrZSBzb21lIG1vcmUgdXNlZnVsIGluZm9ybWF0aW9uIG9uIGhvdyB0byBpbnRlcnByZXQgdGhlIHBhcmFtZXRlcnMgKGxvZ2dlZCBvciBvdGhlcndpc2UpIHRoZSBlbWVyZ2UgZnJvbSBhIFBvaXNzb24gUmVncmVzc2lvbiBtb2RlbCwgYWdhaW4sIFtUYXlsb3IgT3NoYW4ncyBwcmltZXJdKGh0dHA6Ly9vcGVuam91cm5hbHMud3UuYWMuYXQvcmVnaW9uL3BhcGVyXzE3NS8xNzUuaHRtbCkgaXMgYW4gZXhjZWxsZW50IHBsYWNlIHRvIHR1cm4uIA0KDQojIyM0LjIgRnJvbSBQb2lzc29uIFJlZ3Jlc3Npb24gYmFjayB0byBFbnRyb3B5DQoNCkFzIHdpdGggdGhlIGVhcmxpZXIgbW9kZWxzLCBJIGhhdmUgc2hvd24geW91IGhvdyB5b3UgY2FuIHBsdWcgdGhlIHBhcmFtZXRlciBlc3RpbWF0ZXMgYmFjayBpbnRvIFdpbHNvbidzIGVudHJvcHkgbWF4aW1pc2luZyBtdWx0aXBsaWNhdGl2ZSBtb2RlbHMgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgZXN0aW1hdGVzIGFuZCB0d2VhayB0aGluZ3Mgc3RpbGwgZnVydGhlci4gDQoNCklmIHlvdSByZW1lbWJlciBmcm9tIEVxdWF0aW9ucyAxMSBhbmQgMTIgYWJvdmUsIHRoZSBrZXkgdG8gdGhlIGRvdWJseSBjb25zdHJhaW5lZCBtb2RlbHMgaXMgdGhlICRBX2kkIGFuZCAkQl9qJCBiYWxhbmNpbmcgZmFjdG9ycyBhbmQgYXMgdGhleSByZWx5IG9uIGVhY2ggb3RoZXIsIHRoZXkgbmVlZCB0byBiZSBjYWxjdWxhdGVkIGl0ZXJhdGl2ZWx5LiBXZSBjYW4gZG8gdGhpcyB1c2luZyBbU2VuaW9yJ3MgYWxnb3J0aG0gXShodHRwOi8vam91cm5hbHMuc2FnZXB1Yi5jb20vZG9pL2Ficy8xMC4xMTc3LzAzMDkxMzI1NzkwMDMwMDIxOCkgYWxzbyBtZW50aW9uZWQgZWFybGllci4gDQoNCkhlcmUgaXMgdGhlIGFsZ29yaXRobSBpbiBSLi4uIChJIHdyb3RlIHRoaXMgY29kZSB3YXkgYmFjayBpbiAyMDEyLCBzbyBpdCdzIGEgYml0IHJvcGV5LCBidXQgaXQgd29ya3MuLi4pIFRvIHJ1biBpdCwgaGlnaHRsaWdodCB0aGUgd2hvbGUgdGhpbmcgYW5kIHJ1biBpdCBhbGwgYXQgb25jZS4gDQoNCkFmdGVyIGl0IHdvcmtzLCB5b3UgY2FuIHNwZW5kIHNvbWUgdGltZSB3b3JraW5nIG91dCB3aGF0IGlzIGdvaW5nIG9uLiBJIGZpbmQgdGhlIGJlc3Qgc29sdXRpb24gaXMgdG8gdHJ5IGFuZCBydW4gZWxlbWVudHMgb2YgdGhlIGNvZGUgaW5kaXZpZHVhbGx5IHRvIHNlZSB3aGF0IGlzIGhhcHBlbmluZy4uLg0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KI1RoaXMgYmxvY2sgb2YgY29kZSB3aWxsIGNhbGN1bGF0ZSBiYWxhbmNpbmcgZmFjdG9ycyBmb3IgYW4gZW50cm9weQ0KI21heGltaXNpbmcgbW9kZWwgKGRvdWJseSBjb25zdHJhaW5lZCkNCg0KI3NldCBiZXRhIHRvIHRoZSBhcHByb3ByaWF0ZSB2YWx1ZSBhY2NvcmRpbmcgdG8gd2hldGhlciBleHBvbmVudGlhbCBvciBwb3dlcg0KaWYodGFpbChuYW1lcyhjb2VmKGRvdWJTaW0pKSwxKT09ImRpc3QiKXsNCiAgY2RhdGFzdWIkYmV0YSA8LSBjb2VmKGRvdWJTaW0pWyJkaXN0Il0NCiAgZGlzZGVjYXkgPSAwDQp9IGVsc2Ugew0KICBjZGF0YXN1YiRiZXRhIDwtIGNvZWYoZG91YlNpbSlbImxvZyhkaXN0KSJdDQogIGRpc2RlY2F5ID0gMQ0KfQ0KDQojQ3JlYXRlIHNvbWUgbmV3IEFpIGFuZCBCaiBjb2x1bW5zIGFuZCBmaWxsIHRoZW0gd2l0aCBzdGFydGluZyB2YWx1ZXMNCmNkYXRhc3ViJEFpIDwtIDENCmNkYXRhc3ViJEJqIDwtIDENCmNkYXRhc3ViJE9sZEFpIDwtIDEwDQpjZGF0YXN1YiRPbGRCaiA8LSAxMA0KY2RhdGFzdWIkZGlmZiA8LSBhYnMoKGNkYXRhc3ViJE9sZEFpLWNkYXRhc3ViJEFpKS9jZGF0YXN1YiRPbGRBaSkNCg0KI2NyZWF0ZSBjb252ZXJnZW5jZSBhbmQgaXRlcmF0aW9uIHZhcmlhYmxlcyBhbmQgZ2l2ZSB0aGVtIGluaXRpYWwgdmFsdWVzDQpjbnZnID0gMQ0KaXRzID0gMA0KDQojVGhpcyBpcyBhIHdoaWxlLWxvb3Agd2hpY2ggd2lsbCBjYWxjdWxhdGUgT3JpZyBhbmQgRGVzdCBiYWxhbmNpbmcNCiNmYWN0b3JzIHVudGlsIHRoZSBzcGVjaWZpZWQgY29udmVyZ2VuY2UgY3JpdGVyaWEgaXMgbWV0DQp3aGlsZShjbnZnID4gMC4wMDEpew0KICBwcmludChwYXN0ZTAoIml0ZXJhdGlvbiAiLCBpdHMpKQ0KICBpdHMgPSBpdHMgKyAxICNpbmNyZW1lbnQgdGhlIGl0ZXJhdGlvbiBjb3VudGVyIGJ5IDENCiAgI0ZpcnN0IHNvbWUgaW5pdGlhbCBjYWxjdWxhdGlvbnMgZm9yIEFpLi4uDQogIGlmKGRpc2RlY2F5PT0wKXsNCiAgICBjZGF0YXN1YiRBaSA8LSAoY2RhdGFzdWIkQmoqY2RhdGFzdWIkRF9qKmV4cChjZGF0YXN1YiRkaXN0KmNkYXRhc3ViJGJldGEpKQ0KICB9IGVsc2Ugew0KICAgIGNkYXRhc3ViJEFpIDwtIChjZGF0YXN1YiRCaipjZGF0YXN1YiREX2oqZXhwKGxvZyhjZGF0YXN1YiRkaXN0KSpjZGF0YXN1YiRiZXRhKSkNCiAgfSAgDQogICNhZ2dyZWdhdGUgdGhlIHJlc3VsdHMgYnkgeW91ciBPcmlncyBhbmQgc3RvcmUgaW4gYSBuZXcgZGF0YWZyYW1lDQogIEFpQkYgPC0gYWdncmVnYXRlKEFpIH4gT3JpZywgZGF0YSA9IGNkYXRhc3ViLCBzdW0pDQogICNub3cgZGl2aWRlIGJ5IDENCiAgQWlCRiRBaSA8LSAxL0FpQkYkQWkgDQogICNhbmQgcmVwbGFjZSB0aGUgaW5pdGlhbCB2YWx1ZXMgd2l0aCB0aGUgbmV3IGJhbGFuY2luZyBmYWN0b3JzDQogIHVwZGF0ZXMgPSBBaUJGW21hdGNoKGNkYXRhc3ViJE9yaWcsQWlCRiRPcmlnKSwiQWkiXQ0KICBjZGF0YXN1YiRBaSA9IGlmZWxzZSghaXMubmEodXBkYXRlcyksIHVwZGF0ZXMsIGNkYXRhc3ViJEFpKQ0KICAjbm93LCBpZiBub3QgdGhlIGZpcnN0IGl0ZXJhdGlvbiwgY2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gIHRoZSBuZXcgQWkgdmFsdWVzIGFuZCB0aGUgb2xkIEFpIHZhbHVlcyBhbmQgb25jZSBkb25lLCBvdmVyd3JpdGUgdGhlIG9sZCBBaSB2YWx1ZXMgd2l0aCB0aGUgbmV3IG9uZXMuIA0KICBpZihpdHM9PTEpew0KICAgIGNkYXRhc3ViJE9sZEFpIDwtIGNkYXRhc3ViJEFpICAgIA0KICB9IGVsc2Ugew0KICAgIGNkYXRhc3ViJGRpZmYgPC0gYWJzKChjZGF0YXN1YiRPbGRBaS1jZGF0YXN1YiRBaSkvY2RhdGFzdWIkT2xkQWkpICAgIA0KICAgIGNkYXRhc3ViJE9sZEFpIDwtIGNkYXRhc3ViJEFpDQogIH0NCiAgDQogICNOb3cgc29tZSBzaW1pbGFyIGNhbGN1bGF0aW9ucyBmb3IgQmouLi4NCiAgaWYoZGlzZGVjYXk9PTApew0KICAgIGNkYXRhc3ViJEJqIDwtIChjZGF0YXN1YiRBaSpjZGF0YXN1YiRPX2kqZXhwKGNkYXRhc3ViJGRpc3QqY2RhdGFzdWIkYmV0YSkpDQogIH0gZWxzZSB7DQogICAgY2RhdGFzdWIkQmogPC0gKGNkYXRhc3ViJEFpKmNkYXRhc3ViJE9faSpleHAobG9nKGNkYXRhc3ViJGRpc3QpKmNkYXRhc3ViJGJldGEpKQ0KICB9DQogICNhZ2dyZWdhdGUgdGhlIHJlc3VsdHMgYnkgeW91ciBEZXN0cyBhbmQgc3RvcmUgaW4gYSBuZXcgZGF0YWZyYW1lDQogIEJqQkYgPC0gYWdncmVnYXRlKEJqIH4gRGVzdCwgZGF0YSA9IGNkYXRhc3ViLCBzdW0pDQogICNub3cgZGl2aWRlIGJ5IDENCiAgQmpCRiRCaiA8LSAxL0JqQkYkQmogIA0KICAjYW5kIHJlcGxhY2UgdGhlIGluaXRpYWwgdmFsdWVzIGJ5IHRoZSBiYWxhbmNpbmcgZmFjdG9yDQogIHVwZGF0ZXMgPSBCakJGW21hdGNoKGNkYXRhc3ViJERlc3QsQmpCRiREZXN0KSwiQmoiXQ0KICBjZGF0YXN1YiRCaiA9IGlmZWxzZSghaXMubmEodXBkYXRlcyksIHVwZGF0ZXMsIGNkYXRhc3ViJEJqKQ0KI25vdywgaWYgbm90IHRoZSBmaXJzdCBpdGVyYXRpb24sIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBuZXcgQmogdmFsdWVzIGFuZCB0aGUgb2xkIEJqIHZhbHVlcyBhbmQgb25jZSBkb25lLCBvdmVyd3JpdGUgdGhlIG9sZCBCaiB2YWx1ZXMgd2l0aCB0aGUgbmV3IG9uZXMuDQogIGlmKGl0cz09MSl7DQogICAgY2RhdGFzdWIkT2xkQmogPC0gY2RhdGFzdWIkQmoNCiAgfSBlbHNlIHsNCiAgICBjZGF0YXN1YiRkaWZmIDwtIGFicygoY2RhdGFzdWIkT2xkQmotY2RhdGFzdWIkQmopL2NkYXRhc3ViJE9sZEJqKSAgICANCiAgICBjZGF0YXN1YiRPbGRCaiA8LSBjZGF0YXN1YiRCag0KICB9IA0KICAjb3ZlcndyaXRlIHRoZSBjb252ZXJnZW5jZSB2YXJpYWJsZSB3aXRoIA0KICBjbnZnID0gc3VtKGNkYXRhc3ViJGRpZmYpDQp9DQpgYGANCg0KU28sIHdlJ3ZlIGNhbGN1bGF0ZWQgb3VyICRBX2kkIGFuZCAkQl9qJCB2YWx1ZXMgdXNpbmcgb3V0IGl0ZXJhdGl2ZSByb3V0aW5lIC0gbm93IHdlIGNhbiBwbHVnIHRoZW0gYmFjayBpbnRvIG91ciBtb2RlbCwgdG8gcHJvdmUsIG9uY2UgYWdhaW4sIHRoYXQgdGhlIFBvaXNzb24gTW9kZWwgaXMgZXhhY3RseSB0aGUgc2FtZSBhcyB0aGUgbXVsdGlwbGljYXRpdmUgRW50cm9weSBNYXhpbWlzaW5nIE1vZGVsLi4uDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojTm93IGNyZWF0ZSBzb21lIFNJTSBlc3RpbWF0ZXMNCmlmKGRpc2RlY2F5PT0wKXsNCiAgY2RhdGFzdWIkU0lNX0VzdGltYXRlcyA8LSAoY2RhdGFzdWIkT19pKmNkYXRhc3ViJEFpKmNkYXRhc3ViJERfaipjZGF0YXN1YiRCaipleHAoY2RhdGFzdWIkZGlzdCpjZGF0YXN1YiRiZXRhKSkNCn0gZWxzZXsNCiAgY2RhdGFzdWIkU0lNX0VzdGltYXRlc19wb3cgPC0gKGNkYXRhc3ViJE9faSpjZGF0YXN1YiRBaSpjZGF0YXN1YiREX2oqY2RhdGFzdWIkQmoqZXhwKGxvZyhjZGF0YXN1YiRkaXN0KSpjZGF0YXN1YiRiZXRhKSkNCn0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KYGBgDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNkYXRhc3ViJFNJTV9Fc3RpbWF0ZXMgPC0gcm91bmQoY2RhdGFzdWIkU0lNX0VzdGltYXRlcywwKQ0KY2RhdGFzdWJtYXQ4IDwtIGRjYXN0KGNkYXRhc3ViLCBPcmlnIH4gRGVzdCwgc3VtLCB2YWx1ZS52YXIgPSAiU0lNX0VzdGltYXRlcyIsIG1hcmdpbnM9YygiT3JpZyIsICJEZXN0IikpDQpjZGF0YXN1Ym1hdDgNCmBgYA0KDQpBcmUgdGhlIHRoZSBzYW1lPyBZZXMhISBXb28gaG9vLg0KDQojIzUuIENvbmNsdXNpb25zLCBmdXJ0aGVyIG5vdGVzIGFuZCBpZGVhcyBmb3IgYWRkaXRpb25hbCBhY3Rpdml0aWVzDQoNCkhvcGVmdWxseSB5b3UgaGF2ZSBub3cgc2VlbiBob3cgaXQgaXMgZXh0cmVtZWx5IHN0cmFpZ2h0LWZvcndhcmQgdG8gcnVuIGFuZCBjYWxpYnJhdGUgV2lsc29uJ3MgZnVsbCBmYW1pbHkgb2YgU3BhdGlhbCBJbnRlcmFjdGlvbiBNb2RlbHMgaW4gUiB1c2luZyBHTE0gYW5kIFBvaXNzb24gUmVncmVzc2lvbi4gDQoNCiMjIzUuMSBTb21lIEZ1cnRoZXIgTm90ZXMNCg0KTm93IG1pZ2h0IGJlIHRoZSB0aW1lIHRvIG1lbnRpb24gdGhhdCBkZXNwaXRlIGV2ZXJ5dGhpbmcgSSd2ZSBzaG93biB5b3UsIHRoZXJlIGhhcyBiZWVuIHNvbWUgZGlzY3Vzc2lvbiBpbiB0aGUgbGl0ZXJhdHVyZSBhcyB0byB3aGV0aGVyIHRoZSBQb2lzc29uIE1vZGVsIGlzIGFjdHVhbGx5IGEgbWlzc3BlY2lmaWNhdGlvbiwgZXNwZWNpYWxseSBmb3IgbW9kZWxsaW5nIG1pZ3JhdGlvbiBmbG93cy4gSWYgeW91IGhhdmUgdGhlIHN0b21hY2ggZm9yIGl0LCBbdGhpcyBwYXBlciBieSBDb25nZG9uIGdvZXMgaW50byBhIGxvdCBvZiBkZXRhaWxdKGh0dHA6Ly9qb3VybmFscy5zYWdlcHViLmNvbS9kb2kvYWJzLzEwLjEwNjgvYTI1MTQ4MSkuDQoNClRoZSBpc3N1ZSBpcyBhIHRoaW5nIGNhbGxlZCAnb3ZlcmRpc3BlcnNpb24nIHdoaWNoLCB0cmFuc2xhdGVkLCBlc3NlbnRpYWxseSByZWxhdGVzIHRvIHRoZSBtb2RlbCBub3QgYmVpbmcgYWJsZSB0byBjYXB0dXJlIGFsbCBvZiB0aGUgdGhpbmdzIHRoYXQgY291bGQgYmUgZXhwbGFpbmluZyB0aGUgZmxvd3MgaW4gdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyB0aGF0IGFyZSBzdXBwbGllZCB0byB0aGUgbW9kZWwuIFRoZSBkZXRhaWxzIGFyZSB0ZWRpb3VzIGFuZCBvbmx5IHJlYWxseSBpbnRlbGxpZ2libGUgdG8gdGhvc2Ugd2l0aCBhIHN0YXRpc3RpY3MgYmFja2dyb3VuZC4gSWYgeW91IHdhbnQgYSBzdGFydGVyLCBbdHJ5IGhlcmVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL092ZXJkaXNwZXJzaW9uKSwgYnV0IGluIHByYWN0aWNhbCB0ZXJtcywgd2UgY2FuIGdldCBhcm91bmQgdGhpcyBwcm9ibGVtIGJ5IGZpdHRpbmcgYSB2ZXJ5IHNpbWlsYXIgc29ydCBvZiByZWdyZXNzaW9uIG1vZGVsIGNhbGxlZCB0aGUgKm5lZ2F0aXZlIGJpbm9taWFsKiByZWdyZXNzaW9uIG1vZGVsLiANCg0KSWYgeW91IHdpc2gsIHlvdSBjYW4gcmVhZCB1cCBhbmQgZXhwZXJpbWVudCB3aXRoIHRoaXMgbW9kZWwgLSB5b3UgY2FuIGZpdCBpdCBpbiBleGFjdGx5IHRoZSBzYW1lIHdheSBhcyB0aGUgYGdsbWAgbW9kZWwgYnV0IHVzaW5nIGEgZnVuY3Rpb24gY2FsbGVkIGBnbG0ubmJgIHdoaWNoIGlzIHBhcnQgb2YgdGhlIGBtYXNzYCBwYWNrYWdlLiBUaGUgbmVnYXRpdmUgYmlub21pYWwgbW9kZWwgaGFzIGFuIGV4dHJhIHBhcmFtZXRlciBpbiB0aGUgbW9kZWwgZm9yIG92ZXJkaXNwZXJzaW9uLiBZb3UgeW91IGRvIHRyeSB0aGlzLCB5b3Ugd2lsbCBhbG1vc3QgY2VydGFpbmx5IGRpc2NvdmVyIHRoYXQgeW91ciByZXN1bHRzIGJhcmVseSBjaGFuZ2UgLSBidXQgaGVsbCwgeW91IG1pZ2h0IGtlZXAgYSBwZWRhbnRpYyByZXZpZXdlciBhdCBiYXkgaWYgeW91IHN1Ym1pdCB0aGlzIHRvIGEgam91cm5hbCAobm90IHRoYXQgSSdtIHNwZWFraW5nIGZyb20gZXhwZXJpZW5jZSBvciBhbnl0aGluZykuIA0KDQojIyNBbmQgc29tZSBtb3JlIGNvbW1lbnRzDQoNCkFub3RoZXIgdGhpbmcgdG8gbm90ZSBpcyB0aGF0IHRoZSBleGFtcGxlIHdlIHVzZWQgaGVyZSBoYWQgcXVpdGUgbmVhdCBkYXRhLiBZb3Ugd2lsbCBhbG1vc3QgY2VydGFpbmx5IHJ1biBpbnRvIHByb2JsZW1zIGlmIHlvdSBoYXZlIHNwYXJzZSBkYXRhIG9yIHByZWRpY3RvcnMgd2l0aCAwcyBpbiB0aGVtLiBJZiB0aGlzIGhhcHBlbnMsIHRoZW4geW91IG1pZ2h0IG5lZWQgdG8gZWl0aGVyIGRyb3Agc29tZSByb3dzIGluIHlvdXIgZGF0YSAoaWYgcG9wdWxhdGVkIHdpdGggMHMpIG9yIHN1YnN0aXR1dGUgMHMgZm9yIHZlcnkgc21hbGwgbnVtYmVycywgbXVjaCBsZXNzIHRoYW4gMSwgYnV0IGdyZWF0ZXIgdGhhbiAwICh0aGlzIGlzIGJlY2F1c2UgeW91IGNhbid0IHRha2UgdGhlIGxvZyBvZiAwKQ0KDQpBbmQgYW5vdGhlciB0aGluZyB0byBub3RlIGlzIHRoYXQgb3VyIGZsb3cgZGF0YSBhbmQgb3VyIHByZWRpY3RvcnMgd2VyZSBhbGwgaW4gYW5kIGFyb3VuZCB0aGUgc2FtZSBvcmRlciBvciBtYWduaXR1ZGUuIElmIHlvdSBzdWRkZW5seSBnZXQgZGF0YSB0aGF0IChzdWNoIGFzIHBvcHVsYXRpb24gbWFzc2VzIGF0IG9yaWdpbnMgYW5kIGRlc3RpbmF0aW9ucykgdGhhdCBhcmUgYW4gb3JkZXIgb2YgbWFnbml0dWRlIGRpZmZlcmVudCAoaS5lLiBwb3B1bGF0aW9ucyBhYm91dCB0ZW4gdGltZXMgbGFyZ2VyIGluIGRpZmZlcmVudCBsb2NhdGlvbnMpIHRoZW4gdGhlIG1vZGVsIGVzdGltYXRlcyBtaWdodCBiZSBiaWFzZWQuIEZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgcGFja2FnZXMgYXZhaWxhYmxlIHRvIGhlbHAgdXMgd2l0aCB0aGVzZSBwcm9ibGVtcyBhcyB3ZWxsLiBUaGUgW2Byb2J1c3RiYXNlYCBwYWNrYWdlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcm9idXN0YmFzZS9pbmRleC5odG1sKSBmZWF0dXJlcyBhIGZ1bmN0aW9uIGNhbGxlZCBgZ2xtcm9iYCB3aGljaCB3aWxsIGRlYWwgd2l0aCB0aGlzIGlzc3VlcyAoYWdhaW4sIHlvdXIgcmVzdWx0cyBwcm9iYWJseSB3b24ndCBjaGFuZ2UgbXVjaCwgYnV0IHdvcnRoIGtub3dpbmcpLg0KDQojIyM1LjIgRnVydGhlciBBY3Rpdml0aWVzDQoNCjEuIFRlc3RpbmcgdGhlc2UgbW9kZWxzIG91dCBvbiB0aGUgd2hvbGUgb2YgTG9uZG9uIGFuZCBmb3IgZGlmZmVyZW50IHllYXJzDQogICAgKyBZb3UndmUgYmVlbiBwbGF5aW5nIGFyb3VuZCB3aXRoIGp1c3QgYSBzbWFsbCA3IGJvcm91Z2ggc2FtcGxlLCB3aHkgbm90IHRyeSB0aGUgZnVsbCBMb25kb24gc3lzdGVtLiANCiAgICArWW91IGNhbiBhbHNvIHRyeSBhbmQgZG93bmxvYWQgc29tZSBzaW1pbGFyIGRhdGEgZnJvbSB0aGUgMjAxMSBDZW5zdXMgZnJvbSBbV2ljaWRdKGh0dHA6Ly93aWNpZC51a2RhdGFzZXJ2aWNlLmFjLnVrLykgLSBzZWUgaWYgdXNpbmcgJE9pJCBhbmQgJERqJCB0b3RhbHMgYW5kIHRoZSBwYXJhbWV0ZXJzIHlvdSBjYWxpYnJhdGVkIG9uIHRoZSAyMDAxIGRhdGEsIHdoZXRoZXIgeW91IGNhbiBnZXQgcmVhc29uYWJsZSBlc3RpbWF0ZXMgb2YgdGhlIDIwMTEgZmxvd3MuDQogICAgK0hvdyBoYXZlIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGNoYW5nZWQgYmV0d2VlbiAyMDAxIGFuZCAyMDExIC0gd2hhdCBkb2VzIHRoaXMgbWVhbg0KDQoNCjIuIFZpc3VhbGlzaW5nIHlvdXIgZmxvdyBlc3RpbWF0ZXMNCiAgICArIHRyeSB1c2luZyB0aGUgbWV0aG9kcyBpbiBgc3RwbGFucmAgYW5kIGBsZWFmbGV0YCBmcm9tIFt0aGUgbGFzdCBwcmFjdGljYWxdKGh0dHBzOi8vcnB1YnMuY29tL2FkYW1fZGVubmV0dC8yNTcyMzEpIHRvIHZpc3VhbGlzZSBzb21lIG9mIHlvdXIgZmxvdyBlc3RpbWF0ZXMgb3IgZmxvdyByZXNpZHVhbHMuLi4NCg0KDQo=