A good friend of mine works for a car dealership, and although I had always joked about buying a car from him someday, that day finally arrived in 2015. My 2003 orange Honda Element (known by friends as “The Uglement”… sort of rolls off the tongue) was on its last legs. The struts and suspension were shot, engine clicking and struggling to turn over, and the body and interior were worse for wear after over a decade of fieldwork (I once transported an adult sea turtle in the car, which took up the entire trunk and backseat). I had been saving for a new car since acquiring The Uglement, and so, somewhat reluctantly, I decided it was time to zero-out the fund.

I was living in Maine at the time, so a Subaru Outback seemed like the appropriate choice to blend in among the locals (also, I had considered one before purchasing the Element). Conveniently enough, my aformentioned friend worked with a Subaru dealership, and so I took a train down to Virginia, and drove home in the most Uglement looking 2016 Subaru Outback I could find.

Element vs Outback

Element vs Outback

As with most newer cars, my Outback has an integrated display showing real-time, and trip averaged, Miles Per Gallon (MPG). For my first few weeks with the new car (now known as “Ruby”), I entertained myself with the MPG readout by trying to be as fuel efficient as possible. As with any predicted estimate though… I thought the MPG readout required some validation.

In October of 2015, only a few weeks after buying the new car, I began gathering my own MPG data for the car with the following procedure:

  1. Fill the gas tank. Use the automatic stop on the gas nozzle to determine when the tank is full.

  2. Reset the trip odometer in the Subaru. The car can calculate the MPG for a specific trip, so I can directly relate the miles traveled to a 2016 Subaru Outback calculated MPG estimate.

  3. Drive!

  4. Before filling up the gas tank again, write down miles traveled on the trip and estimated MPG from the odometer and dashboard display.

  5. Fill the gas tank, again using the automatic stop on the gas nozzle to determine when the tank is full. Write down how many gallons filled the tank (and hence were used since the last fill up).

  6. Reset the trip odomoeter and MPG display

  7. Go back to step 2

These data (Date, Miles traveled, Gallons used, Actual MPG (calculated from the last two values), and Computer MPG) are displayed below (the first few lines at least).

The next step is to look at the relationship between Computer estimated and Actual MPG. We can use linear regression for this, but first we take a quick look at how well correlated the data are, to which of course the answer is “very well”.

cor(mpg.dat$Computer.MPG,mpg.dat$Actual.MPG)
[1] 0.9629931

Now, we could use a simple linear regression (‘lm’ function), but what we really want to understand is the variation around the regression parameters - specifically the y intercept (‘a’ in the equation ‘y=a+bx’), which will tell us the deviation between computer predicted and actual MPG if we constrain the slope (‘b’ in the afformentioned equation) to ‘1’ (so, just assuming the relationship between computer and actual MPG is 1 to 1). This presents a good opportunity to use a Bayesian method with the package ‘rjags’.

So we fit the following model:

yi∼N(μi,τ)
μi=β+1*xi
β∼N(0,0.001)
τ=1/(σ^2)
σ∼U(0,100)

We construct this model, and run it for 1000 iterations on each of 3 chains using JAGS (Just Another Gibs Samopler… equivalent to WinBugs language).

library(rjags)
library(coda)
mpg.mod="
model {
   #Likelihood part of the model
   for (i in 1:n) { # itterate over each point
      y[i]~dnorm(mu[i],tau) #tau is precision (1 / variance)
      mu[i] <- beta+1*x[i] #linear relation constrained to a slope of 1,
                           #so only the intercept is allowed to vary
   }
   #Speciy your priors
   beta ~ dnorm(0,0.001) # intercept normally distributed
   tau <- 1 / (sigma * sigma) # variance as the squared standed deviation
   sigma~dunif(0,100) # standard deviation based on a uniform prior
}"
mpg.jagsmodel <- jags.model(textConnection(mpg.mod), # call the model
                   data = list('y' = mpg.dat$Actual.MPG,
                               'x' = mpg.dat$Computer.MPG,
                               'n' = length(mpg.dat$Computer.MPG)
                              ),
                   n.chains = 3, #how many parallel chains to run
                   n.adapt = 100) # How many samples should be thrown away as part of the 
                                  # adaptive sampling period of the chain
update(mpg.jagsmodel, 1000) # run the model another 1000 itterations as a burn in
mpg.jagsmodel.samples<-coda.samples(mpg.jagsmodel, # now take this simulation run
             c('beta'), # sample these variables
             1000) # take this many samples

We can visualize the Priors for alpha and sigma.

#Prior for beta (β)
plot(c(seq(-1,1,.0001)),dnorm(seq(-1,1,.0001),0,0.001))

