By Angus Ball
Alpha diversity just got COMPLICATED Ill be merging these two
tutorials, and honestly, they are going to be alot better than I could
ever be so go give them a run through! I’m still gonna explain here so
we have a S.O.P but when the author of the code offers to explain how to
use it, don’t turn your nose https://github.com/adw96/DivNet/blob/main/vignettes/getting-started.Rmd
https://adw96.github.io/breakaway/articles/breakaway.html
https://github.com/adw96/breakaway/blob/main/vignettes/diversity-hypothesis-testing.Rmd
Irregardless lets go! start by uploading your data!
filepulling <- "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\Kenzies Data\\Kenzies data for michael\\physeq_Key_angusfixed2.rds" #add your file location here
physeq_Key <- readRDS(file = filepulling)
Then your packages
library(DivNet)
library(phyloseq)
library(breakaway)
library(speedyseq)
library(magrittr)
library(tibble)
library(tidyverse)
This alpha diversity program assumes samples are from the same
population of species (as they often are!) to estimate the true
abundance of species and have an altogether more accurate alpha
diversity metric. First things first, this is a taxing program to run so
we are going to look at the order level, you can change this just as
easy.
physeq_order <- tax_glom(physeq_Key, taxrank="Order") #conglomerates everything to an order level
physeq_order %>% sample_variables()
[1] "Sample_No" "Location" "Layer" "Treatment" "Site" "Place" "SeqTech"
We just combined all the taxonomy so the lowest level of depth is at
an order level Now lets run the stats!
#ouputobject <- physeq_object %>% divnet(tuning = fast or careful,
# formula = ~ covariate + covariate)
set.seed(6458740)
divnet_order <- physeq_order %>% divnet(tuning = "careful")
divnet_order_Treatment <- physeq_order %>% divnet(tuning = "careful", formula = ~ Treatment)
divnet_order_Location <- physeq_order %>% divnet(tuning = "careful", formula = ~ Location)
divnet_order_Treatment_Location <- physeq_order %>% divnet(tuning = "careful", formula = ~ Treatment + Location)
divnet_order_Treatment_Location_Layer <- physeq_order %>% divnet(tuning = "careful", formula = ~ Treatment + Location + Layer) #only running this one because its the only one we think matters
divnet_order_Location_Layer <- physeq_order %>% divnet(tuning = "careful", formula = ~ Location + Layer)
Okay I just did alot of things, I get that, First things first. we
are taking our physeq object and applying (%>%) the divnet command to
it. We are apply this code at a “careful” level. i.e. this means more
computing power is going into the equation and you’ll get a more
accurate representation of the stats being done (you can also do this
“fast” but uhh dont?). next is this formula stuff. Within data sets we
have covariates (go search up and read what a covariate is now), and
when microbial communities covary with say a location each sample
reflects a part of the population that can be “combined” statistically
so that we can estimate the total population diversity. I’m pretty sure
the covariates within this dataset are treatment AND location AND Layer;
however, we’re gonna run all of them just to see what happens! fun
right?
PS. I set a seed because I’ll likely be rerunning code to make sure
it works, and I dont want to go around changing my conclusions! PS2.
This can take a half second to run
These take FIVE-EVER to run so Imma make and save copies of the
objects
divnetfiles <- "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\Kenzies Data\\old stuff\\"
saveRDS(divnet_order, file = file.path(divnetfiles, "divnet_order.rds"))
saveRDS(divnet_order_Location, file = file.path(divnetfiles, "divnet_order_Location.rds"))
saveRDS(divnet_order_Treatment, file = file.path(divnetfiles, "divnet_order_Treatment.rds"))
saveRDS(divnet_order_Treatment_Location, file = file.path(divnetfiles,"divnet_order_Treatment_Location.rds"))
saveRDS(divnet_order_Treatment_Location_Layer, file = file.path(divnetfiles,"divnet_order_Treatment_Location_Layer.rds"))
saveRDS(divnet_order_Location_Layer, file = file.path(divnetfiles,"divnet_order_Location_Layer.rds"))
divnet_order_Location_Layer <- readRDS(file = file.path(divnetfiles,"divnet_order_Location_Layer.rds"))
divnet_order_Treatment_Location <- readRDS(file = file.path(divnetfiles, "divnet_order_Treatment_Location.rds"))
divnet_order_Treatment <- readRDS(file = file.path(divnetfiles,"divnet_order_Treatment.rds"))
divnet_order_Location <- readRDS(file = file.path(divnetfiles,"divnet_order_Location.rds"))
divnet_order <- readRDS(file = file.path(divnetfiles,"divnet_order.rds"))
divnet_order_Treatment_Location_Layer <- readRDS(file = file.path(divnetfiles,"divnet_order_Treatment_Location_Layer.rds"))
Now lets plot it out! This is a plot of the raw diversity estimates
for each sample with no covariate information shared across samples.
divnet_order$shannon %>%
plot(physeq_order)

look at that graph that means absolutely nothing to me! oh yeah
Now lets add some covariate info, treatments
divnet_order_Treatment$shannon %>%
plot(physeq_order, color = "Treatment")

okay something more, how about location covariate info instead?
divnet_order_Location$shannon %>%
plot(physeq_order, color = "Location")

okay…okay, adding them together
divnet_order_Treatment_Location$shannon %>%
plot(physeq_order, color = "Location", shape = "Treatment")

and finally with all of the covariate information
divnet_order_Treatment_Location_Layer$shannon %>%
plot(physeq_order, color = "Location", shape = "Layer")

But perhaps this is too much covariate information does fallow versus
replicate really reflect unqiue and different covariates? perhaps no.
This inlies a very important part of this kind of analysis. Since the
answers you get are partially based on the covariates you choose, you
need to be damned sure you choose the correct ones.
divnet_order_Location_Layer$shannon %>%
plot(physeq_order, color = "Location", shape = "Layer")