#Prior for sigma (σ)
plot(c(seq(0,200,1)),dunif(seq(0,200,1),0,100))

Now, looking at the model, we find that the intercept (beta, β) is negative, and the uncertainty around it does not cross 0

summary(mpg.jagsmodel.samples)

Iterations = 1101:2100
Thinning interval = 1 
Number of chains = 3 
Sample size per chain = 1000 

1. Empirical mean and standard deviation for each variable,
   plus standard error of the mean:

          Mean             SD       Naive SE Time-series SE 
     -1.188634       0.095158       0.001737       0.001737 

2. Quantiles for each variable:

   2.5%     25%     50%     75%   97.5% 
-1.3795 -1.2489 -1.1880 -1.1256 -0.9989 

The same can be seen by plotting the MCMC progression and density of beta (β) for one of our 3 chains from the analysis (we can also see the MCMC is pretty stable).

plot(mpg.jagsmodel.samples[[1]][,c("beta")])

Visualizing the output as a regression, we see the fit (red, solid) and 95% credible intervals around the fit (red, dashed) fall at least 1 MPG outside of a 1:1 relationship between Computer predicted MPG and Actual MPG with an intercept of 0 (blue, solid).

library(ggplot2)
quants<-summary(mpg.jagsmodel.samples)$quantiles
ggplot(data=mpg.dat,aes(y=Actual.MPG, x=Computer.MPG))+
geom_point()+
geom_abline(slope=1,intercept=quants[3],cex=.5,colour='red')+
geom_abline(slope=1,intercept=quants[1],cex=.5,colour='red',lty='dashed')+
geom_abline(slope=1,intercept=quants[5],cex=.5,colour='red',lty='dashed')+
geom_abline(slope=1,intercept=0,cex=.5,colour='blue')+
scale_x_continuous(name="Computer Predicted MPG",limits=c(min(c(mpg.dat$Actual.MPG,mpg.dat$Computer.MPGt)),max(c(mpg.dat$Actual.MPG,mpg.dat$Computer.MPG))))+
scale_y_continuous(name="Actual MPG",limits=c(min(c(mpg.dat$Actual.MPG,mpg.dat$Computer.MPG)),max(c(mpg.dat$Actual.MPG,mpg.dat$Computer.MPG))))+
  coord_fixed()+
theme_bw()

All in all, my Outback’s Computer predicts MPG pretty well. Even if I let the slope vary in the linear regression, slope (beta 2) comes out very close to 1, although now the 95% credibility intervals (2.5%-97.5% quantiles) of the y-intercept overlap with 0. This suggests that the relationship between Computer reported and Actual MPG (slope) is pretty direct (~1:1, as it should be), and that allowing for computer error in the precision of this relationship accounts for some portion of the computers overall deviation from actual car MPG (the y intercept).

mpg.mod2="
model {
   for (i in 1:n) { 
      y[i]~dnorm(mu[i],tau) 
      mu[i] <- beta1+beta2*x[i] 
   }
   beta1 ~ dnorm(0,0.001) # intercept normally distributed
   beta2 ~ dnorm(1,0.001) # intercept normally distributed
   tau <- 1 / (sigma * sigma) # variance as the squared standed deviation
   sigma~dunif(0,100) # standard deviation based on a uniform prior
}"
mpg.jagsmodel2 <- jags.model(textConnection(mpg.mod2), # call the model
                   data = list('y' = mpg.dat$Actual.MPG,
                               'x' = mpg.dat$Computer.MPG,
                               'n' = length(mpg.dat$Computer.MPG)
                              ),
                   n.chains = 3, 
                   n.adapt = 100) 
summary(mpg.jagsmodel2.samples)

Iterations = 1101:2100
Thinning interval = 1 
Number of chains = 3 
Sample size per chain = 1000 

1. Empirical mean and standard deviation for each variable,
   plus standard error of the mean:

         Mean      SD  Naive SE Time-series SE
beta1 -0.7032 0.85794 0.0156637       0.207444
beta2  0.9831 0.02965 0.0005413       0.007009

2. Quantiles for each variable:

        2.5%     25%     50%     75% 97.5%
beta1 -2.379 -1.2594 -0.7311 -0.1421 1.079
beta2  0.922  0.9636  0.9841  1.0021 1.042

Even allowing that slope to vary however, the majority of the y-intercept’s 95% credibility intervals fall < 0, and extremely few points fall on or above the 1:1 line (above: blue, solid). This all seems a bit too convenient, especially considering the marketing benefit of a seemingly higher MPG vehicle. In the end though, the computer readout of MPG is mostly within 1 MPG of the actual, and I imagine there is some fine print somewhere in the user manual that disclaims the potential inaccuracy. Also, I get FAR more MPG in my 2016 Subaru Outback than I did in the Uglement (RIP you orange beauty… she was scrapped), I now have a real-time readout of MPG performance to tailor my driving to, and the Outback performs fantastically in inclement weather. I recommend the car to anyone who asks… but I do disclaim 1 MPG overestimation of the readout… even if my analysis was on a sample size of 1 car.