Based on that our analysis we be on data that includes location and
layer covariates
this creates an object thats pretty much the key from phyloseq
creation, we need it to hold on to metadata
sample_data_key <- physeq_order %>%
sample_data %>%
tibble::as_tibble() %>%
dplyr::mutate("sample_names" = physeq_order %>% sample_names )
lets run the test!, we need to make three objects to use this
function, theyre just more objects that betta expects and we just need
to bend to its will
estimates <- divnet_order_Location_Layer$shannon %>% summary %$% estimate #this is where our shannon data is stored
ses <- sqrt(divnet_order_Location_Layer$`shannon-variance`) #take variance and turns it into standard error
X <- model.matrix(~Treatment, data = sample_data_key) #this creates a matrix that the model can use, its the sample sample_data_key but with numbers instead of words, also were we specify that we want to compare treatment
TLL_Treatment_betta <- betta(estimates, ses, X) # run the command
TLL_Treatment_betta$table #whats the p values
Estimates Standard Errors p-values
(Intercept) 1.940003783 0.03364096 0.000
TreatmentPosCont 0.015072262 0.09003531 0.867
TreatmentReplicate -0.004040079 0.04078468 0.921
Here we imputed the estimates of diversity, the error of these
estimates (ses) and a table of covarites (X) and than ran a hypothesis
test. intercept is the initial covatiate, in this case fallow, and all
the tests are compared off of this covariate. It’s dumb but the
estimates (the average diversity across all samples of a type) are given
as values compared to the intercept estimate, not their true value. ei
total diversity of replicate is (0.015 + 1.94). Based on this, the
fallowed land does not have a significantly lower diversity than the
replicate land. This is fine. but it only works with one comparison and
we have three (although i think we can only do two using this program.
Hey factorial anovas are hard yo, but also we saw that this factor is
unimportant)
TLL_Location_betta_random <-
betta_random(estimates, ses, X, groups = sample_data_key$Treatment )
TLL_Location_betta_random$table
Estimates Standard Errors p-values
(Intercept) 1.938068477 0.03320415 0.000
TreatmentPosCont 0.015162995 0.08112858 0.852
TreatmentReplicate -0.003309698 0.04229795 0.938
Hey whats betta_random and whys it different from betta/which one
should you use? betta is said to be “A simple plotting interface for
comparing total diversity across samples or a covariate gradient.”
betta_random is “This function extends betta() to permit random effects
modelling.” so what is fixed and random effects? well read: https://web.pdx.edu/~newsomj/mlrclass/ho_randfixd.pdf
but briefly, fixed effects are independent variables that are measured
without error, for example whether a sample was measured in a forest or
a plantation. Random effects are independent variables that are drawn
from a population thus representing a population and therefore random
values around the population mean i.e. this dataset should use a fixed
effect model as it is valid for betta which is a stronger model
statistical than betta_random
Either way lets look at how the the model responds to the layer
information
estimates <- divnet_order_Location_Layer$shannon %>% summary %$% estimate #this is where our shannon data is stored
ses <- sqrt(divnet_order_Location_Layer$`shannon-variance`) #take variance and turns it into standard error
X <- model.matrix(~Layer, data = sample_data_key) #this creates a matrix that the model can use, its the sample sample_data_key but with numbers instead of words, also were we specify that we want to compare layers
TLL_Layer_betta <- betta(estimates, ses, X) # run the command
TLL_Layer_betta$table #whats the p values
Estimates Standard Errors p-values
(Intercept) 1.6292022 0.01168101 0
LayerOrganic 0.5891731 0.01416913 0
This means that the organic layer has a significantly higher
diversity than the mineral layer across all samples its total amount is
(0.58+1.62).
Okay what if we wanted to compare multiple things at once? we know
replicate versus fallow doesn’t matter, so lets just compare location
and layer
estimates <- divnet_order_Location_Layer$shannon %>% summary %$% estimate #this is where our shannon data is stored
ses <- sqrt(divnet_order_Location_Layer$`shannon-variance`) #take variance and turns it into standard error
X <- model.matrix(~Location*Layer, data = sample_data_key)#this is where we specify location and layer
TLL_Layer_Location_betta <- betta(estimates, ses, X, p.digits = 5) # run the command
TLL_Layer_Location_betta$table #whats the p values
Estimates Standard Errors p-values
(Intercept) 1.66939013 0.01090394 0.00000
LocationFallow -0.03562652 0.08321919 0.66857
LocationForest -0.02561327 0.01632864 0.11674
LocationForest PC 0.03488587 0.08862756 0.69386
LocationPlant -0.12318836 0.01623102 0.00000
LocationPositive Control 0.03851131 0.04822450 0.42453
LayerOrganic 0.40429978 0.01303620 0.00000
LocationFallow:LayerOrganic 0.34732128 0.11658520 0.00289
LocationForest:LayerOrganic 0.05674660 0.02143990 0.00813
LocationForest PC:LayerOrganic 0.33099816 0.12595408 0.00859
LocationPlant:LayerOrganic 0.35412853 0.01760813 0.00000
LocationPositive Control:LayerOrganic 0.04586626 0.06933592 0.50829
okay WHAT the fuck does this all mean? well fun fact this is the
worst way to make a p value table known to man. Look at all the names in
the intercept, which ones from your dataset are missing? Edge:mineral.
That means edge:mineral is the intercept. so locationfallow is actually
fallow:mineral, and layerorganic is actually edge:organic and so on.
Estimates work the same way as before AND its still dumb. But regardless
you can* use this table to make comparisons except there are two very
important notes.
Firstly, why is edge:mineral first? its because its alphabetically
first…. great. But we can change this by changing the factor, run the
below commands and then rerun the betta exercise
sample_data_key <- physeq_order %>%
sample_data %>%
tibble::as_tibble() %>%
dplyr::mutate("sample_names" = physeq_order %>% sample_names )
sample_data_key <- sample_data_key %>% mutate(Location = factor(
Location,
levels=c("Forest", "Edge", "Fallow", "Forest PC", "Plant", "Positive Control")
))
#include all the datatypes in your column and order them the way you want the analysis to be conducted (in this example forest is now the intercept)
estimates <- divnet_order_Location_Layer$shannon %>% summary %$% estimate #this is where our shannon data is stored
ses <- sqrt(divnet_order_Location_Layer$`shannon-variance`) #take variance and turns it into standard error
X <- model.matrix(~Location*Layer, data = sample_data_key)#this is where we specify location and layer
TLL_Layer_Location_betta_forest <- betta(estimates, ses, X, p.digits = 5) # run the command
TLL_Layer_Location_betta_forest$table #whats the p values
Estimates Standard Errors p-values
(Intercept) 1.64377686 0.01090394 0.00000
LocationEdge 0.02561327 0.07842806 0.74398
LocationFallow -0.01001325 0.08321919 0.90423
LocationForest PC 0.06049914 0.08862756 0.49485
LocationPlant -0.09757509 0.01623102 0.00000
LocationPositive Control 0.06412458 0.04822450 0.18361
LayerOrganic 0.46104638 0.01303620 0.00000
LocationEdge:LayerOrganic -0.05674660 0.08484083 0.50359
LocationFallow:LayerOrganic 0.29057468 0.11658520 0.01269
LocationForest PC:LayerOrganic 0.27425156 0.12595408 0.02945
LocationPlant:LayerOrganic 0.29738194 0.01760813 0.00000
LocationPositive Control:LayerOrganic -0.01088034 0.06933592 0.87531
Secondly, betta does not account for the family wise error rate. ALSO
great https://github.com/adw96/breakaway/issues/84
so whats there to be done?
ptable <- TLL_Layer_Location_betta$table #start by separating the ptable from the weird betta object
ptable_adj <- ptable %>% #moving it to a new table bc reasons
as.data.frame() %>%
rename(p_values = "p-values") #removing the p-vlaues names because dashes mess stuff up later on
ptable_adj$p_values <- p.adjust(ptable_adj$p_values, method = "bonferroni") #apply whatever pvalue correction you'd like, bonferroni isn't a recommendation, but the first one I remembered off hand
ptable_adj
Sweet! now the p values have been adjusted and are valid as
comparisons! But be very careful. Since the cut off earlier was at five
digits some of these values were reported as zeros. These zeros will of
course pass the bonferroni adjustment! so make sure you have enough
zeros you’re comfortable with the adjusted p-table. PS. the intercept P
value IS actually zero, so don’t worry about that (and technically you
should remove it from this table before running the padjust, bc it isnt
a real comparison)
dont forget to save your p-table!!!
Lets go ahead and fix the zeros and reomve the intercept…
ptable <- TLL_Layer_Location_betta$table #start by separating the ptable from the weird betta object
ptable_adj <- ptable %>% #moving it to a new table bc reasons
as.data.frame() %>%
rename(p_values = "p-values") #removing the p-vlaues names because dashes mess stuff up later on
ptable_adj <- ptable_adj %>% filter(!row_number() %in% c(1))
ptable_adj$p_values <- p.adjust(ptable_adj$p_values, method = "bonferroni") #apply whatever pvalue correction you'd like, bonferroni isn't a recommendation, but the first one I remembered off hand
ptable_adj
yknow what? if there wasnt a value 8 digits under I think it might
just be signifigant…
PS. heres the original issues page where I finally figured out how to
read the intercept tables This makes the table 10x more unreadable and
took me a whole ass day to find out https://github.com/adw96/DivNet/issues/34
LS0tDQp0aXRsZTogIkFscGhhIERpdmVyc2l0eSBFc3RpbWF0aW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCkJ5IEFuZ3VzIEJhbGwNCg0KDQpBbHBoYSBkaXZlcnNpdHkganVzdCBnb3QgQ09NUExJQ0FURUQNCklsbCBiZSBtZXJnaW5nIHRoZXNlIHR3byB0dXRvcmlhbHMsIGFuZCBob25lc3RseSwgdGhleSBhcmUgZ29pbmcgdG8gYmUgYWxvdCBiZXR0ZXIgdGhhbiBJIGNvdWxkIGV2ZXIgYmUgc28gZ28gZ2l2ZSB0aGVtIGEgcnVuIHRocm91Z2ghIEknbSBzdGlsbCBnb25uYSBleHBsYWluIGhlcmUgc28gd2UgaGF2ZSBhIFMuTy5QIGJ1dCB3aGVuIHRoZSBhdXRob3Igb2YgdGhlIGNvZGUgb2ZmZXJzIHRvIGV4cGxhaW4gaG93IHRvIHVzZSBpdCwgZG9uJ3QgdHVybiB5b3VyIG5vc2UNCmh0dHBzOi8vZ2l0aHViLmNvbS9hZHc5Ni9EaXZOZXQvYmxvYi9tYWluL3ZpZ25ldHRlcy9nZXR0aW5nLXN0YXJ0ZWQuUm1kDQpodHRwczovL2Fkdzk2LmdpdGh1Yi5pby9icmVha2F3YXkvYXJ0aWNsZXMvYnJlYWthd2F5Lmh0bWwNCmh0dHBzOi8vZ2l0aHViLmNvbS9hZHc5Ni9icmVha2F3YXkvYmxvYi9tYWluL3ZpZ25ldHRlcy9kaXZlcnNpdHktaHlwb3RoZXNpcy10ZXN0aW5nLlJtZA0KDQpJcnJlZ2FyZGxlc3MgbGV0cyBnbyENCnN0YXJ0IGJ5IHVwbG9hZGluZyB5b3VyIGRhdGEhDQoNCmBgYHtyfQ0KcmVhZHBhdGggPC0gIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXEJpb2luZm9ybWF0aWNzXFxLZW56aWVzIERhdGFcXEtlbnppZXMgZGF0YSBmb3IgbWljaGFlbFxcIiAjYWRkIHlvdXIgZmlsZSBsb2NhdGlvbiBoZXJlDQpwaHlzZXFfS2V5IDwtIHJlYWRSRFMoZmlsZSA9IGZpbGUucGF0aChyZWFkcGF0aCwgInBoeXNlcV9LZXlfYW5ndXNmaXhlZDIucmRzIikpDQpgYGANCg0KVGhlbiB5b3VyIHBhY2thZ2VzDQoNCmBgYHtyLCBlY2hvID0gVCwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoRGl2TmV0KQ0KbGlicmFyeShwaHlsb3NlcSkNCmxpYnJhcnkoYnJlYWthd2F5KQ0KbGlicmFyeShzcGVlZHlzZXEpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeSh0aWJibGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpUaGlzIGFscGhhIGRpdmVyc2l0eSBwcm9ncmFtIGFzc3VtZXMgc2FtcGxlcyBhcmUgZnJvbSB0aGUgc2FtZSBwb3B1bGF0aW9uIG9mIHNwZWNpZXMgKGFzIHRoZXkgb2Z0ZW4gYXJlISkgdG8gZXN0aW1hdGUgdGhlIHRydWUgYWJ1bmRhbmNlIG9mIHNwZWNpZXMgYW5kIGhhdmUgYW4gYWx0b2dldGhlciBtb3JlIGFjY3VyYXRlIGFscGhhIGRpdmVyc2l0eSBtZXRyaWMuIEZpcnN0IHRoaW5ncyBmaXJzdCwgdGhpcyBpcyBhIHRheGluZyBwcm9ncmFtIHRvIHJ1biBzbyB3ZSBhcmUgZ29pbmcgdG8gbG9vayBhdCB0aGUgb3JkZXIgbGV2ZWwsIHlvdSBjYW4gY2hhbmdlIHRoaXMganVzdCBhcyBlYXN5Lg0KDQpgYGB7cn0NCnBoeXNlcV9vcmRlciA8LSB0YXhfZ2xvbShwaHlzZXFfS2V5LCB0YXhyYW5rPSJPcmRlciIpICNjb25nbG9tZXJhdGVzIGV2ZXJ5dGhpbmcgdG8gYW4gb3JkZXIgbGV2ZWwNCmBgYA0KDQoNCg0KYGBge3J9DQpwaHlzZXFfb3JkZXIgJT4lIHNhbXBsZV92YXJpYWJsZXMoKQ0KDQpgYGANCg0KDQpXZSBqdXN0IGNvbWJpbmVkIGFsbCB0aGUgdGF4b25vbXkgc28gdGhlIGxvd2VzdCBsZXZlbCBvZiBkZXB0aCBpcyBhdCBhbiBvcmRlciBsZXZlbA0KTm93IGxldHMgcnVuIHRoZSBzdGF0cyEgDQoNCmBgYHtyfQ0KI291cHV0b2JqZWN0IDwtIHBoeXNlcV9vYmplY3QgJT4lIGRpdm5ldCh0dW5pbmcgPSBmYXN0IG9yIGNhcmVmdWwsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXVsYSA9IH4gY292YXJpYXRlICsgY292YXJpYXRlKQ0Kc2V0LnNlZWQoNjQ1ODc0MCkNCg0KZGl2bmV0X29yZGVyIDwtIHBoeXNlcV9vcmRlciAlPiUgZGl2bmV0KHR1bmluZyA9ICJjYXJlZnVsIikNCmRpdm5ldF9vcmRlcl9UcmVhdG1lbnQgPC0gcGh5c2VxX29yZGVyICU+JSBkaXZuZXQodHVuaW5nID0gImNhcmVmdWwiLCBmb3JtdWxhID0gfiBUcmVhdG1lbnQpDQpkaXZuZXRfb3JkZXJfTG9jYXRpb24gPC0gcGh5c2VxX29yZGVyICU+JSBkaXZuZXQodHVuaW5nID0gImNhcmVmdWwiLCBmb3JtdWxhID0gfiBMb2NhdGlvbikNCmRpdm5ldF9vcmRlcl9UcmVhdG1lbnRfTG9jYXRpb24gPC0gcGh5c2VxX29yZGVyICU+JSBkaXZuZXQodHVuaW5nID0gImNhcmVmdWwiLCBmb3JtdWxhID0gfiBUcmVhdG1lbnQgKyBMb2NhdGlvbikNCmRpdm5ldF9vcmRlcl9UcmVhdG1lbnRfTG9jYXRpb25fTGF5ZXIgPC0gcGh5c2VxX29yZGVyICU+JSBkaXZuZXQodHVuaW5nID0gImNhcmVmdWwiLCBmb3JtdWxhID0gfiBUcmVhdG1lbnQgKyBMb2NhdGlvbiArIExheWVyKSAjb25seSBydW5uaW5nIHRoaXMgb25lIGJlY2F1c2UgaXRzIHRoZSBvbmx5IG9uZSB3ZSB0aGluayBtYXR0ZXJzDQpkaXZuZXRfb3JkZXJfTG9jYXRpb25fTGF5ZXIgPC0gcGh5c2VxX29yZGVyICU+JSBkaXZuZXQodHVuaW5nID0gImNhcmVmdWwiLCBmb3JtdWxhID0gfiAgTG9jYXRpb24gKyBMYXllcikNCmBgYA0KT2theSBJIGp1c3QgZGlkIGFsb3Qgb2YgdGhpbmdzLCBJIGdldCB0aGF0LCBGaXJzdCB0aGluZ3MgZmlyc3QuIHdlIGFyZSB0YWtpbmcgb3VyIHBoeXNlcSBvYmplY3QgYW5kIGFwcGx5aW5nICglPiUpIHRoZSBkaXZuZXQgY29tbWFuZCB0byBpdC4gV2UgYXJlIGFwcGx5IHRoaXMgY29kZSBhdCBhICJjYXJlZnVsIiBsZXZlbC4gaS5lLiB0aGlzIG1lYW5zIG1vcmUgY29tcHV0aW5nIHBvd2VyIGlzIGdvaW5nIGludG8gdGhlIGVxdWF0aW9uIGFuZCB5b3UnbGwgZ2V0IGEgbW9yZSBhY2N1cmF0ZSByZXByZXNlbnRhdGlvbiBvZiB0aGUgc3RhdHMgYmVpbmcgZG9uZSAoeW91IGNhbiBhbHNvIGRvIHRoaXMgImZhc3QiIGJ1dCB1aGggZG9udD8pLiBuZXh0IGlzIHRoaXMgZm9ybXVsYSBzdHVmZi4gV2l0aGluIGRhdGEgc2V0cyB3ZSBoYXZlIGNvdmFyaWF0ZXMgKGdvIHNlYXJjaCB1cCBhbmQgcmVhZCB3aGF0IGEgY292YXJpYXRlIGlzIG5vdyksIGFuZCB3aGVuIG1pY3JvYmlhbCBjb21tdW5pdGllcyBjb3Zhcnkgd2l0aCBzYXkgYSBsb2NhdGlvbiBlYWNoIHNhbXBsZSByZWZsZWN0cyBhIHBhcnQgb2YgdGhlIHBvcHVsYXRpb24gdGhhdCBjYW4gYmUgImNvbWJpbmVkIiBzdGF0aXN0aWNhbGx5IHNvIHRoYXQgd2UgY2FuIGVzdGltYXRlIHRoZSB0b3RhbCBwb3B1bGF0aW9uIGRpdmVyc2l0eS4gSSdtIHByZXR0eSBzdXJlIHRoZSBjb3ZhcmlhdGVzIHdpdGhpbiB0aGlzIGRhdGFzZXQgYXJlIHRyZWF0bWVudCBBTkQgbG9jYXRpb24gQU5EIExheWVyOyBob3dldmVyLCB3ZSdyZSBnb25uYSBydW4gYWxsIG9mIHRoZW0ganVzdCB0byBzZWUgd2hhdCBoYXBwZW5zISBmdW4gcmlnaHQ/IA0KDQpQUy4gSSBzZXQgYSBzZWVkIGJlY2F1c2UgSSdsbCBsaWtlbHkgYmUgcmVydW5uaW5nIGNvZGUgdG8gbWFrZSBzdXJlIGl0IHdvcmtzLCBhbmQgSSBkb250IHdhbnQgdG8gZ28gYXJvdW5kIGNoYW5naW5nIG15IGNvbmNsdXNpb25zIQ0KUFMyLiBUaGlzIGNhbiB0YWtlIGEgaGFsZiBzZWNvbmQgdG8gcnVuDQoNCg0KVGhlc2UgdGFrZSBGSVZFLUVWRVIgdG8gcnVuIHNvIEltbWEgbWFrZSBhbmQgc2F2ZSBjb3BpZXMgb2YgdGhlIG9iamVjdHMNCmBgYHtyfQ0KZGl2bmV0ZmlsZXMgPC0gIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXEJpb2luZm9ybWF0aWNzXFxLZW56aWVzIERhdGFcXG9sZCBzdHVmZlxcIg0Kc2F2ZVJEUyhkaXZuZXRfb3JkZXIsIGZpbGUgPSBmaWxlLnBhdGgoZGl2bmV0ZmlsZXMsICJkaXZuZXRfb3JkZXIucmRzIikpDQpzYXZlUkRTKGRpdm5ldF9vcmRlcl9Mb2NhdGlvbiwgZmlsZSA9IGZpbGUucGF0aChkaXZuZXRmaWxlcywgImRpdm5ldF9vcmRlcl9Mb2NhdGlvbi5yZHMiKSkNCnNhdmVSRFMoZGl2bmV0X29yZGVyX1RyZWF0bWVudCwgZmlsZSA9IGZpbGUucGF0aChkaXZuZXRmaWxlcywgICJkaXZuZXRfb3JkZXJfVHJlYXRtZW50LnJkcyIpKQ0Kc2F2ZVJEUyhkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uLCBmaWxlID0gZmlsZS5wYXRoKGRpdm5ldGZpbGVzLCJkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uLnJkcyIpKQ0Kc2F2ZVJEUyhkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uX0xheWVyLCBmaWxlID0gZmlsZS5wYXRoKGRpdm5ldGZpbGVzLCJkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uX0xheWVyLnJkcyIpKQ0Kc2F2ZVJEUyhkaXZuZXRfb3JkZXJfTG9jYXRpb25fTGF5ZXIsIGZpbGUgPSBmaWxlLnBhdGgoZGl2bmV0ZmlsZXMsImRpdm5ldF9vcmRlcl9Mb2NhdGlvbl9MYXllci5yZHMiKSkNCg0KZGl2bmV0X29yZGVyX0xvY2F0aW9uX0xheWVyIDwtIHJlYWRSRFMoZmlsZSA9IGZpbGUucGF0aChkaXZuZXRmaWxlcywiZGl2bmV0X29yZGVyX0xvY2F0aW9uX0xheWVyLnJkcyIpKQ0KZGl2bmV0X29yZGVyX1RyZWF0bWVudF9Mb2NhdGlvbiA8LSByZWFkUkRTKGZpbGUgPSBmaWxlLnBhdGgoZGl2bmV0ZmlsZXMsICJkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uLnJkcyIpKQ0KZGl2bmV0X29yZGVyX1RyZWF0bWVudCA8LSByZWFkUkRTKGZpbGUgPSBmaWxlLnBhdGgoZGl2bmV0ZmlsZXMsImRpdm5ldF9vcmRlcl9UcmVhdG1lbnQucmRzIikpDQpkaXZuZXRfb3JkZXJfTG9jYXRpb24gPC0gcmVhZFJEUyhmaWxlID0gZmlsZS5wYXRoKGRpdm5ldGZpbGVzLCJkaXZuZXRfb3JkZXJfTG9jYXRpb24ucmRzIikpDQpkaXZuZXRfb3JkZXIgPC0gcmVhZFJEUyhmaWxlID0gZmlsZS5wYXRoKGRpdm5ldGZpbGVzLCJkaXZuZXRfb3JkZXIucmRzIikpDQpkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uX0xheWVyIDwtIHJlYWRSRFMoZmlsZSA9IGZpbGUucGF0aChkaXZuZXRmaWxlcywiZGl2bmV0X29yZGVyX1RyZWF0bWVudF9Mb2NhdGlvbl9MYXllci5yZHMiKSkNCmBgYA0KDQoNCg0KTm93IGxldHMgcGxvdCBpdCBvdXQhDQpUaGlzIGlzIGEgcGxvdCBvZiB0aGUgcmF3IGRpdmVyc2l0eSBlc3RpbWF0ZXMgZm9yIGVhY2ggc2FtcGxlIHdpdGggbm8gY292YXJpYXRlIGluZm9ybWF0aW9uIHNoYXJlZCBhY3Jvc3Mgc2FtcGxlcy4NCmBgYHtyfQ0KZGl2bmV0X29yZGVyJHNoYW5ub24gJT4lIA0KICBwbG90KHBoeXNlcV9vcmRlcikNCmBgYA0KbG9vayBhdCB0aGF0IGdyYXBoIHRoYXQgbWVhbnMgYWJzb2x1dGVseSBub3RoaW5nIHRvIG1lISBvaCB5ZWFoDQoNCk5vdyBsZXRzIGFkZCBzb21lIGNvdmFyaWF0ZSBpbmZvLCB0cmVhdG1lbnRzDQpgYGB7cn0NCmRpdm5ldF9vcmRlcl9UcmVhdG1lbnQkc2hhbm5vbiAlPiUgDQogIHBsb3QocGh5c2VxX29yZGVyLCBjb2xvciA9ICJUcmVhdG1lbnQiKQ0KYGBgDQpva2F5IHNvbWV0aGluZyBtb3JlLCBob3cgYWJvdXQgbG9jYXRpb24gY292YXJpYXRlIGluZm8gaW5zdGVhZD8NCmBgYHtyfQ0KZGl2bmV0X29yZGVyX0xvY2F0aW9uJHNoYW5ub24gJT4lIA0KICBwbG90KHBoeXNlcV9vcmRlciwgY29sb3IgPSAiTG9jYXRpb24iKQ0KYGBgDQpva2F5Li4ub2theSwgYWRkaW5nIHRoZW0gdG9nZXRoZXINCg0KYGBge3J9DQpkaXZuZXRfb3JkZXJfVHJlYXRtZW50X0xvY2F0aW9uJHNoYW5ub24gJT4lIA0KICBwbG90KHBoeXNlcV9vcmRlciwgY29sb3IgPSAiTG9jYXRpb24iLCBzaGFwZSA9ICJUcmVhdG1lbnQiKQ0KYGBgDQoNCmFuZCBmaW5hbGx5IHdpdGggYWxsIG9mIHRoZSBjb3ZhcmlhdGUgaW5mb3JtYXRpb24gDQpgYGB7cn0NCmRpdm5ldF9vcmRlcl9UcmVhdG1lbnRfTG9jYXRpb25fTGF5ZXIkc2hhbm5vbiAlPiUgDQogIHBsb3QocGh5c2VxX29yZGVyLCBjb2xvciA9ICJMb2NhdGlvbiIsIHNoYXBlID0gIkxheWVyIikNCmBgYA0KQnV0IHBlcmhhcHMgdGhpcyBpcyB0b28gbXVjaCBjb3ZhcmlhdGUgaW5mb3JtYXRpb24gZG9lcyBmYWxsb3cgdmVyc3VzIHJlcGxpY2F0ZSByZWFsbHkgcmVmbGVjdCB1bnFpdWUgYW5kIGRpZmZlcmVudCBjb3ZhcmlhdGVzPyBwZXJoYXBzIG5vLiBUaGlzIGlubGllcyBhIHZlcnkgaW1wb3J0YW50IHBhcnQgb2YgdGhpcyBraW5kIG9mIGFuYWx5c2lzLiBTaW5jZSB0aGUgYW5zd2VycyB5b3UgZ2V0IGFyZSBwYXJ0aWFsbHkgYmFzZWQgb24gdGhlIGNvdmFyaWF0ZXMgeW91IGNob29zZSwgeW91IG5lZWQgdG8gYmUgZGFtbmVkIHN1cmUgeW91IGNob29zZSB0aGUgY29ycmVjdCBvbmVzLg0KDQpgYGB7cn0NCmRpdm5ldF9vcmRlcl9Mb2NhdGlvbl9MYXllciRzaGFubm9uICU+JSANCiAgcGxvdChwaHlzZXFfb3JkZXIsIGNvbG9yID0gIkxvY2F0aW9uIiwgc2hhcGUgPSAiTGF5ZXIiKQ0KYGBgDQpCYXNlZCBvbiB0aGF0IG91ciBhbmFseXNpcyB3ZSBiZSBvbiBkYXRhIHRoYXQgaW5jbHVkZXMgbG9jYXRpb24gYW5kIGxheWVyIGNvdmFyaWF0ZXMNCg0KDQoNCnRoaXMgY3JlYXRlcyBhbiBvYmplY3QgdGhhdHMgcHJldHR5IG11Y2ggdGhlIGtleSBmcm9tIHBoeWxvc2VxIGNyZWF0aW9uLCB3ZSBuZWVkIGl0IHRvIGhvbGQgb24gdG8gbWV0YWRhdGENCg0KYGBge3J9DQpzYW1wbGVfZGF0YV9rZXkgPC0gcGh5c2VxX29yZGVyICU+JQ0KICBzYW1wbGVfZGF0YSAlPiUNCiAgdGliYmxlOjphc190aWJibGUoKSAlPiUNCiAgZHBseXI6Om11dGF0ZSgic2FtcGxlX25hbWVzIiA9IHBoeXNlcV9vcmRlciAlPiUgc2FtcGxlX25hbWVzICkNCmBgYA0KDQoNCmxldHMgcnVuIHRoZSB0ZXN0ISwgd2UgbmVlZCB0byBtYWtlIHRocmVlIG9iamVjdHMgdG8gdXNlIHRoaXMgZnVuY3Rpb24sIHRoZXlyZSBqdXN0IG1vcmUgb2JqZWN0cyB0aGF0IGJldHRhIGV4cGVjdHMgYW5kIHdlIGp1c3QgbmVlZCB0byBiZW5kIHRvIGl0cyB3aWxsDQpgYGB7cn0NCg0KZXN0aW1hdGVzIDwtIGRpdm5ldF9vcmRlcl9Mb2NhdGlvbl9MYXllciRzaGFubm9uICU+JSBzdW1tYXJ5ICUkJSBlc3RpbWF0ZSAjdGhpcyBpcyB3aGVyZSBvdXIgc2hhbm5vbiBkYXRhIGlzIHN0b3JlZA0Kc2VzIDwtIHNxcnQoZGl2bmV0X29yZGVyX0xvY2F0aW9uX0xheWVyJGBzaGFubm9uLXZhcmlhbmNlYCkgI3Rha2UgdmFyaWFuY2UgYW5kIHR1cm5zIGl0IGludG8gc3RhbmRhcmQgZXJyb3INClggPC0gbW9kZWwubWF0cml4KH5UcmVhdG1lbnQsIGRhdGEgPSBzYW1wbGVfZGF0YV9rZXkpICN0aGlzIGNyZWF0ZXMgYSBtYXRyaXggdGhhdCB0aGUgbW9kZWwgY2FuIHVzZSwgaXRzIHRoZSBzYW1wbGUgc2FtcGxlX2RhdGFfa2V5IGJ1dCB3aXRoIG51bWJlcnMgaW5zdGVhZCBvZiB3b3JkcywgYWxzbyB3ZXJlIHdlIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIGNvbXBhcmUgdHJlYXRtZW50DQpUTExfVHJlYXRtZW50X2JldHRhIDwtIGJldHRhKGVzdGltYXRlcywgc2VzLCBYKSAjIHJ1biB0aGUgY29tbWFuZA0KVExMX1RyZWF0bWVudF9iZXR0YSR0YWJsZSAjd2hhdHMgdGhlIHAgdmFsdWVzDQpgYGANCg0KSGVyZSB3ZSBpbXB1dGVkIHRoZSBlc3RpbWF0ZXMgb2YgZGl2ZXJzaXR5LCB0aGUgZXJyb3Igb2YgdGhlc2UgZXN0aW1hdGVzIChzZXMpIGFuZCBhIHRhYmxlIG9mIGNvdmFyaXRlcyAoWCkgYW5kIHRoYW4gcmFuIGEgaHlwb3RoZXNpcyB0ZXN0LiBpbnRlcmNlcHQgaXMgdGhlIGluaXRpYWwgY292YXRpYXRlLCBpbiB0aGlzIGNhc2UgZmFsbG93LCBhbmQgYWxsIHRoZSB0ZXN0cyBhcmUgY29tcGFyZWQgb2ZmIG9mIHRoaXMgY292YXJpYXRlLiBJdCdzIGR1bWIgYnV0IHRoZSBlc3RpbWF0ZXMgKHRoZSBhdmVyYWdlIGRpdmVyc2l0eSBhY3Jvc3MgYWxsIHNhbXBsZXMgb2YgYSB0eXBlKSBhcmUgZ2l2ZW4gYXMgdmFsdWVzIGNvbXBhcmVkIHRvIHRoZSBpbnRlcmNlcHQgZXN0aW1hdGUsIG5vdCB0aGVpciB0cnVlIHZhbHVlLiBlaSB0b3RhbCBkaXZlcnNpdHkgb2YgcmVwbGljYXRlIGlzICgwLjAxNSArIDEuOTQpLiBCYXNlZCBvbiB0aGlzLCB0aGUgZmFsbG93ZWQgbGFuZCBkb2VzIG5vdCBoYXZlIGEgc2lnbmlmaWNhbnRseSBsb3dlciBkaXZlcnNpdHkgdGhhbiB0aGUgcmVwbGljYXRlIGxhbmQuIFRoaXMgaXMgZmluZS4gYnV0IGl0IG9ubHkgd29ya3Mgd2l0aCBvbmUgY29tcGFyaXNvbiBhbmQgd2UgaGF2ZSB0aHJlZSAoYWx0aG91Z2ggaSB0aGluayB3ZSBjYW4gb25seSBkbyB0d28gdXNpbmcgdGhpcyBwcm9ncmFtLiBIZXkgZmFjdG9yaWFsIGFub3ZhcyBhcmUgaGFyZCB5bywgYnV0IGFsc28gd2Ugc2F3IHRoYXQgdGhpcyBmYWN0b3IgaXMgdW5pbXBvcnRhbnQpDQoNCg0KDQpgYGB7cn0NClRMTF9Mb2NhdGlvbl9iZXR0YV9yYW5kb20gPC0NCiAgICBiZXR0YV9yYW5kb20oZXN0aW1hdGVzLCBzZXMsIFgsIGdyb3VwcyA9IHNhbXBsZV9kYXRhX2tleSRUcmVhdG1lbnQgKQ0KVExMX0xvY2F0aW9uX2JldHRhX3JhbmRvbSR0YWJsZQ0KYGBgDQpIZXkgd2hhdHMgYmV0dGFfcmFuZG9tIGFuZCB3aHlzIGl0IGRpZmZlcmVudCBmcm9tIGJldHRhL3doaWNoIG9uZSBzaG91bGQgeW91IHVzZT8NCmJldHRhIGlzIHNhaWQgdG8gYmUgIkEgc2ltcGxlIHBsb3R0aW5nIGludGVyZmFjZSBmb3IgY29tcGFyaW5nIHRvdGFsIGRpdmVyc2l0eSBhY3Jvc3Mgc2FtcGxlcyBvciBhIGNvdmFyaWF0ZSBncmFkaWVudC4iDQpiZXR0YV9yYW5kb20gaXMgIlRoaXMgZnVuY3Rpb24gZXh0ZW5kcyBiZXR0YSgpIHRvIHBlcm1pdCByYW5kb20gZWZmZWN0cyBtb2RlbGxpbmcuIg0Kc28gd2hhdCBpcyBmaXhlZCBhbmQgcmFuZG9tIGVmZmVjdHM/IHdlbGwgcmVhZDogaHR0cHM6Ly93ZWIucGR4LmVkdS9+bmV3c29tai9tbHJjbGFzcy9ob19yYW5kZml4ZC5wZGYNCmJ1dCBicmllZmx5LCBmaXhlZCBlZmZlY3RzIGFyZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgdGhhdCBhcmUgbWVhc3VyZWQgd2l0aG91dCBlcnJvciwgZm9yIGV4YW1wbGUgd2hldGhlciBhIHNhbXBsZSB3YXMgbWVhc3VyZWQgaW4gYSBmb3Jlc3Qgb3IgYSBwbGFudGF0aW9uLiBSYW5kb20gZWZmZWN0cyBhcmUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHRoYXQgYXJlIGRyYXduIGZyb20gYSBwb3B1bGF0aW9uIHRodXMgcmVwcmVzZW50aW5nIGEgcG9wdWxhdGlvbiBhbmQgdGhlcmVmb3JlIHJhbmRvbSB2YWx1ZXMgYXJvdW5kIHRoZSBwb3B1bGF0aW9uIG1lYW4NCmkuZS4gdGhpcyBkYXRhc2V0IHNob3VsZCB1c2UgYSBmaXhlZCBlZmZlY3QgbW9kZWwgYXMgaXQgaXMgdmFsaWQgZm9yIGJldHRhIHdoaWNoIGlzIGEgc3Ryb25nZXIgbW9kZWwgc3RhdGlzdGljYWwgdGhhbiBiZXR0YV9yYW5kb20gDQoNCg0KRWl0aGVyIHdheSBsZXRzIGxvb2sgYXQgaG93IHRoZSB0aGUgbW9kZWwgcmVzcG9uZHMgdG8gdGhlIGxheWVyIGluZm9ybWF0aW9uDQpgYGB7cn0NCg0KZXN0aW1hdGVzIDwtIGRpdm5ldF9vcmRlcl9Mb2NhdGlvbl9MYXllciRzaGFubm9uICU+JSBzdW1tYXJ5ICUkJSBlc3RpbWF0ZSAjdGhpcyBpcyB3aGVyZSBvdXIgc2hhbm5vbiBkYXRhIGlzIHN0b3JlZA0Kc2VzIDwtIHNxcnQoZGl2bmV0X29yZGVyX0xvY2F0aW9uX0xheWVyJGBzaGFubm9uLXZhcmlhbmNlYCkgI3Rha2UgdmFyaWFuY2UgYW5kIHR1cm5zIGl0IGludG8gc3RhbmRhcmQgZXJyb3INClggPC0gbW9kZWwubWF0cml4KH5MYXllciwgZGF0YSA9IHNhbXBsZV9kYXRhX2tleSkgI3RoaXMgY3JlYXRlcyBhIG1hdHJpeCB0aGF0IHRoZSBtb2RlbCBjYW4gdXNlLCBpdHMgdGhlIHNhbXBsZSBzYW1wbGVfZGF0YV9rZXkgYnV0IHdpdGggbnVtYmVycyBpbnN0ZWFkIG9mIHdvcmRzLCBhbHNvIHdlcmUgd2Ugc3BlY2lmeSB0aGF0IHdlIHdhbnQgdG8gY29tcGFyZSBsYXllcnMNClRMTF9MYXllcl9iZXR0YSA8LSBiZXR0YShlc3RpbWF0ZXMsIHNlcywgWCkgIyBydW4gdGhlIGNvbW1hbmQNClRMTF9MYXllcl9iZXR0YSR0YWJsZSAjd2hhdHMgdGhlIHAgdmFsdWVzDQpgYGANClRoaXMgbWVhbnMgdGhhdCB0aGUgb3JnYW5pYyBsYXllciBoYXMgYSBzaWduaWZpY2FudGx5IGhpZ2hlciBkaXZlcnNpdHkgdGhhbiB0aGUgbWluZXJhbCBsYXllciBhY3Jvc3MgYWxsIHNhbXBsZXMgaXRzIHRvdGFsIGFtb3VudCBpcyAoMC41OCsxLjYyKS4NCg0KT2theSB3aGF0IGlmIHdlIHdhbnRlZCB0byBjb21wYXJlIG11bHRpcGxlIHRoaW5ncyBhdCBvbmNlPyB3ZSBrbm93IHJlcGxpY2F0ZSB2ZXJzdXMgZmFsbG93IGRvZXNuJ3QgbWF0dGVyLCBzbyBsZXRzIGp1c3QgY29tcGFyZSBsb2NhdGlvbiBhbmQgbGF5ZXINCg0KYGBge3J9DQoNCmVzdGltYXRlcyA8LSBkaXZuZXRfb3JkZXJfTG9jYXRpb25fTGF5ZXIkc2hhbm5vbiAlPiUgc3VtbWFyeSAlJCUgZXN0aW1hdGUgI3RoaXMgaXMgd2hlcmUgb3VyIHNoYW5ub24gZGF0YSBpcyBzdG9yZWQNCnNlcyA8LSBzcXJ0KGRpdm5ldF9vcmRlcl9Mb2NhdGlvbl9MYXllciRgc2hhbm5vbi12YXJpYW5jZWApICN0YWtlIHZhcmlhbmNlIGFuZCB0dXJucyBpdCBpbnRvIHN0YW5kYXJkIGVycm9yDQoNClggPC0gbW9kZWwubWF0cml4KH5Mb2NhdGlvbipMYXllciwgZGF0YSA9IHNhbXBsZV9kYXRhX2tleSkjdGhpcyBpcyB3aGVyZSB3ZSBzcGVjaWZ5IGxvY2F0aW9uIGFuZCBsYXllcg0KDQoNCg0KVExMX0xheWVyX0xvY2F0aW9uX2JldHRhIDwtIGJldHRhKGVzdGltYXRlcywgc2VzLCBYLCBwLmRpZ2l0cyA9IDUpICMgcnVuIHRoZSBjb21tYW5kDQpUTExfTGF5ZXJfTG9jYXRpb25fYmV0dGEkdGFibGUgI3doYXRzIHRoZSBwIHZhbHVlcw0KYGBgDQpva2F5IFdIQVQgdGhlIGZ1Y2sgZG9lcyB0aGlzIGFsbCBtZWFuPyB3ZWxsIGZ1biBmYWN0IHRoaXMgaXMgdGhlIHdvcnN0IHdheSB0byBtYWtlIGEgcCB2YWx1ZSB0YWJsZSBrbm93biB0byBtYW4uIExvb2sgYXQgYWxsIHRoZSBuYW1lcyBpbiB0aGUgaW50ZXJjZXB0LCB3aGljaCBvbmVzIGZyb20geW91ciBkYXRhc2V0IGFyZSBtaXNzaW5nPyBFZGdlOm1pbmVyYWwuIFRoYXQgbWVhbnMgZWRnZTptaW5lcmFsIGlzIHRoZSBpbnRlcmNlcHQuIHNvIGxvY2F0aW9uZmFsbG93IGlzIGFjdHVhbGx5IGZhbGxvdzptaW5lcmFsLCBhbmQgbGF5ZXJvcmdhbmljIGlzIGFjdHVhbGx5IGVkZ2U6b3JnYW5pYyBhbmQgc28gb24uIEVzdGltYXRlcyB3b3JrIHRoZSBzYW1lIHdheSBhcyBiZWZvcmUgQU5EIGl0cyBzdGlsbCBkdW1iLiBCdXQgcmVnYXJkbGVzcyB5b3UgY2FuKiB1c2UgdGhpcyB0YWJsZSB0byBtYWtlIGNvbXBhcmlzb25zIGV4Y2VwdCB0aGVyZSBhcmUgdHdvIHZlcnkgaW1wb3J0YW50IG5vdGVzLg0KDQpGaXJzdGx5LCB3aHkgaXMgZWRnZTptaW5lcmFsIGZpcnN0PyBpdHMgYmVjYXVzZSBpdHMgYWxwaGFiZXRpY2FsbHkgZmlyc3QuLi4uIGdyZWF0LiBCdXQgd2UgY2FuIGNoYW5nZSB0aGlzIGJ5IGNoYW5naW5nIHRoZSBmYWN0b3IsIHJ1biB0aGUgYmVsb3cgY29tbWFuZHMgYW5kIHRoZW4gcmVydW4gdGhlIGJldHRhIGV4ZXJjaXNlIA0KDQpgYGB7cn0NCnNhbXBsZV9kYXRhX2tleSA8LSBwaHlzZXFfb3JkZXIgJT4lDQogIHNhbXBsZV9kYXRhICU+JQ0KICB0aWJibGU6OmFzX3RpYmJsZSgpICU+JQ0KICBkcGx5cjo6bXV0YXRlKCJzYW1wbGVfbmFtZXMiID0gcGh5c2VxX29yZGVyICU+JSBzYW1wbGVfbmFtZXMgKQ0KDQpzYW1wbGVfZGF0YV9rZXkgPC0gc2FtcGxlX2RhdGFfa2V5ICU+JSBtdXRhdGUoTG9jYXRpb24gPSBmYWN0b3IoDQogIExvY2F0aW9uLA0KICBsZXZlbHM9YygiRm9yZXN0IiwgIkVkZ2UiLCAiRmFsbG93IiwgIkZvcmVzdCBQQyIsICJQbGFudCIsICJQb3NpdGl2ZSBDb250cm9sIikgDQopKQ0KI2luY2x1ZGUgYWxsIHRoZSBkYXRhdHlwZXMgaW4geW91ciBjb2x1bW4gYW5kIG9yZGVyIHRoZW0gdGhlIHdheSB5b3Ugd2FudCB0aGUgYW5hbHlzaXMgdG8gYmUgY29uZHVjdGVkIChpbiB0aGlzIGV4YW1wbGUgZm9yZXN0IGlzIG5vdyB0aGUgaW50ZXJjZXB0KQ0KDQplc3RpbWF0ZXMgPC0gZGl2bmV0X29yZGVyX0xvY2F0aW9uX0xheWVyJHNoYW5ub24gJT4lIHN1bW1hcnkgJSQlIGVzdGltYXRlICN0aGlzIGlzIHdoZXJlIG91ciBzaGFubm9uIGRhdGEgaXMgc3RvcmVkDQpzZXMgPC0gc3FydChkaXZuZXRfb3JkZXJfTG9jYXRpb25fTGF5ZXIkYHNoYW5ub24tdmFyaWFuY2VgKSAjdGFrZSB2YXJpYW5jZSBhbmQgdHVybnMgaXQgaW50byBzdGFuZGFyZCBlcnJvcg0KDQpYIDwtIG1vZGVsLm1hdHJpeCh+TG9jYXRpb24qTGF5ZXIsIGRhdGEgPSBzYW1wbGVfZGF0YV9rZXkpI3RoaXMgaXMgd2hlcmUgd2Ugc3BlY2lmeSBsb2NhdGlvbiBhbmQgbGF5ZXINCg0KDQoNClRMTF9MYXllcl9Mb2NhdGlvbl9iZXR0YV9mb3Jlc3QgPC0gYmV0dGEoZXN0aW1hdGVzLCBzZXMsIFgsIHAuZGlnaXRzID0gNSkgIyBydW4gdGhlIGNvbW1hbmQNClRMTF9MYXllcl9Mb2NhdGlvbl9iZXR0YV9mb3Jlc3QkdGFibGUgI3doYXRzIHRoZSBwIHZhbHVlcw0KYGBgDQoNCg0KU2Vjb25kbHksIGJldHRhIGRvZXMgbm90IGFjY291bnQgZm9yIHRoZSBmYW1pbHkgd2lzZSBlcnJvciByYXRlLiBBTFNPIGdyZWF0DQpodHRwczovL2dpdGh1Yi5jb20vYWR3OTYvYnJlYWthd2F5L2lzc3Vlcy84NA0KDQpzbyB3aGF0cyB0aGVyZSB0byBiZSBkb25lPw0KDQpgYGB7cn0NCnB0YWJsZSA8LSBUTExfTGF5ZXJfTG9jYXRpb25fYmV0dGEkdGFibGUgI3N0YXJ0IGJ5IHNlcGFyYXRpbmcgdGhlIHB0YWJsZSBmcm9tIHRoZSB3ZWlyZCBiZXR0YSBvYmplY3QNCnB0YWJsZV9hZGogPC0gcHRhYmxlICU+JSAjbW92aW5nIGl0IHRvIGEgbmV3IHRhYmxlIGJjIHJlYXNvbnMNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICByZW5hbWUocF92YWx1ZXMgPSAicC12YWx1ZXMiKSAjcmVtb3ZpbmcgdGhlIHAtdmxhdWVzIG5hbWVzIGJlY2F1c2UgZGFzaGVzIG1lc3Mgc3R1ZmYgdXAgbGF0ZXIgb24NCnB0YWJsZV9hZGokcF92YWx1ZXMgPC0gcC5hZGp1c3QocHRhYmxlX2FkaiRwX3ZhbHVlcywgbWV0aG9kID0gImJvbmZlcnJvbmkiKSAjYXBwbHkgd2hhdGV2ZXIgcHZhbHVlIGNvcnJlY3Rpb24geW91J2QgbGlrZSwgYm9uZmVycm9uaSBpc24ndCBhIHJlY29tbWVuZGF0aW9uLCBidXQgdGhlIGZpcnN0IG9uZSBJIHJlbWVtYmVyZWQgb2ZmIGhhbmQNCnB0YWJsZV9hZGoNCmBgYA0KU3dlZXQhIG5vdyB0aGUgcCB2YWx1ZXMgaGF2ZSBiZWVuIGFkanVzdGVkIGFuZCBhcmUgdmFsaWQgYXMgY29tcGFyaXNvbnMhIEJ1dCBiZSB2ZXJ5IGNhcmVmdWwuIFNpbmNlIHRoZSBjdXQgb2ZmIGVhcmxpZXIgd2FzIGF0IGZpdmUgZGlnaXRzIHNvbWUgb2YgdGhlc2UgdmFsdWVzIHdlcmUgcmVwb3J0ZWQgYXMgemVyb3MuIFRoZXNlIHplcm9zIHdpbGwgb2YgY291cnNlIHBhc3MgdGhlIGJvbmZlcnJvbmkgYWRqdXN0bWVudCEgc28gbWFrZSBzdXJlIHlvdSBoYXZlIGVub3VnaCB6ZXJvcyB5b3UncmUgY29tZm9ydGFibGUgd2l0aCB0aGUgYWRqdXN0ZWQgcC10YWJsZS4gUFMuIHRoZSBpbnRlcmNlcHQgUCB2YWx1ZSBJUyBhY3R1YWxseSB6ZXJvLCBzbyBkb24ndCB3b3JyeSBhYm91dCB0aGF0IChhbmQgdGVjaG5pY2FsbHkgeW91IHNob3VsZCByZW1vdmUgaXQgZnJvbSB0aGlzIHRhYmxlIGJlZm9yZSBydW5uaW5nIHRoZSBwYWRqdXN0LCBiYyBpdCBpc250IGEgcmVhbCBjb21wYXJpc29uKQ0KDQpkb250IGZvcmdldCB0byBzYXZlIHlvdXIgcC10YWJsZSEhIQ0KDQoNCkxldHMgZ28gYWhlYWQgYW5kIGZpeCB0aGUgemVyb3MgYW5kIHJlb212ZSB0aGUgaW50ZXJjZXB0Li4uDQpgYGB7cn0NCnB0YWJsZSA8LSBUTExfTGF5ZXJfTG9jYXRpb25fYmV0dGEkdGFibGUgI3N0YXJ0IGJ5IHNlcGFyYXRpbmcgdGhlIHB0YWJsZSBmcm9tIHRoZSB3ZWlyZCBiZXR0YSBvYmplY3QNCnB0YWJsZV9hZGogPC0gcHRhYmxlICU+JSAjbW92aW5nIGl0IHRvIGEgbmV3IHRhYmxlIGJjIHJlYXNvbnMNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICByZW5hbWUocF92YWx1ZXMgPSAicC12YWx1ZXMiKSAjcmVtb3ZpbmcgdGhlIHAtdmxhdWVzIG5hbWVzIGJlY2F1c2UgZGFzaGVzIG1lc3Mgc3R1ZmYgdXAgbGF0ZXIgb24NCnB0YWJsZV9hZGogPC0gcHRhYmxlX2FkaiAlPiUgZmlsdGVyKCFyb3dfbnVtYmVyKCkgJWluJSBjKDEpKQ0KcHRhYmxlX2FkaiRwX3ZhbHVlcyA8LSBwLmFkanVzdChwdGFibGVfYWRqJHBfdmFsdWVzLCBtZXRob2QgPSAiYm9uZmVycm9uaSIpICNhcHBseSB3aGF0ZXZlciBwdmFsdWUgY29ycmVjdGlvbiB5b3UnZCBsaWtlLCBib25mZXJyb25pIGlzbid0IGEgcmVjb21tZW5kYXRpb24sIGJ1dCB0aGUgZmlyc3Qgb25lIEkgcmVtZW1iZXJlZCBvZmYgaGFuZA0KcHRhYmxlX2Fkag0KYGBgDQp5a25vdyB3aGF0PyBpZiB0aGVyZSB3YXNudCBhIHZhbHVlIDggZGlnaXRzIHVuZGVyIEkgdGhpbmsgaXQgbWlnaHQganVzdCBiZSBzaWduaWZpZ2FudC4uLg0KDQoNCg0KUFMuIGhlcmVzIHRoZSBvcmlnaW5hbCBpc3N1ZXMgcGFnZSB3aGVyZSBJIGZpbmFsbHkgZmlndXJlZCBvdXQgaG93IHRvIHJlYWQgdGhlIGludGVyY2VwdCB0YWJsZXMNClRoaXMgbWFrZXMgdGhlIHRhYmxlIDEweCBtb3JlIHVucmVhZGFibGUgYW5kIHRvb2sgbWUgYSB3aG9sZSBhc3MgZGF5IHRvIGZpbmQgb3V0DQpodHRwczovL2dpdGh1Yi5jb20vYWR3OTYvRGl2TmV0L2lzc3Vlcy8zNA0KDQo=