LS0tCnRpdGxlOiAiUHJlY2lzaW9uIG9mIFlvdXIgQ2FyJ3MgUmVwb3J0ZWQgTVBHIHdpdGggQmF5ZXNpYW4gSW5mZXJlbmNlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpBIGdvb2QgZnJpZW5kIG9mIG1pbmUgd29ya3MgZm9yIGEgY2FyIGRlYWxlcnNoaXAsIGFuZCBhbHRob3VnaCBJIGhhZCBhbHdheXMgam9rZWQgYWJvdXQgYnV5aW5nIGEgY2FyIGZyb20gaGltIHNvbWVkYXksIHRoYXQgZGF5IGZpbmFsbHkgYXJyaXZlZCBpbiAyMDE1LiBNeSAyMDAzIG9yYW5nZSBIb25kYSBFbGVtZW50IChrbm93biBieSBmcmllbmRzIGFzICJUaGUgVWdsZW1lbnQiLi4uIHNvcnQgb2Ygcm9sbHMgb2ZmIHRoZSB0b25ndWUpIHdhcyBvbiBpdHMgbGFzdCBsZWdzLiBUaGUgc3RydXRzIGFuZCBzdXNwZW5zaW9uIHdlcmUgc2hvdCwgZW5naW5lIGNsaWNraW5nIGFuZCBzdHJ1Z2dsaW5nIHRvIHR1cm4gb3ZlciwgYW5kIHRoZSBib2R5IGFuZCBpbnRlcmlvciB3ZXJlICB3b3JzZSBmb3Igd2VhciBhZnRlciAgb3ZlciBhIGRlY2FkZSBvZiBmaWVsZHdvcmsgKEkgb25jZSB0cmFuc3BvcnRlZCBhbiBhZHVsdCBzZWEgdHVydGxlIGluIHRoZSBjYXIsIHdoaWNoIHRvb2sgdXAgdGhlIGVudGlyZSB0cnVuayBhbmQgYmFja3NlYXQpLiBJIGhhZCBiZWVuIHNhdmluZyBmb3IgYSBuZXcgY2FyIHNpbmNlIGFjcXVpcmluZyBUaGUgVWdsZW1lbnQsIGFuZCBzbywgc29tZXdoYXQgcmVsdWN0YW50bHksIEkgZGVjaWRlZCBpdCB3YXMgdGltZSB0byB6ZXJvLW91dCB0aGUgZnVuZC4KCkkgd2FzIGxpdmluZyBpbiBNYWluZSBhdCB0aGUgdGltZSwgc28gYSBTdWJhcnUgT3V0YmFjayBzZWVtZWQgbGlrZSB0aGUgYXBwcm9wcmlhdGUgY2hvaWNlIHRvIGJsZW5kIGluIGFtb25nIHRoZSBsb2NhbHMgKGFsc28sIEkgaGFkIGNvbnNpZGVyZWQgb25lIGJlZm9yZSBwdXJjaGFzaW5nIHRoZSBFbGVtZW50KS4gQ29udmVuaWVudGx5IGVub3VnaCwgbXkgYWZvcm1lbnRpb25lZCBmcmllbmQgd29ya2VkIHdpdGggYSBTdWJhcnUgZGVhbGVyc2hpcCwgYW5kIHNvIEkgdG9vayBhIHRyYWluIGRvd24gdG8gVmlyZ2luaWEsIGFuZCBkcm92ZSBob21lIGluIHRoZSBtb3N0IFVnbGVtZW50IGxvb2tpbmcgMjAxNiBTdWJhcnUgT3V0YmFjayBJIGNvdWxkIGZpbmQuCgohW0VsZW1lbnQgdnMgT3V0YmFja10oL1VzZXJzL3Njb3R0bW9yZWxsby9Ecm9wYm94L0FyY2hpdmVzL1BlcnNvbmFsL1JhbmRvbSBBbmFseXNpcy9SYW5kb21fUGVyc29uYWxfV29yay9lbGVtZW50c3ViYXJ1LmpwZykKCkFzIHdpdGggbW9zdCBuZXdlciBjYXJzLCBteSBPdXRiYWNrIGhhcyBhbiBpbnRlZ3JhdGVkIGRpc3BsYXkgc2hvd2luZyByZWFsLXRpbWUsIGFuZCB0cmlwIGF2ZXJhZ2VkLCBNaWxlcyBQZXIgR2FsbG9uIChNUEcpLiBGb3IgbXkgZmlyc3QgZmV3IHdlZWtzIHdpdGggdGhlIG5ldyBjYXIgKG5vdyBrbm93biBhcyAiUnVieSIpLCBJIGVudGVydGFpbmVkIG15c2VsZiB3aXRoIHRoZSBNUEcgcmVhZG91dCBieSB0cnlpbmcgdG8gYmUgYXMgZnVlbCBlZmZpY2llbnQgYXMgcG9zc2libGUuIEFzIHdpdGggYW55IHByZWRpY3RlZCBlc3RpbWF0ZSB0aG91Z2guLi4gSSB0aG91Z2h0IHRoZSBNUEcgcmVhZG91dCByZXF1aXJlZCBzb21lIHZhbGlkYXRpb24uCgpJbiBPY3RvYmVyIG9mIDIwMTUsIG9ubHkgYSBmZXcgd2Vla3MgYWZ0ZXIgYnV5aW5nIHRoZSBuZXcgY2FyLCBJIGJlZ2FuIGdhdGhlcmluZyBteSBvd24gTVBHIGRhdGEgZm9yIHRoZSBjYXIgd2l0aCB0aGUgZm9sbG93aW5nIHByb2NlZHVyZToKCgoxKSBGaWxsIHRoZSBnYXMgdGFuay4gVXNlIHRoZSBhdXRvbWF0aWMgc3RvcCBvbiB0aGUgZ2FzIG5venpsZSB0byBkZXRlcm1pbmUgd2hlbiB0aGUgdGFuayBpcyBmdWxsLgoKMikgUmVzZXQgdGhlIHRyaXAgb2RvbWV0ZXIgaW4gdGhlIFN1YmFydS4gVGhlIGNhciBjYW4gY2FsY3VsYXRlIHRoZSBNUEcgZm9yIGEgc3BlY2lmaWMgdHJpcCwgc28gSSBjYW4gZGlyZWN0bHkgcmVsYXRlIHRoZSBtaWxlcyB0cmF2ZWxlZCB0byBhIDIwMTYgU3ViYXJ1IE91dGJhY2sgY2FsY3VsYXRlZCBNUEcgZXN0aW1hdGUuCgozKSBEcml2ZSEKCjQpIEJlZm9yZSBmaWxsaW5nIHVwIHRoZSBnYXMgdGFuayBhZ2Fpbiwgd3JpdGUgZG93biBtaWxlcyB0cmF2ZWxlZCBvbiB0aGUgdHJpcCBhbmQgZXN0aW1hdGVkIE1QRyBmcm9tIHRoZSBvZG9tZXRlciBhbmQgZGFzaGJvYXJkIGRpc3BsYXkuCgo1KSBGaWxsIHRoZSBnYXMgdGFuaywgYWdhaW4gdXNpbmcgdGhlIGF1dG9tYXRpYyBzdG9wIG9uIHRoZSBnYXMgbm96emxlIHRvIGRldGVybWluZSB3aGVuIHRoZSB0YW5rIGlzIGZ1bGwuIFdyaXRlIGRvd24gaG93IG1hbnkgZ2FsbG9ucyBmaWxsZWQgdGhlIHRhbmsgKGFuZCBoZW5jZSB3ZXJlIHVzZWQgc2luY2UgdGhlIGxhc3QgZmlsbCB1cCkuCgo2KSBSZXNldCB0aGUgdHJpcCBvZG9tb2V0ZXIgYW5kIE1QRyBkaXNwbGF5Cgo3KSBHbyBiYWNrIHRvIHN0ZXAgMgoKClRoZXNlIGRhdGEgKERhdGUsIE1pbGVzIHRyYXZlbGVkLCBHYWxsb25zIHVzZWQsIEFjdHVhbCBNUEcgKGNhbGN1bGF0ZWQgZnJvbSB0aGUgbGFzdCB0d28gdmFsdWVzKSwgYW5kIENvbXB1dGVyIE1QRykgYXJlIGRpc3BsYXllZCBiZWxvdyAodGhlIGZpcnN0IGZldyBsaW5lcyBhdCBsZWFzdCkuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI0ltcG9ydCB0aGUgZGF0YSBmcm9tIGEgLmNzdiBmaWxlCm1wZy5kYXQ8LXJlYWQuY3N2KCIvVXNlcnMvc2NvdHRtb3JlbGxvL0Ryb3Bib3gvQXJjaGl2ZXMvUGVyc29uYWwvT3RoZXIvQ2FyIE1QRyBEYXRhLmNzdiIpCgojTG9vayBhdCB0aGUgZmlyc3QgZmV3IGxpbmVzCmhlYWQobXBnLmRhdCkKYGBgCgpUaGUgbmV4dCBzdGVwIGlzIHRvIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIENvbXB1dGVyIGVzdGltYXRlZCBhbmQgQWN0dWFsIE1QRy4gV2UgY2FuIHVzZSBsaW5lYXIgcmVncmVzc2lvbiBmb3IgdGhpcywgYnV0IGZpcnN0IHdlIHRha2UgYSBxdWljayBsb29rIGF0IGhvdyB3ZWxsIGNvcnJlbGF0ZWQgdGhlIGRhdGEgYXJlLCB0byB3aGljaCBvZiBjb3Vyc2UgdGhlIGFuc3dlciBpcyAidmVyeSB3ZWxsIi4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNvcihtcGcuZGF0JENvbXB1dGVyLk1QRyxtcGcuZGF0JEFjdHVhbC5NUEcpCmBgYAoKTm93LCB3ZSBjb3VsZCB1c2UgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gKCdsbScgZnVuY3Rpb24pLCBidXQgd2hhdCB3ZSByZWFsbHkgd2FudCB0byB1bmRlcnN0YW5kIGlzIHRoZSB2YXJpYXRpb24gYXJvdW5kIHRoZSByZWdyZXNzaW9uIHBhcmFtZXRlcnMgLSBzcGVjaWZpY2FsbHkgdGhlIHkgaW50ZXJjZXB0ICgnYScgaW4gdGhlIGVxdWF0aW9uICd5PWErYngnKSwgd2hpY2ggd2lsbCB0ZWxsIHVzIHRoZSBkZXZpYXRpb24gYmV0d2VlbiBjb21wdXRlciBwcmVkaWN0ZWQgYW5kIGFjdHVhbCBNUEcgaWYgd2UgY29uc3RyYWluIHRoZSBzbG9wZSAoJ2InIGluIHRoZSBhZmZvcm1lbnRpb25lZCBlcXVhdGlvbikgdG8gJzEnIChzbywganVzdCBhc3N1bWluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY29tcHV0ZXIgYW5kIGFjdHVhbCBNUEcgaXMgMSB0byAxKS4gVGhpcyBwcmVzZW50cyBhIGdvb2Qgb3Bwb3J0dW5pdHkgdG8gdXNlIGEgQmF5ZXNpYW4gbWV0aG9kIHdpdGggdGhlIHBhY2thZ2UgJ3JqYWdzJy4KClNvIHdlIGZpdCB0aGUgZm9sbG93aW5nIG1vZGVsOgoKeWniiLxOKM68aSzPhCkgICAgIArOvGk9zrIrMSp4aSAgICAgCs6y4oi8TigwLDAuMDAxKSAgICAgCs+EPTEvKM+DXjIpICAgICAKz4PiiLxVKDAsMTAwKQoKV2UgY29uc3RydWN0IHRoaXMgbW9kZWwsIGFuZCBydW4gaXQgZm9yIDEwMDAgaXRlcmF0aW9ucyBvbiBlYWNoIG9mIDMgY2hhaW5zIHVzaW5nIEpBR1MgKEp1c3QgQW5vdGhlciBHaWJzIFNhbW9wbGVyLi4uIGVxdWl2YWxlbnQgdG8gV2luQnVncyBsYW5ndWFnZSkuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLHJlc3VsdHM9ImhpZGUifQpsaWJyYXJ5KHJqYWdzKQpsaWJyYXJ5KGNvZGEpCgoKbXBnLm1vZD0iCm1vZGVsIHsKICAgI0xpa2VsaWhvb2QgcGFydCBvZiB0aGUgbW9kZWwKICAgZm9yIChpIGluIDE6bikgeyAjIGl0dGVyYXRlIG92ZXIgZWFjaCBwb2ludAogICAgICB5W2ldfmRub3JtKG11W2ldLHRhdSkgI3RhdSBpcyBwcmVjaXNpb24gKDEgLyB2YXJpYW5jZSkKICAgICAgbXVbaV0gPC0gYmV0YSsxKnhbaV0gI2xpbmVhciByZWxhdGlvbiBjb25zdHJhaW5lZCB0byBhIHNsb3BlIG9mIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICNzbyBvbmx5IHRoZSBpbnRlcmNlcHQgaXMgYWxsb3dlZCB0byB2YXJ5CiAgIH0KCiAgICNTcGVjaXkgeW91ciBwcmlvcnMKICAgYmV0YSB+IGRub3JtKDAsMC4wMDEpICMgaW50ZXJjZXB0IG5vcm1hbGx5IGRpc3RyaWJ1dGVkCiAgIHRhdSA8LSAxIC8gKHNpZ21hICogc2lnbWEpICMgdmFyaWFuY2UgYXMgdGhlIHNxdWFyZWQgc3RhbmRlZCBkZXZpYXRpb24KICAgc2lnbWF+ZHVuaWYoMCwxMDApICMgc3RhbmRhcmQgZGV2aWF0aW9uIGJhc2VkIG9uIGEgdW5pZm9ybSBwcmlvcgp9IgoKbXBnLmphZ3Ntb2RlbCA8LSBqYWdzLm1vZGVsKHRleHRDb25uZWN0aW9uKG1wZy5tb2QpLCAjIGNhbGwgdGhlIG1vZGVsCiAgICAgICAgICAgICAgICAgICBkYXRhID0gbGlzdCgneScgPSBtcGcuZGF0JEFjdHVhbC5NUEcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAneCcgPSBtcGcuZGF0JENvbXB1dGVyLk1QRywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICduJyA9IGxlbmd0aChtcGcuZGF0JENvbXB1dGVyLk1QRykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgIG4uY2hhaW5zID0gMywgI2hvdyBtYW55IHBhcmFsbGVsIGNoYWlucyB0byBydW4KICAgICAgICAgICAgICAgICAgIG4uYWRhcHQgPSAxMDApICMgSG93IG1hbnkgc2FtcGxlcyBzaG91bGQgYmUgdGhyb3duIGF3YXkgYXMgcGFydCBvZiB0aGUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFkYXB0aXZlIHNhbXBsaW5nIHBlcmlvZCBvZiB0aGUgY2hhaW4KCnVwZGF0ZShtcGcuamFnc21vZGVsLCAxMDAwKSAjIHJ1biB0aGUgbW9kZWwgYW5vdGhlciAxMDAwIGl0dGVyYXRpb25zIGFzIGEgYnVybiBpbgoKbXBnLmphZ3Ntb2RlbC5zYW1wbGVzPC1jb2RhLnNhbXBsZXMobXBnLmphZ3Ntb2RlbCwgIyBub3cgdGFrZSB0aGlzIHNpbXVsYXRpb24gcnVuCiAgICAgICAgICAgICBjKCdiZXRhJyksICMgc2FtcGxlIHRoZXNlIHZhcmlhYmxlcwogICAgICAgICAgICAgMTAwMCkgIyB0YWtlIHRoaXMgbWFueSBzYW1wbGVzCgpgYGAKV2UgY2FuIHZpc3VhbGl6ZSB0aGUgUHJpb3JzIGZvciBhbHBoYSBhbmQgc2lnbWEuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojUHJpb3IgZm9yIGJldGEgKM6yKQpwbG90KGMoc2VxKC0xLDEsLjAwMDEpKSxkbm9ybShzZXEoLTEsMSwuMDAwMSksMCwwLjAwMSkpCiNQcmlvciBmb3Igc2lnbWEgKM+DKQpwbG90KGMoc2VxKDAsMjAwLDEpKSxkdW5pZihzZXEoMCwyMDAsMSksMCwxMDApKQpgYGAKCk5vdywgbG9va2luZyBhdCB0aGUgbW9kZWwsIHdlIGZpbmQgdGhhdCB0aGUgaW50ZXJjZXB0IChiZXRhLCDOsikgaXMgbmVnYXRpdmUsIGFuZCB0aGUgdW5jZXJ0YWludHkgYXJvdW5kIGl0IGRvZXMgbm90IGNyb3NzIDAKYGBge3J9CnN1bW1hcnkobXBnLmphZ3Ntb2RlbC5zYW1wbGVzKQpgYGAKClRoZSBzYW1lIGNhbiBiZSBzZWVuIGJ5IHBsb3R0aW5nIHRoZSBNQ01DIHByb2dyZXNzaW9uIGFuZCBkZW5zaXR5IG9mIGJldGEgKM6yKSBmb3Igb25lIG9mIG91ciAzIGNoYWlucyBmcm9tIHRoZSBhbmFseXNpcyAod2UgY2FuIGFsc28gc2VlIHRoZSBNQ01DIGlzIHByZXR0eSBzdGFibGUpLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGxvdChtcGcuamFnc21vZGVsLnNhbXBsZXNbWzFdXVssYygiYmV0YSIpXSkKYGBgCgoKVmlzdWFsaXppbmcgdGhlIG91dHB1dCBhcyBhIHJlZ3Jlc3Npb24sIHdlIHNlZSB0aGUgZml0IChyZWQsIHNvbGlkKSBhbmQgOTUlIGNyZWRpYmxlIGludGVydmFscyBhcm91bmQgdGhlIGZpdCAocmVkLCBkYXNoZWQpIGZhbGwgYXQgbGVhc3QgMSBNUEcgb3V0c2lkZSBvZiBhIDE6MSByZWxhdGlvbnNoaXAgYmV0d2VlbiBDb21wdXRlciBwcmVkaWN0ZWQgTVBHIGFuZCBBY3R1YWwgTVBHIHdpdGggYW4gaW50ZXJjZXB0IG9mIDAgKGJsdWUsIHNvbGlkKS4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKcXVhbnRzPC1zdW1tYXJ5KG1wZy5qYWdzbW9kZWwuc2FtcGxlcykkcXVhbnRpbGVzCgpnZ3Bsb3QoZGF0YT1tcGcuZGF0LGFlcyh5PUFjdHVhbC5NUEcsIHg9Q29tcHV0ZXIuTVBHKSkrCmdlb21fcG9pbnQoKSsKZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9cXVhbnRzWzNdLGNleD0uNSxjb2xvdXI9J3JlZCcpKwpnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD1xdWFudHNbMV0sY2V4PS41LGNvbG91cj0ncmVkJyxsdHk9J2Rhc2hlZCcpKwpnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD1xdWFudHNbNV0sY2V4PS41LGNvbG91cj0ncmVkJyxsdHk9J2Rhc2hlZCcpKwpnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wLGNleD0uNSxjb2xvdXI9J2JsdWUnKSsKc2NhbGVfeF9jb250aW51b3VzKG5hbWU9IkNvbXB1dGVyIFByZWRpY3RlZCBNUEciLGxpbWl0cz1jKG1pbihjKG1wZy5kYXQkQWN0dWFsLk1QRyxtcGcuZGF0JENvbXB1dGVyLk1QR3QpKSxtYXgoYyhtcGcuZGF0JEFjdHVhbC5NUEcsbXBnLmRhdCRDb21wdXRlci5NUEcpKSkpKwpzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0iQWN0dWFsIE1QRyIsbGltaXRzPWMobWluKGMobXBnLmRhdCRBY3R1YWwuTVBHLG1wZy5kYXQkQ29tcHV0ZXIuTVBHKSksbWF4KGMobXBnLmRhdCRBY3R1YWwuTVBHLG1wZy5kYXQkQ29tcHV0ZXIuTVBHKSkpKSsKICBjb29yZF9maXhlZCgpKwp0aGVtZV9idygpCgpgYGAKCkFsbCBpbiBhbGwsIG15IE91dGJhY2sncyBDb21wdXRlciBwcmVkaWN0cyBNUEcgcHJldHR5IHdlbGwuIEV2ZW4gaWYgSSBsZXQgdGhlIHNsb3BlIHZhcnkgaW4gdGhlIGxpbmVhciByZWdyZXNzaW9uLCBzbG9wZSAoYmV0YSAyKSBjb21lcyBvdXQgdmVyeSBjbG9zZSB0byAxLCBhbHRob3VnaCBub3cgdGhlIDk1JSBjcmVkaWJpbGl0eSBpbnRlcnZhbHMgKDIuNSUtOTcuNSUgcXVhbnRpbGVzKSBvZiB0aGUgeS1pbnRlcmNlcHQgIG92ZXJsYXAgd2l0aCAwLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIENvbXB1dGVyIHJlcG9ydGVkIGFuZCBBY3R1YWwgTVBHIChzbG9wZSkgaXMgcHJldHR5IGRpcmVjdCAofjE6MSwgYXMgaXQgc2hvdWxkIGJlKSwgYW5kIHRoYXQgYWxsb3dpbmcgZm9yIGNvbXB1dGVyIGVycm9yIGluIHRoZSBwcmVjaXNpb24gb2YgdGhpcyByZWxhdGlvbnNoaXAgIGFjY291bnRzIGZvciBzb21lIHBvcnRpb24gb2YgdGhlIGNvbXB1dGVycyBvdmVyYWxsIGRldmlhdGlvbiBmcm9tIGFjdHVhbCBjYXIgTVBHICh0aGUgeSBpbnRlcmNlcHQpLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UscmVzdWx0cz0iaGlkZSJ9CgptcGcubW9kMj0iCm1vZGVsIHsKICAgZm9yIChpIGluIDE6bikgeyAKICAgICAgeVtpXX5kbm9ybShtdVtpXSx0YXUpIAogICAgICBtdVtpXSA8LSBiZXRhMStiZXRhMip4W2ldIAogICB9CgoKICAgYmV0YTEgfiBkbm9ybSgwLDAuMDAxKSAjIGludGVyY2VwdCBub3JtYWxseSBkaXN0cmlidXRlZAogICBiZXRhMiB+IGRub3JtKDEsMC4wMDEpICMgaW50ZXJjZXB0IG5vcm1hbGx5IGRpc3RyaWJ1dGVkCiAgIHRhdSA8LSAxIC8gKHNpZ21hICogc2lnbWEpICMgdmFyaWFuY2UgYXMgdGhlIHNxdWFyZWQgc3RhbmRlZCBkZXZpYXRpb24KICAgc2lnbWF+ZHVuaWYoMCwxMDApICMgc3RhbmRhcmQgZGV2aWF0aW9uIGJhc2VkIG9uIGEgdW5pZm9ybSBwcmlvcgp9IgoKbXBnLmphZ3Ntb2RlbDIgPC0gamFncy5tb2RlbCh0ZXh0Q29ubmVjdGlvbihtcGcubW9kMiksICMgY2FsbCB0aGUgbW9kZWwKICAgICAgICAgICAgICAgICAgIGRhdGEgPSBsaXN0KCd5JyA9IG1wZy5kYXQkQWN0dWFsLk1QRywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd4JyA9IG1wZy5kYXQkQ29tcHV0ZXIuTVBHLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ24nID0gbGVuZ3RoKG1wZy5kYXQkQ29tcHV0ZXIuTVBHKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgbi5jaGFpbnMgPSAzLCAKICAgICAgICAgICAgICAgICAgIG4uYWRhcHQgPSAxMDApIAoKdXBkYXRlKG1wZy5qYWdzbW9kZWwyLCAxMDAwKSAKCm1wZy5qYWdzbW9kZWwyLnNhbXBsZXM8LWNvZGEuc2FtcGxlcyhtcGcuamFnc21vZGVsMixjKCdiZXRhMScsJ2JldGEyJyksIDEwMDApCgpgYGAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnN1bW1hcnkobXBnLmphZ3Ntb2RlbDIuc2FtcGxlcykKYGBgCgogRXZlbiBhbGxvd2luZyB0aGF0IHNsb3BlIHRvIHZhcnkgaG93ZXZlciwgdGhlIG1ham9yaXR5IG9mIHRoZSB5LWludGVyY2VwdCdzIDk1JSBjcmVkaWJpbGl0eSBpbnRlcnZhbHMgZmFsbCA8IDAsIGFuZCBleHRyZW1lbHkgZmV3IHBvaW50cyBmYWxsIG9uIG9yIGFib3ZlIHRoZSAxOjEgbGluZSAoYWJvdmU6IGJsdWUsIHNvbGlkKS4gVGhpcyBhbGwgc2VlbXMgYSBiaXQgdG9vIGNvbnZlbmllbnQsIGVzcGVjaWFsbHkgY29uc2lkZXJpbmcgdGhlIG1hcmtldGluZyBiZW5lZml0IG9mIGEgc2VlbWluZ2x5IGhpZ2hlciBNUEcgdmVoaWNsZS4gSW4gdGhlIGVuZCB0aG91Z2gsIHRoZSBjb21wdXRlciByZWFkb3V0IG9mIE1QRyBpcyBtb3N0bHkgd2l0aGluIDEgTVBHIG9mIHRoZSBhY3R1YWwsIGFuZCBJIGltYWdpbmUgdGhlcmUgaXMgc29tZSBmaW5lIHByaW50IHNvbWV3aGVyZSBpbiB0aGUgdXNlciBtYW51YWwgdGhhdCBkaXNjbGFpbXMgdGhlIHBvdGVudGlhbCBpbmFjY3VyYWN5LiBBbHNvLCBJIGdldCBGQVIgbW9yZSBNUEcgaW4gbXkgMjAxNiBTdWJhcnUgT3V0YmFjayB0aGFuIEkgZGlkIGluIHRoZSBVZ2xlbWVudCAoUklQIHlvdSBvcmFuZ2UgYmVhdXR5Li4uIHNoZSB3YXMgc2NyYXBwZWQpLCBJIG5vdyBoYXZlIGEgcmVhbC10aW1lIHJlYWRvdXQgb2YgTVBHIHBlcmZvcm1hbmNlIHRvIHRhaWxvciBteSBkcml2aW5nIHRvLCBhbmQgdGhlIE91dGJhY2sgcGVyZm9ybXMgZmFudGFzdGljYWxseSBpbiBpbmNsZW1lbnQgd2VhdGhlci4gSSByZWNvbW1lbmQgdGhlIGNhciB0byBhbnlvbmUgd2hvIGFza3MuLi4gYnV0IEkgZG8gZGlzY2xhaW0gMSBNUEcgb3ZlcmVzdGltYXRpb24gb2YgdGhlIHJlYWRvdXQuLi4gZXZlbiBpZiBteSBhbmFseXNpcyB3YXMgb24gYSBzYW1wbGUgc2l6ZSBvZiAxIGNhci4KCgo=