Load the libraries
Get the data
read_csv("RootingWindow_RawData.csv")%>%
tbl_df()->
dat
dat%>%
select(-DegreeOfBranching,-NoOfRootingPts)->
dat
Stem width vs stem height
dat%>%
mutate(Plant="ALL")%>%
bind_rows(dat)%>%
filter(StemWidth_mm!=max(StemWidth_mm))%>%
ggplot(aes(StemWidth_mm,Height_mm))+
geom_point()+
geom_smooth(method="lm")+
stat_poly_line(se=FALSE) +
stat_poly_eq(aes(label = paste(after_stat(eq.label),
after_stat(rr.label), sep = "*\", \"*")))+
facet_wrap(~Plant)+
ylab("Height (mm)")+xlab("Stem diameter (mm)")
Stem width at start of experiment vs above ground biomass at
end
dat%>%
mutate(Plant="ALL")%>%
bind_rows(dat)%>%
filter(StemWidth_mm!=max(StemWidth_mm))%>%
ggplot(aes(StemWidth_mm,AboveGroundMass_g))+
geom_point()+
geom_smooth(method="lm")+
stat_poly_line(se=FALSE) +
stat_poly_eq(aes(label = paste(after_stat(eq.label),
after_stat(rr.label), sep = "*\", \"*")))+
facet_wrap(~Plant)+
ylab("Above ground biomass at end of experiment")+xlab("Stem diameter (mm)")
ggsave(paste("ScatterPlot_Width_Mass_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""),width=8,height=8)
Stem Height at start of experiment vs above ground biomass at
end
dat%>%
mutate(Plant="ALL")%>%
bind_rows(dat)%>%
filter(StemWidth_mm!=max(StemWidth_mm))%>%
ggplot(aes(Height_mm,AboveGroundMass_g))+
geom_point()+
geom_smooth(method="lm")+
stat_poly_line(se=FALSE) +
stat_poly_eq(aes(label = paste(after_stat(eq.label),
after_stat(rr.label), sep = "*\", \"*")))+
facet_wrap(~Plant)+
ylab("Above ground biomass at end of experiment")+xlab("Height (mm)")
ggsave(paste("ScatterPlot_Height_Mass_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""),width=8,height=8)
Figure: Boxplot of StemWidth across Plants with post-hoc Tukey
dissimilarity letters
dat%>%
lm(StemWidth_mm~Plant,data=.)%>%
aov()->anova
anova%>%
TukeyHSD()->tukey
cld <- multcompLetters4(anova, tukey)
print(cld)
dat%>%
group_by(Plant)%>%
summarise(max=max(StemWidth_mm))%>%
#summarise(mean=mean(weight), quant = quantile(weight, probs = 0.75)) %>%
arrange(desc(max))->
Tk
# extracting the compact letter display and adding to the Tk table
as.data.frame.list(cld$Plant)%>%
rownames_to_column("Plant")%>%
right_join(Tk)->
Tk2
dat%>%
ggplot(aes(Plant,StemWidth_mm))+
geom_violin()+
geom_boxplot(width=0.3)+
geom_text(data = Tk2, aes(x = Plant, y = 10, label = Letters))+
theme_bw()+
xlab("Plant ID")+ylab("Basal Stem Width of Cutting (mm)")-> width.fig
width.fig
ggsave(paste("StemWidth_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""))
Figure: Boxplot of Stem Height across Plants with post-hoc Tukey
dissimilarity letters
dat%>%
lm(Height_mm~Plant,data=.)%>%
aov()->anova
anova%>%
TukeyHSD()->tukey
cld <- multcompLetters4(anova, tukey)
print(cld)
dat%>%
group_by(Plant)%>%
summarise(max=max(Height_mm))%>%
#summarise(mean=mean(weight), quant = quantile(weight, probs = 0.75)) %>%
arrange(desc(max))->
Tk
# extracting the compact letter display and adding to the Tk table
as.data.frame.list(cld$Plant)%>%
rownames_to_column("Plant")%>%
right_join(Tk)->
Tk2
dat%>%
ggplot(aes(Plant,Height_mm))+
geom_violin()+
geom_boxplot(width=0.3)+
geom_text(data = Tk2, aes(x = Plant, y = 290, label = Letters))+
theme_bw()+
xlab("Plant ID")+ylab("Stem Height of Cutting (mm)")-> height.fig
height.fig
#ggsave(paste("Height_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""))
Figure: Boxplot of Dry Stem mass across Plants with post-hoc Tukey
dissimilarity letters
dat%>%
lm(StemDryMass_g~Plant,data=.)%>%
aov()->anova
anova%>%
TukeyHSD()->tukey
cld <- multcompLetters4(anova, tukey)
print(cld)
dat%>%
group_by(Plant)%>%
summarise(max=max(StemDryMass_g))%>%
#summarise(mean=mean(weight), quant = quantile(weight, probs = 0.75)) %>%
arrange(desc(max))->
Tk
# extracting the compact letter display and adding to the Tk table
as.data.frame.list(cld$Plant)%>%
rownames_to_column("Plant")%>%
right_join(Tk)->
Tk2
dat%>%
ggplot(aes(Plant,StemDryMass_g))+
geom_violin()+
geom_boxplot(width=0.3)+
geom_text(data = Tk2, aes(x = Plant, y = 290, label = Letters))+
theme_bw()+
xlab("Plant ID")+ylab("Stem Height of Cutting (mm)")
stemdrymass.fig
#ggsave(paste("Height_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""))
Linear model and correlation of stem width with rooting
fraction
dat%>%
mutate(Plant="ALL")%>%
bind_rows(dat)%>%
ggplot(aes(StemWidth_mm,RootFraction*100))+
geom_point()+
geom_smooth(method="lm",se=FALSE)+
stat_poly_line(se=FALSE) +
stat_poly_eq(aes(label = paste(after_stat(eq.label),
after_stat(rr.label), sep = "*\", \"*")))+
facet_wrap(~Plant)+
ylab("Root Percentage")+xlab("Stem diameter (mm)")
ggsave(paste("Width_vs_RootFraction_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""),width=8,height=8)
Linear model and correlation of cutting height with rooting
fraction
dat%>%
mutate(Plant="ALL")%>%
bind_rows(dat)%>%
ggplot(aes(Height_mm,RootFraction*100))+
geom_point()+
geom_smooth(method="lm",se=FALSE)+
stat_poly_line(se=FALSE) +
stat_poly_eq(aes(label = paste(after_stat(eq.label),
after_stat(rr.label), sep = "*\", \"*")))+
facet_wrap(~Plant)+
ylab("Root Percentage")+xlab("Plant Height (mm)")
ggsave(paste("Height_vs_RootFraction_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""),width=9,height=9)
Nested ANOVA: Root mass
dat%>%
filter(Treatment!="C")%>%
filter(!is.na(RootMass_g))%>%
aov(RootMass_g~Treatment/Plant,data=.)%>%
summary()
dat%>%
group_by(Plant,Treatment)%>%summarise(n=n()
)
Nested ANOVA: Root Fraction
dat%>%
filter(Treatment!="C")%>%
filter(!is.na(RootMass_g))%>%
aov(RootFraction~Treatment/Plant,data=.)%>%
summary()
Root mass overall and individual boxplot
dat%>%
ggplot(aes(Treatment,RootMass_g))+
geom_boxplot(size=1,outlier.size = 2)+
geom_boxplot(aes(fill=Plant),alpha=0.5,outlier.shape = "")+
scale_fill_manual(values=wes_palette(7, name = "Zissou1", type = "continuous"))+
#geom_violin()+
#geom_line()+
#geom_jitter(width = 0.15,aes(colour=Plant,shape=Plant))+
#facet_wrap(~Plant)+
ylab("Dry Root Mass (g)")
ggsave(paste("RootMassBoxplot_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""),width=7,height=5)
Root mass overall and individual boxplot v2
dat%>%
bind_rows(mutate(dat,Plant="All"))->
tutu
tutu%>%
filter(Treatment!="C")->tutu
for (i in unique(tutu$Plant)) {
print(i)
tutu%>%
filter(Plant==i)->
toto
kruskal.test(toto$RootMass_g,as.factor(toto$Treatment))%>%
print()
}
res <- NULL
for (i in unique(tutu$Plant)) {
tutu%>%
filter(Plant==i)->
toto
#kruskal.test(toto$RootMass_g,as.factor(toto$Treatment))
kruskalmc(toto$RootMass_g,as.factor(toto$Treatment)) ->
tata
dif3 <- tata$dif.com$difference
names(dif3)<-rownames(tata$dif.com)
multcompLetters(dif3)-> tyty
bind_rows(res,c(Plant=i,tyty$Letters))->res
}
# res%>%
# mutate(C="")->res
apply(res,1,function(fx) length(unique(fx)))->ind
res[ind==2,2:7]<-""
res%>%
pivot_longer(2:7,names_to="Treatment",values_to = "Symbol")->
res2
tutu%>%
group_by(Plant,Treatment)%>%
summarise(RootMass_g=max(RootMass_g,na.rm=T))%>%
left_join(res2)%>%
as.data.frame()->
sigletter
tutu%>%
ggplot(aes(Treatment,RootMass_g))+
geom_boxplot(size=1,outlier.size = 2)+
#geom_boxplot(aes(fill=Plant),alpha=0.5,outlier.shape = "")+
#scale_fill_manual(values=wes_palette(7, name = "Zissou1", type = "continuous"))+
#geom_violin()+
#geom_line()+
#geom_jitter(width = 0.15,aes(colour=Plant,shape=Plant))+
facet_wrap(~Plant,ncol=4)+
geom_text(data=sigletter,aes(Treatment,RootMass_g+0.05,label=Symbol))+
ylab("Dry Root Mass (g)")+
scale_x_discrete(guide = guide_axis(n.dodge = 2))
ggsave(paste("RootMassBoxplot_V3_",format(Sys.time(),"%Y%m%d-%Hh%Mm"),".jpg",sep=""),width=7,height=5)
Kruskal Test within plants across treatments
dat%>%
filter(Treatment!="C")%>%
group_by(Plant)%>%
do(tidy(kruskal.test(x = .$RootMass_g, g = .$Treatment)))
Rooting fraction per plant across treatments
(dat%>%
filter(Treatment!="C")%>%
group_by(Treatment,Plant)%>%
summarise(Rooted=sum(RootMass_g>0,na.rm=T),
n=n(),
RootedFrac=Rooted/n*100)->for_stats)%>%
ungroup()%>%
ggplot(aes(Treatment,RootedFrac,label=Plant))+
#geom_violin()+
geom_line(colour="grey",size=3)+
geom_text_repel(colour="darkgrey",fontface=3)+
geom_point(size=3)+
ylim(c(0,100))+
ylab("Rooted percentage per plant")+
xlab("Day of watering")+
labs(title = expression(paste(italic("Portulacaria afra")," rooting window experiment")),
subtitle = "Rooting percentage per plant for day of watering during the first month\n
Seven plants were harvested and the day of watering in the first month is shown. 12 cuttings per plant.",
caption = paste("Spekboom Restoration Research Group, Nelson Mandela University, 2021\n
Figure generated by Alastair.Potts@mandela.ac.za (",Sys.time(),")",sep=""))
ggsave("RootingWindow-RootPercent.jpg",width=8,height=5)
(dat%>%
filter(Treatment!="C")%>%
group_by(Treatment,Plant)%>%
summarise(Rooted=sum(RootMass_g>0,na.rm=T),
n=n(),
RootedFrac=Rooted/n*100)->for_stats)%>%
ungroup()%>%
ggplot(aes(Treatment,RootedFrac,label=Plant))+
#geom_violin()+
geom_line(colour="grey",size=3)+
geom_text_repel(colour="darkgrey",fontface=3)+
geom_point(size=3)+
ylim(c(0,100))+
ylab("Rooted percentage per plant")+
xlab("Day of watering")
ggsave("Fig1_Publication_RootingWindow-RootPercent.jpg",width=7,height=4)
Mean rooting fraction per plant for each treatment
(dat%>%
filter(Treatment!="C")%>%
group_by(Treatment,Plant)%>%
summarise(Rooted=sum(RootMass_g>0,na.rm=T),
n=n(),
uRootMass=mean(RootMass_g,na.rm=T),
medRootMass=median(RootMass_g)))%>%
ungroup()%>%
ggplot(aes(Treatment,uRootMass,label=Plant))+
#geom_violin()+
geom_line(colour="grey",size=3)+
geom_text_repel(colour="darkgrey",fontface=3)+
geom_point(size=3)+
#ylim(c(0,100))+
ylab("Mean root percentage per plant")+
xlab("Day of watering")
dat%>%
filter(Treatment!="C")%>%
group_by(Treatment,Plant)%>%
summarise(Rooted=sum(RootMass_g>0,na.rm=T),
n=n(),
uRootMass=mean(RootMass_g),
medRootMass=median(RootMass_g,na.rm=T))%>%
ungroup()%>%
ggplot(aes(Treatment,medRootMass,label=Plant))+
#geom_violin()+
geom_line(colour="grey",size=3)+
geom_text_repel(colour="darkgrey",fontface=3,min.segment.length = 0)+
geom_point(size=3)+
#ylim(c(0,100))+
ylab("Median root percentage per plant")+
xlab("Day of watering")
#Suplementary glasshouse environmental data analysis (.CSV files not
provided, if you would like to run this script please generate the
relevant data sets using “RootingWindow_FullData” provided)
read_csv(construct_download_url(url,sheetid="838520867"))%>%
tbl_df()->
dat_temp
read_csv(construct_download_url(url,sheetid="1444740489"))%>%
tbl_df()->
dat_treat
dat_treat%>%
mutate(Date=mdy_hm(paste(Date,"11h59")))->
dat_treat2
dat_temp%>%
mutate(Timestamp=ymd_hm(Timestamp))%>%
ggplot(aes(Timestamp,Temperature))+
geom_vline(xintercept = dat_treat2$Date,colour="darkgrey",size=1)+
geom_line()+
ylim(0,36)+
scale_x_datetime(sec.axis = sec_axis(~.,breaks=dat_treat2$Date,labels=dat_treat2$Treatment))+
xlab("")+
ylab("Temperature (°C) ")+
theme(axis.text.x.top =element_text(color="darkgrey"))+
geom_line(aes(Timestamp,DewPoint_dC),colour="black",linetype=2)->
A
dat_temp%>%
ggplot(aes("",Temperature))+
geom_boxplot()+
ylim(0,36)+
xlab("")+
ylab("")->
B
dat_temp%>%
mutate(Timestamp=ymd_hm(Timestamp))%>%
ggplot(aes(Timestamp,RelativeHumidity))+
geom_vline(xintercept = dat_treat2$Date,colour="darkgrey",size=1)+
geom_line()+
ylim(0,100)+
scale_x_datetime(sec.axis = sec_axis(~.,breaks=dat_treat2$Date,labels=dat_treat2$Treatment))+
theme(axis.text.x.top =element_text(color="darkgrey"))+
xlab("")+
ylab("Relative humidity (%)")->
C
(A+B)/C
A/C+plot_annotation(tag_levels = 'A')
ggsave("Env.conditions.jpg",width=8,height=5.5)
dat_temp%>%
mutate(Timestamp=ymd_hm(Timestamp),
Day=date(Timestamp))%>%
group_by(Day)%>%
summarise(maxT=max(Temperature),
minT=min(Temperature))%>%
ungroup()%>%
summarise(mean(maxT),
mean(minT),
sd(maxT),
sd(minT))
dat_temp%>%
summarise(mean(RelativeHumidity),sd(RelativeHumidity))
dat_temp%>%
group_by(Da)
summarise(uT=mean(Temperature))
LS0tDQp0aXRsZTogInJvb3RpbmdXaW5kb3dfUGVlckogMjAyMiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KIyBMb2FkIHRoZSBsaWJyYXJpZXMNCmBgYHtyIGxpYnJhcmllcywgZWNobz1GfQ0KDQpsaWJyYXJ5KCJkcGx5ciIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmxpYnJhcnkoImdncG1pc2MiKQ0KbGlicmFyeSgiZ2dwdWJyIikNCmxpYnJhcnkoInBhdGNod29yayIpDQpsaWJyYXJ5KCJsdWJyaWRhdGUiKQ0KbGlicmFyeSgidGliYmxlIikNCmxpYnJhcnkoInRpZHlyIikNCmxpYnJhcnkoImdzaGVldCIpDQpsaWJyYXJ5KCJyZWFkciIpDQpsaWJyYXJ5KCJndG9vbHMiKQ0KbGlicmFyeSgid2VzYW5kZXJzb24iKQ0KbGlicmFyeSgiZ2dmb3JjZSIpDQpsaWJyYXJ5KCJnZ3JlcGVsIikNCmxpYnJhcnkoInRpYmJsZXRpbWUiKQ0KbGlicmFyeSgiYnJvb20iKQ0KbGlicmFyeSgibmxtZSIpDQpsaWJyYXJ5KCJtdWx0Y29tcFZpZXciKQ0KbGlicmFyeSgibG1lNCIpDQpsaWJyYXJ5KCJybWFya2Rvd24iKQ0KbGlicmFyeSgicGdpcm1lc3MiKQ0KDQojUGxlYXNlIHNldCB5b3VyIG93biB3b3JraW5nIGRpcmVjdG9yeQ0Kc2V0d2QoKQ0KYGBgDQoNCiMgR2V0IHRoZSBkYXRhDQpgYGB7cn0NCg0KDQpyZWFkX2NzdigiUm9vdGluZ1dpbmRvd19SYXdEYXRhLmNzdiIpJT4lDQogIHRibF9kZigpLT4NCiAgZGF0DQoNCmRhdCU+JQ0KICBzZWxlY3QoLURlZ3JlZU9mQnJhbmNoaW5nLC1Ob09mUm9vdGluZ1B0cyktPg0KICBkYXQNCg0KDQpgYGANCg0KIyBTdGVtIHdpZHRoIHZzIHN0ZW0gaGVpZ2h0DQpgYGB7cn0NCmRhdCU+JQ0KICBtdXRhdGUoUGxhbnQ9IkFMTCIpJT4lDQogIGJpbmRfcm93cyhkYXQpJT4lDQogIGZpbHRlcihTdGVtV2lkdGhfbW0hPW1heChTdGVtV2lkdGhfbW0pKSU+JQ0KICBnZ3Bsb3QoYWVzKFN0ZW1XaWR0aF9tbSxIZWlnaHRfbW0pKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikrDQogIHN0YXRfcG9seV9saW5lKHNlPUZBTFNFKSArDQogIHN0YXRfcG9seV9lcShhZXMobGFiZWwgPSBwYXN0ZShhZnRlcl9zdGF0KGVxLmxhYmVsKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFmdGVyX3N0YXQocnIubGFiZWwpLCBzZXAgPSAiKlwiLCBcIioiKSkpKw0KICBmYWNldF93cmFwKH5QbGFudCkrDQogIHlsYWIoIkhlaWdodCAobW0pIikreGxhYigiU3RlbSBkaWFtZXRlciAobW0pIikNCmBgYA0KDQojIFN0ZW0gd2lkdGggYXQgc3RhcnQgb2YgZXhwZXJpbWVudCB2cyBhYm92ZSBncm91bmQgYmlvbWFzcyBhdCBlbmQNCmBgYHtyfQ0KZGF0JT4lDQogIG11dGF0ZShQbGFudD0iQUxMIiklPiUNCiAgYmluZF9yb3dzKGRhdCklPiUNCiAgZmlsdGVyKFN0ZW1XaWR0aF9tbSE9bWF4KFN0ZW1XaWR0aF9tbSkpJT4lDQogIGdncGxvdChhZXMoU3RlbVdpZHRoX21tLEFib3ZlR3JvdW5kTWFzc19nKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpKw0KICBzdGF0X3BvbHlfbGluZShzZT1GQUxTRSkgKw0KICBzdGF0X3BvbHlfZXEoYWVzKGxhYmVsID0gcGFzdGUoYWZ0ZXJfc3RhdChlcS5sYWJlbCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZnRlcl9zdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpKSsNCiAgZmFjZXRfd3JhcCh+UGxhbnQpKw0KICB5bGFiKCJBYm92ZSBncm91bmQgYmlvbWFzcyBhdCBlbmQgb2YgZXhwZXJpbWVudCIpK3hsYWIoIlN0ZW0gZGlhbWV0ZXIgKG1tKSIpDQpnZ3NhdmUocGFzdGUoIlNjYXR0ZXJQbG90X1dpZHRoX01hc3NfIixmb3JtYXQoU3lzLnRpbWUoKSwiJVklbSVkLSVIaCVNbSIpLCIuanBnIixzZXA9IiIpLHdpZHRoPTgsaGVpZ2h0PTgpDQpgYGANCiMgU3RlbSBIZWlnaHQgYXQgc3RhcnQgb2YgZXhwZXJpbWVudCB2cyBhYm92ZSBncm91bmQgYmlvbWFzcyBhdCBlbmQNCmBgYHtyfQ0KZGF0JT4lDQogIG11dGF0ZShQbGFudD0iQUxMIiklPiUNCiAgYmluZF9yb3dzKGRhdCklPiUNCiAgZmlsdGVyKFN0ZW1XaWR0aF9tbSE9bWF4KFN0ZW1XaWR0aF9tbSkpJT4lDQogIGdncGxvdChhZXMoSGVpZ2h0X21tLEFib3ZlR3JvdW5kTWFzc19nKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpKw0KICBzdGF0X3BvbHlfbGluZShzZT1GQUxTRSkgKw0KICBzdGF0X3BvbHlfZXEoYWVzKGxhYmVsID0gcGFzdGUoYWZ0ZXJfc3RhdChlcS5sYWJlbCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZnRlcl9zdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpKSsNCiAgZmFjZXRfd3JhcCh+UGxhbnQpKw0KICB5bGFiKCJBYm92ZSBncm91bmQgYmlvbWFzcyBhdCBlbmQgb2YgZXhwZXJpbWVudCIpK3hsYWIoIkhlaWdodCAobW0pIikNCmdnc2F2ZShwYXN0ZSgiU2NhdHRlclBsb3RfSGVpZ2h0X01hc3NfIixmb3JtYXQoU3lzLnRpbWUoKSwiJVklbSVkLSVIaCVNbSIpLCIuanBnIixzZXA9IiIpLHdpZHRoPTgsaGVpZ2h0PTgpDQpgYGANCg0KIyBGaWd1cmU6IEJveHBsb3Qgb2YgU3RlbVdpZHRoIGFjcm9zcyBQbGFudHMgd2l0aCBwb3N0LWhvYyBUdWtleSBkaXNzaW1pbGFyaXR5IGxldHRlcnMNCmBgYHtyfQ0KZGF0JT4lDQogIGxtKFN0ZW1XaWR0aF9tbX5QbGFudCxkYXRhPS4pJT4lDQogIGFvdigpLT5hbm92YQ0KYW5vdmElPiUNCiAgVHVrZXlIU0QoKS0+dHVrZXkNCmNsZCA8LSBtdWx0Y29tcExldHRlcnM0KGFub3ZhLCB0dWtleSkNCnByaW50KGNsZCkNCmRhdCU+JQ0KICBncm91cF9ieShQbGFudCklPiUNCiAgc3VtbWFyaXNlKG1heD1tYXgoU3RlbVdpZHRoX21tKSklPiUNCiAgI3N1bW1hcmlzZShtZWFuPW1lYW4od2VpZ2h0KSwgcXVhbnQgPSBxdWFudGlsZSh3ZWlnaHQsIHByb2JzID0gMC43NSkpICU+JQ0KICBhcnJhbmdlKGRlc2MobWF4KSktPg0KICBUaw0KIyBleHRyYWN0aW5nIHRoZSBjb21wYWN0IGxldHRlciBkaXNwbGF5IGFuZCBhZGRpbmcgdG8gdGhlIFRrIHRhYmxlDQphcy5kYXRhLmZyYW1lLmxpc3QoY2xkJFBsYW50KSU+JQ0KICByb3duYW1lc190b19jb2x1bW4oIlBsYW50IiklPiUNCiAgcmlnaHRfam9pbihUayktPg0KICBUazINCmRhdCU+JQ0KICBnZ3Bsb3QoYWVzKFBsYW50LFN0ZW1XaWR0aF9tbSkpKw0KICBnZW9tX3Zpb2xpbigpKw0KICBnZW9tX2JveHBsb3Qod2lkdGg9MC4zKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBUazIsIGFlcyh4ID0gUGxhbnQsIHkgPSAxMCwgbGFiZWwgPSBMZXR0ZXJzKSkrDQogIHRoZW1lX2J3KCkrDQogIHhsYWIoIlBsYW50IElEIikreWxhYigiQmFzYWwgU3RlbSBXaWR0aCBvZiBDdXR0aW5nIChtbSkiKS0+IHdpZHRoLmZpZw0Kd2lkdGguZmlnDQpnZ3NhdmUocGFzdGUoIlN0ZW1XaWR0aF8iLGZvcm1hdChTeXMudGltZSgpLCIlWSVtJWQtJUhoJU1tIiksIi5qcGciLHNlcD0iIikpDQpgYGANCg0KIyBGaWd1cmU6IEJveHBsb3Qgb2YgU3RlbSBIZWlnaHQgYWNyb3NzIFBsYW50cyB3aXRoIHBvc3QtaG9jIFR1a2V5IGRpc3NpbWlsYXJpdHkgbGV0dGVycw0KYGBge3J9DQpkYXQlPiUNCiAgbG0oSGVpZ2h0X21tflBsYW50LGRhdGE9LiklPiUNCiAgYW92KCktPmFub3ZhDQphbm92YSU+JQ0KICBUdWtleUhTRCgpLT50dWtleQ0KY2xkIDwtIG11bHRjb21wTGV0dGVyczQoYW5vdmEsIHR1a2V5KQ0KcHJpbnQoY2xkKQ0KZGF0JT4lDQogIGdyb3VwX2J5KFBsYW50KSU+JQ0KICBzdW1tYXJpc2UobWF4PW1heChIZWlnaHRfbW0pKSU+JQ0KICAjc3VtbWFyaXNlKG1lYW49bWVhbih3ZWlnaHQpLCBxdWFudCA9IHF1YW50aWxlKHdlaWdodCwgcHJvYnMgPSAwLjc1KSkgJT4lDQogIGFycmFuZ2UoZGVzYyhtYXgpKS0+DQogIFRrDQojIGV4dHJhY3RpbmcgdGhlIGNvbXBhY3QgbGV0dGVyIGRpc3BsYXkgYW5kIGFkZGluZyB0byB0aGUgVGsgdGFibGUNCmFzLmRhdGEuZnJhbWUubGlzdChjbGQkUGxhbnQpJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiUGxhbnQiKSU+JQ0KICByaWdodF9qb2luKFRrKS0+DQogIFRrMg0KZGF0JT4lDQogIGdncGxvdChhZXMoUGxhbnQsSGVpZ2h0X21tKSkrDQogIGdlb21fdmlvbGluKCkrDQogIGdlb21fYm94cGxvdCh3aWR0aD0wLjMpKw0KICBnZW9tX3RleHQoZGF0YSA9IFRrMiwgYWVzKHggPSBQbGFudCwgeSA9IDI5MCwgbGFiZWwgPSBMZXR0ZXJzKSkrDQogIHRoZW1lX2J3KCkrDQogIHhsYWIoIlBsYW50IElEIikreWxhYigiU3RlbSBIZWlnaHQgIG9mIEN1dHRpbmcgKG1tKSIpLT4gaGVpZ2h0LmZpZw0KaGVpZ2h0LmZpZw0KI2dnc2F2ZShwYXN0ZSgiSGVpZ2h0XyIsZm9ybWF0KFN5cy50aW1lKCksIiVZJW0lZC0lSGglTW0iKSwiLmpwZyIsc2VwPSIiKSkNCmBgYA0KDQojIEZpZ3VyZTogQm94cGxvdCBvZiBEcnkgU3RlbSBtYXNzIGFjcm9zcyBQbGFudHMgd2l0aCBwb3N0LWhvYyBUdWtleSBkaXNzaW1pbGFyaXR5IGxldHRlcnMNCmBgYHtyfQ0KZGF0JT4lDQogIGxtKFN0ZW1EcnlNYXNzX2d+UGxhbnQsZGF0YT0uKSU+JQ0KICBhb3YoKS0+YW5vdmENCmFub3ZhJT4lDQogIFR1a2V5SFNEKCktPnR1a2V5DQpjbGQgPC0gbXVsdGNvbXBMZXR0ZXJzNChhbm92YSwgdHVrZXkpDQpwcmludChjbGQpDQpkYXQlPiUNCiAgZ3JvdXBfYnkoUGxhbnQpJT4lDQogIHN1bW1hcmlzZShtYXg9bWF4KFN0ZW1EcnlNYXNzX2cpKSU+JQ0KICAjc3VtbWFyaXNlKG1lYW49bWVhbih3ZWlnaHQpLCBxdWFudCA9IHF1YW50aWxlKHdlaWdodCwgcHJvYnMgPSAwLjc1KSkgJT4lDQogIGFycmFuZ2UoZGVzYyhtYXgpKS0+DQogIFRrDQojIGV4dHJhY3RpbmcgdGhlIGNvbXBhY3QgbGV0dGVyIGRpc3BsYXkgYW5kIGFkZGluZyB0byB0aGUgVGsgdGFibGUNCmFzLmRhdGEuZnJhbWUubGlzdChjbGQkUGxhbnQpJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiUGxhbnQiKSU+JQ0KICByaWdodF9qb2luKFRrKS0+DQogIFRrMg0KZGF0JT4lDQogIGdncGxvdChhZXMoUGxhbnQsU3RlbURyeU1hc3NfZykpKw0KICBnZW9tX3Zpb2xpbigpKw0KICBnZW9tX2JveHBsb3Qod2lkdGg9MC4zKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBUazIsIGFlcyh4ID0gUGxhbnQsIHkgPSAyOTAsIGxhYmVsID0gTGV0dGVycykpKw0KICB0aGVtZV9idygpKw0KICB4bGFiKCJQbGFudCBJRCIpK3lsYWIoIlN0ZW0gSGVpZ2h0ICBvZiBDdXR0aW5nIChtbSkiKQ0KDQpzdGVtZHJ5bWFzcy5maWcNCiNnZ3NhdmUocGFzdGUoIkhlaWdodF8iLGZvcm1hdChTeXMudGltZSgpLCIlWSVtJWQtJUhoJU1tIiksIi5qcGciLHNlcD0iIikpDQpgYGANCiMgQ29tYmluYXRpb24gb2YgdGhlIHR3byBmaWd1cmVzIGFib3ZlDQpgYGB7cn0NCg0KKHdpZHRoLmZpZyt4bGFiKCIiKStnZ3RpdGxlKCdBKScpKS8oaGVpZ2h0LmZpZyt4bGFiKCIiKStnZ3RpdGxlKCdCKScpKQ0KZ2dzYXZlKHBhc3RlKCJCb3hwbG90c19XaWR0aF9IZWlnaHRfIixmb3JtYXQoU3lzLnRpbWUoKSwiJVklbSVkLSVIaCVNbSIpLCIuanBnIixzZXA9IiIpKQ0KYGBgDQoNCg0KIyBMaW5lYXIgbW9kZWwgYW5kIGNvcnJlbGF0aW9uIG9mIHN0ZW0gd2lkdGggd2l0aCByb290aW5nIGZyYWN0aW9uDQpgYGB7cn0NCmRhdCU+JQ0KICBtdXRhdGUoUGxhbnQ9IkFMTCIpJT4lDQogIGJpbmRfcm93cyhkYXQpJT4lDQogIGdncGxvdChhZXMoU3RlbVdpZHRoX21tLFJvb3RGcmFjdGlvbioxMDApKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIixzZT1GQUxTRSkrDQogIHN0YXRfcG9seV9saW5lKHNlPUZBTFNFKSArDQogIHN0YXRfcG9seV9lcShhZXMobGFiZWwgPSBwYXN0ZShhZnRlcl9zdGF0KGVxLmxhYmVsKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFmdGVyX3N0YXQocnIubGFiZWwpLCBzZXAgPSAiKlwiLCBcIioiKSkpKw0KICBmYWNldF93cmFwKH5QbGFudCkrDQogIHlsYWIoIlJvb3QgUGVyY2VudGFnZSIpK3hsYWIoIlN0ZW0gZGlhbWV0ZXIgKG1tKSIpDQpnZ3NhdmUocGFzdGUoIldpZHRoX3ZzX1Jvb3RGcmFjdGlvbl8iLGZvcm1hdChTeXMudGltZSgpLCIlWSVtJWQtJUhoJU1tIiksIi5qcGciLHNlcD0iIiksd2lkdGg9OCxoZWlnaHQ9OCkNCmBgYA0KDQoNCiMgTGluZWFyIG1vZGVsIGFuZCBjb3JyZWxhdGlvbiBvZiBjdXR0aW5nIGhlaWdodCB3aXRoIHJvb3RpbmcgZnJhY3Rpb24NCmBgYHtyfQ0KZGF0JT4lDQogIG11dGF0ZShQbGFudD0iQUxMIiklPiUNCiAgYmluZF9yb3dzKGRhdCklPiUNCiAgZ2dwbG90KGFlcyhIZWlnaHRfbW0sUm9vdEZyYWN0aW9uKjEwMCkpKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLHNlPUZBTFNFKSsNCiAgc3RhdF9wb2x5X2xpbmUoc2U9RkFMU0UpICsNCiAgc3RhdF9wb2x5X2VxKGFlcyhsYWJlbCA9IHBhc3RlKGFmdGVyX3N0YXQoZXEubGFiZWwpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWZ0ZXJfc3RhdChyci5sYWJlbCksIHNlcCA9ICIqXCIsIFwiKiIpKSkrDQogIGZhY2V0X3dyYXAoflBsYW50KSsNCiAgeWxhYigiUm9vdCBQZXJjZW50YWdlIikreGxhYigiUGxhbnQgSGVpZ2h0IChtbSkiKQ0KZ2dzYXZlKHBhc3RlKCJIZWlnaHRfdnNfUm9vdEZyYWN0aW9uXyIsZm9ybWF0KFN5cy50aW1lKCksIiVZJW0lZC0lSGglTW0iKSwiLmpwZyIsc2VwPSIiKSx3aWR0aD05LGhlaWdodD05KQ0KYGBgDQoNCiMgTmVzdGVkIEFOT1ZBOiBSb290IG1hc3MNCmBgYHtyfQ0KZGF0JT4lDQogIGZpbHRlcihUcmVhdG1lbnQhPSJDIiklPiUNCiAgZmlsdGVyKCFpcy5uYShSb290TWFzc19nKSklPiUNCiAgYW92KFJvb3RNYXNzX2d+VHJlYXRtZW50L1BsYW50LGRhdGE9LiklPiUNCiAgc3VtbWFyeSgpDQoNCmRhdCU+JQ0KIGdyb3VwX2J5KFBsYW50LFRyZWF0bWVudCklPiVzdW1tYXJpc2Uobj1uKCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQoNCmBgYA0KIyBOZXN0ZWQgQU5PVkE6IFJvb3QgRnJhY3Rpb24NCmBgYHtyfQ0KZGF0JT4lDQogIGZpbHRlcihUcmVhdG1lbnQhPSJDIiklPiUNCiAgZmlsdGVyKCFpcy5uYShSb290TWFzc19nKSklPiUNCiAgYW92KFJvb3RGcmFjdGlvbn5UcmVhdG1lbnQvUGxhbnQsZGF0YT0uKSU+JQ0KICBzdW1tYXJ5KCkNCmBgYA0KDQojIFJvb3QgbWFzcyBvdmVyYWxsIGFuZCBpbmRpdmlkdWFsIGJveHBsb3QNCmBgYHtyfQ0KZGF0JT4lDQogIGdncGxvdChhZXMoVHJlYXRtZW50LFJvb3RNYXNzX2cpKSsNCiAgZ2VvbV9ib3hwbG90KHNpemU9MSxvdXRsaWVyLnNpemUgPSAyKSsNCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVBsYW50KSxhbHBoYT0wLjUsb3V0bGllci5zaGFwZSA9ICIiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXdlc19wYWxldHRlKDcsIG5hbWUgPSAiWmlzc291MSIsIHR5cGUgPSAiY29udGludW91cyIpKSsNCiAgI2dlb21fdmlvbGluKCkrDQogICNnZW9tX2xpbmUoKSsNCiAgI2dlb21faml0dGVyKHdpZHRoID0gMC4xNSxhZXMoY29sb3VyPVBsYW50LHNoYXBlPVBsYW50KSkrDQogICNmYWNldF93cmFwKH5QbGFudCkrDQogIHlsYWIoIkRyeSBSb290IE1hc3MgKGcpIikNCmdnc2F2ZShwYXN0ZSgiUm9vdE1hc3NCb3hwbG90XyIsZm9ybWF0KFN5cy50aW1lKCksIiVZJW0lZC0lSGglTW0iKSwiLmpwZyIsc2VwPSIiKSx3aWR0aD03LGhlaWdodD01KQ0KYGBgDQojIFJvb3QgbWFzcyBvdmVyYWxsIGFuZCBpbmRpdmlkdWFsIGJveHBsb3QgdjINCmBgYHtyfQ0KZGF0JT4lDQogIGJpbmRfcm93cyhtdXRhdGUoZGF0LFBsYW50PSJBbGwiKSktPg0KICB0dXR1DQogdHV0dSU+JQ0KICAgZmlsdGVyKFRyZWF0bWVudCE9IkMiKS0+dHV0dQ0KDQpmb3IgKGkgaW4gdW5pcXVlKHR1dHUkUGxhbnQpKSB7DQogIHByaW50KGkpDQogIHR1dHUlPiUNCiAgICBmaWx0ZXIoUGxhbnQ9PWkpLT4NCiAgICB0b3RvDQogIGtydXNrYWwudGVzdCh0b3RvJFJvb3RNYXNzX2csYXMuZmFjdG9yKHRvdG8kVHJlYXRtZW50KSklPiUNCiAgICBwcmludCgpDQp9DQogDQogDQpyZXMgPC0gTlVMTA0KZm9yIChpIGluIHVuaXF1ZSh0dXR1JFBsYW50KSkgew0KICB0dXR1JT4lDQogICAgZmlsdGVyKFBsYW50PT1pKS0+DQogICAgdG90bw0KICAja3J1c2thbC50ZXN0KHRvdG8kUm9vdE1hc3NfZyxhcy5mYWN0b3IodG90byRUcmVhdG1lbnQpKQ0KICBrcnVza2FsbWModG90byRSb290TWFzc19nLGFzLmZhY3Rvcih0b3RvJFRyZWF0bWVudCkpIC0+DQogIHRhdGENCiAgZGlmMyA8LSB0YXRhJGRpZi5jb20kZGlmZmVyZW5jZSANCiAgbmFtZXMoZGlmMyk8LXJvd25hbWVzKHRhdGEkZGlmLmNvbSkNCiAgbXVsdGNvbXBMZXR0ZXJzKGRpZjMpLT4gdHl0eQ0KICANCiAgYmluZF9yb3dzKHJlcyxjKFBsYW50PWksdHl0eSRMZXR0ZXJzKSktPnJlcw0KfQ0KIyByZXMlPiUNCiMgICBtdXRhdGUoQz0iIiktPnJlcw0KYXBwbHkocmVzLDEsZnVuY3Rpb24oZngpIGxlbmd0aCh1bmlxdWUoZngpKSktPmluZA0KcmVzW2luZD09MiwyOjddPC0iIg0KcmVzJT4lDQogIHBpdm90X2xvbmdlcigyOjcsbmFtZXNfdG89IlRyZWF0bWVudCIsdmFsdWVzX3RvID0gIlN5bWJvbCIpLT4NCiAgcmVzMg0KdHV0dSU+JQ0KICBncm91cF9ieShQbGFudCxUcmVhdG1lbnQpJT4lDQogIHN1bW1hcmlzZShSb290TWFzc19nPW1heChSb290TWFzc19nLG5hLnJtPVQpKSU+JQ0KICBsZWZ0X2pvaW4ocmVzMiklPiUNCiAgYXMuZGF0YS5mcmFtZSgpLT4NCiAgc2lnbGV0dGVyDQoNCnR1dHUlPiUNCiAgZ2dwbG90KGFlcyhUcmVhdG1lbnQsUm9vdE1hc3NfZykpKw0KICBnZW9tX2JveHBsb3Qoc2l6ZT0xLG91dGxpZXIuc2l6ZSA9IDIpKw0KICAjZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVBsYW50KSxhbHBoYT0wLjUsb3V0bGllci5zaGFwZSA9ICIiKSsNCiAgI3NjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz13ZXNfcGFsZXR0ZSg3LCBuYW1lID0gIlppc3NvdTEiLCB0eXBlID0gImNvbnRpbnVvdXMiKSkrDQogICNnZW9tX3Zpb2xpbigpKw0KICAjZ2VvbV9saW5lKCkrDQogICNnZW9tX2ppdHRlcih3aWR0aCA9IDAuMTUsYWVzKGNvbG91cj1QbGFudCxzaGFwZT1QbGFudCkpKw0KICBmYWNldF93cmFwKH5QbGFudCxuY29sPTQpKw0KICBnZW9tX3RleHQoZGF0YT1zaWdsZXR0ZXIsYWVzKFRyZWF0bWVudCxSb290TWFzc19nKzAuMDUsbGFiZWw9U3ltYm9sKSkrDQogIHlsYWIoIkRyeSBSb290IE1hc3MgKGcpIikrDQogIHNjYWxlX3hfZGlzY3JldGUoZ3VpZGUgPSBndWlkZV9heGlzKG4uZG9kZ2UgPSAyKSkgDQpnZ3NhdmUocGFzdGUoIlJvb3RNYXNzQm94cGxvdF9WM18iLGZvcm1hdChTeXMudGltZSgpLCIlWSVtJWQtJUhoJU1tIiksIi5qcGciLHNlcD0iIiksd2lkdGg9NyxoZWlnaHQ9NSkNCmBgYA0KDQoNCiMgS3J1c2thbCBUZXN0IHdpdGhpbiBwbGFudHMgYWNyb3NzIHRyZWF0bWVudHMNCmBgYHtyfQ0KZGF0JT4lDQogIGZpbHRlcihUcmVhdG1lbnQhPSJDIiklPiUNCiAgZ3JvdXBfYnkoUGxhbnQpJT4lDQogIGRvKHRpZHkoa3J1c2thbC50ZXN0KHggPSAuJFJvb3RNYXNzX2csIGcgPSAuJFRyZWF0bWVudCkpKQ0KYGBgDQoNCiMgUm9vdGluZyBmcmFjdGlvbiBwZXIgcGxhbnQgYWNyb3NzIHRyZWF0bWVudHMNCmBgYHtyfQ0KKGRhdCU+JQ0KICBmaWx0ZXIoVHJlYXRtZW50IT0iQyIpJT4lDQogIGdyb3VwX2J5KFRyZWF0bWVudCxQbGFudCklPiUNCiAgc3VtbWFyaXNlKFJvb3RlZD1zdW0oUm9vdE1hc3NfZz4wLG5hLnJtPVQpLA0KICAgICAgICAgICAgbj1uKCksDQogICAgICAgICAgICBSb290ZWRGcmFjPVJvb3RlZC9uKjEwMCktPmZvcl9zdGF0cyklPiUNCiAgdW5ncm91cCgpJT4lDQogIGdncGxvdChhZXMoVHJlYXRtZW50LFJvb3RlZEZyYWMsbGFiZWw9UGxhbnQpKSsNCiAgI2dlb21fdmlvbGluKCkrDQogIGdlb21fbGluZShjb2xvdXI9ImdyZXkiLHNpemU9MykrDQogIGdlb21fdGV4dF9yZXBlbChjb2xvdXI9ImRhcmtncmV5Iixmb250ZmFjZT0zKSsNCiAgZ2VvbV9wb2ludChzaXplPTMpKw0KICB5bGltKGMoMCwxMDApKSsNCiAgeWxhYigiUm9vdGVkIHBlcmNlbnRhZ2UgcGVyIHBsYW50IikrDQogIHhsYWIoIkRheSBvZiB3YXRlcmluZyIpKw0KICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbihwYXN0ZShpdGFsaWMoIlBvcnR1bGFjYXJpYSBhZnJhIiksIiByb290aW5nIHdpbmRvdyBleHBlcmltZW50IikpLCANCiAgICAgICBzdWJ0aXRsZSA9ICJSb290aW5nIHBlcmNlbnRhZ2UgcGVyIHBsYW50IGZvciBkYXkgb2Ygd2F0ZXJpbmcgZHVyaW5nIHRoZSBmaXJzdCBtb250aFxuDQogICAgICAgU2V2ZW4gcGxhbnRzIHdlcmUgaGFydmVzdGVkIGFuZCB0aGUgZGF5IG9mIHdhdGVyaW5nIGluIHRoZSBmaXJzdCBtb250aCBpcyBzaG93bi4gMTIgY3V0dGluZ3MgcGVyIHBsYW50LiIsIA0KICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiU3Bla2Jvb20gUmVzdG9yYXRpb24gUmVzZWFyY2ggR3JvdXAsIE5lbHNvbiBNYW5kZWxhIFVuaXZlcnNpdHksIDIwMjFcbg0KICAgICAgIEZpZ3VyZSBnZW5lcmF0ZWQgYnkgQWxhc3RhaXIuUG90dHNAbWFuZGVsYS5hYy56YSAoIixTeXMudGltZSgpLCIpIixzZXA9IiIpKQ0KZ2dzYXZlKCJSb290aW5nV2luZG93LVJvb3RQZXJjZW50LmpwZyIsd2lkdGg9OCxoZWlnaHQ9NSkNCmBgYA0KDQpgYGB7cn0NCihkYXQlPiUNCiAgZmlsdGVyKFRyZWF0bWVudCE9IkMiKSU+JQ0KICBncm91cF9ieShUcmVhdG1lbnQsUGxhbnQpJT4lDQogIHN1bW1hcmlzZShSb290ZWQ9c3VtKFJvb3RNYXNzX2c+MCxuYS5ybT1UKSwNCiAgICAgICAgICAgIG49bigpLA0KICAgICAgICAgICAgUm9vdGVkRnJhYz1Sb290ZWQvbioxMDApLT5mb3Jfc3RhdHMpJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICBnZ3Bsb3QoYWVzKFRyZWF0bWVudCxSb290ZWRGcmFjLGxhYmVsPVBsYW50KSkrDQogICNnZW9tX3Zpb2xpbigpKw0KICBnZW9tX2xpbmUoY29sb3VyPSJncmV5IixzaXplPTMpKw0KICBnZW9tX3RleHRfcmVwZWwoY29sb3VyPSJkYXJrZ3JleSIsZm9udGZhY2U9MykrDQogIGdlb21fcG9pbnQoc2l6ZT0zKSsNCiAgeWxpbShjKDAsMTAwKSkrDQogIHlsYWIoIlJvb3RlZCBwZXJjZW50YWdlIHBlciBwbGFudCIpKw0KICB4bGFiKCJEYXkgb2Ygd2F0ZXJpbmciKQ0KZ2dzYXZlKCJGaWcxX1B1YmxpY2F0aW9uX1Jvb3RpbmdXaW5kb3ctUm9vdFBlcmNlbnQuanBnIix3aWR0aD03LGhlaWdodD00KQ0KYGBgDQoNCg0KIyBNZWFuIHJvb3RpbmcgZnJhY3Rpb24gcGVyIHBsYW50IGZvciBlYWNoIHRyZWF0bWVudA0KYGBge3J9DQooZGF0JT4lDQogICAgZmlsdGVyKFRyZWF0bWVudCE9IkMiKSU+JQ0KICAgIGdyb3VwX2J5KFRyZWF0bWVudCxQbGFudCklPiUNCiAgICBzdW1tYXJpc2UoUm9vdGVkPXN1bShSb290TWFzc19nPjAsbmEucm09VCksDQogICAgICAgICAgICAgIG49bigpLA0KICAgICAgICAgICAgICB1Um9vdE1hc3M9bWVhbihSb290TWFzc19nLG5hLnJtPVQpLA0KICAgICAgICAgICAgICBtZWRSb290TWFzcz1tZWRpYW4oUm9vdE1hc3NfZykpKSU+JQ0KICB1bmdyb3VwKCklPiUNCiAgZ2dwbG90KGFlcyhUcmVhdG1lbnQsdVJvb3RNYXNzLGxhYmVsPVBsYW50KSkrDQogICNnZW9tX3Zpb2xpbigpKw0KICBnZW9tX2xpbmUoY29sb3VyPSJncmV5IixzaXplPTMpKw0KICBnZW9tX3RleHRfcmVwZWwoY29sb3VyPSJkYXJrZ3JleSIsZm9udGZhY2U9MykrDQogIGdlb21fcG9pbnQoc2l6ZT0zKSsNCiAgI3lsaW0oYygwLDEwMCkpKw0KICB5bGFiKCJNZWFuIHJvb3QgcGVyY2VudGFnZSBwZXIgcGxhbnQiKSsNCiAgeGxhYigiRGF5IG9mIHdhdGVyaW5nIikgIA0KYGBgDQoNCmBgYHtyfQ0KZGF0JT4lDQogICAgZmlsdGVyKFRyZWF0bWVudCE9IkMiKSU+JQ0KICAgIGdyb3VwX2J5KFRyZWF0bWVudCxQbGFudCklPiUNCiAgICBzdW1tYXJpc2UoUm9vdGVkPXN1bShSb290TWFzc19nPjAsbmEucm09VCksDQogICAgICAgICAgICAgIG49bigpLA0KICAgICAgICAgICAgICB1Um9vdE1hc3M9bWVhbihSb290TWFzc19nKSwNCiAgICAgICAgICAgICAgbWVkUm9vdE1hc3M9bWVkaWFuKFJvb3RNYXNzX2csbmEucm09VCkpJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICBnZ3Bsb3QoYWVzKFRyZWF0bWVudCxtZWRSb290TWFzcyxsYWJlbD1QbGFudCkpKw0KICAjZ2VvbV92aW9saW4oKSsNCiAgZ2VvbV9saW5lKGNvbG91cj0iZ3JleSIsc2l6ZT0zKSsNCiAgZ2VvbV90ZXh0X3JlcGVsKGNvbG91cj0iZGFya2dyZXkiLGZvbnRmYWNlPTMsbWluLnNlZ21lbnQubGVuZ3RoID0gMCkrDQogIGdlb21fcG9pbnQoc2l6ZT0zKSsNCiAgI3lsaW0oYygwLDEwMCkpKw0KICB5bGFiKCJNZWRpYW4gcm9vdCBwZXJjZW50YWdlIHBlciBwbGFudCIpKw0KICB4bGFiKCJEYXkgb2Ygd2F0ZXJpbmciKSAgDQpgYGANCg0KI1N1cGxlbWVudGFyeSBnbGFzc2hvdXNlIGVudmlyb25tZW50YWwgZGF0YSBhbmFseXNpcyAoLkNTViBmaWxlcyBub3QgcHJvdmlkZWQsIGlmIHlvdSB3b3VsZCBsaWtlIHRvIHJ1biB0aGlzIHNjcmlwdCBwbGVhc2UgZ2VuZXJhdGUgdGhlIHJlbGV2YW50IGRhdGEgc2V0cyB1c2luZyAiUm9vdGluZ1dpbmRvd19GdWxsRGF0YSIgcHJvdmlkZWQpDQpgYGB7cn0NCnJlYWRfY3N2KGNvbnN0cnVjdF9kb3dubG9hZF91cmwodXJsLHNoZWV0aWQ9IjgzODUyMDg2NyIpKSU+JQ0KICB0YmxfZGYoKS0+DQogIGRhdF90ZW1wDQoNCnJlYWRfY3N2KGNvbnN0cnVjdF9kb3dubG9hZF91cmwodXJsLHNoZWV0aWQ9IjE0NDQ3NDA0ODkiKSklPiUNCiAgdGJsX2RmKCktPg0KICBkYXRfdHJlYXQNCg0KZGF0X3RyZWF0JT4lDQogIG11dGF0ZShEYXRlPW1keV9obShwYXN0ZShEYXRlLCIxMWg1OSIpKSktPg0KICBkYXRfdHJlYXQyDQoNCg0KDQpkYXRfdGVtcCU+JQ0KICBtdXRhdGUoVGltZXN0YW1wPXltZF9obShUaW1lc3RhbXApKSU+JQ0KICBnZ3Bsb3QoYWVzKFRpbWVzdGFtcCxUZW1wZXJhdHVyZSkpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBkYXRfdHJlYXQyJERhdGUsY29sb3VyPSJkYXJrZ3JleSIsc2l6ZT0xKSsNCiAgZ2VvbV9saW5lKCkrDQogIHlsaW0oMCwzNikrDQogIHNjYWxlX3hfZGF0ZXRpbWUoc2VjLmF4aXMgPSBzZWNfYXhpcyh+LixicmVha3M9ZGF0X3RyZWF0MiREYXRlLGxhYmVscz1kYXRfdHJlYXQyJFRyZWF0bWVudCkpKw0KICB4bGFiKCIiKSsNCiAgeWxhYigiVGVtcGVyYXR1cmUgKMKwQykgIikrDQogIHRoZW1lKGF4aXMudGV4dC54LnRvcCA9ZWxlbWVudF90ZXh0KGNvbG9yPSJkYXJrZ3JleSIpKSsNCiAgZ2VvbV9saW5lKGFlcyhUaW1lc3RhbXAsRGV3UG9pbnRfZEMpLGNvbG91cj0iYmxhY2siLGxpbmV0eXBlPTIpLT4NCiAgQQ0KICANCmRhdF90ZW1wJT4lDQogIGdncGxvdChhZXMoIiIsVGVtcGVyYXR1cmUpKSsNCiAgZ2VvbV9ib3hwbG90KCkrDQogIHlsaW0oMCwzNikrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKS0+DQogIEINCg0KZGF0X3RlbXAlPiUNCiAgbXV0YXRlKFRpbWVzdGFtcD15bWRfaG0oVGltZXN0YW1wKSklPiUNCiAgZ2dwbG90KGFlcyhUaW1lc3RhbXAsUmVsYXRpdmVIdW1pZGl0eSkpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBkYXRfdHJlYXQyJERhdGUsY29sb3VyPSJkYXJrZ3JleSIsc2l6ZT0xKSsNCiAgZ2VvbV9saW5lKCkrDQogIHlsaW0oMCwxMDApKw0KICBzY2FsZV94X2RhdGV0aW1lKHNlYy5heGlzID0gc2VjX2F4aXMofi4sYnJlYWtzPWRhdF90cmVhdDIkRGF0ZSxsYWJlbHM9ZGF0X3RyZWF0MiRUcmVhdG1lbnQpKSsNCiAgdGhlbWUoYXhpcy50ZXh0LngudG9wID1lbGVtZW50X3RleHQoY29sb3I9ImRhcmtncmV5IikpKw0KICB4bGFiKCIiKSsNCiAgeWxhYigiUmVsYXRpdmUgaHVtaWRpdHkgKCUpIiktPg0KICBDDQoNCg0KKEErQikvQw0KQS9DK3Bsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gJ0EnKQ0KZ2dzYXZlKCJFbnYuY29uZGl0aW9ucy5qcGciLHdpZHRoPTgsaGVpZ2h0PTUuNSkgIA0KDQpkYXRfdGVtcCU+JQ0KICBtdXRhdGUoVGltZXN0YW1wPXltZF9obShUaW1lc3RhbXApLA0KICAgICAgICAgRGF5PWRhdGUoVGltZXN0YW1wKSklPiUNCiAgZ3JvdXBfYnkoRGF5KSU+JQ0KICBzdW1tYXJpc2UobWF4VD1tYXgoVGVtcGVyYXR1cmUpLA0KICAgICAgICAgICAgbWluVD1taW4oVGVtcGVyYXR1cmUpKSU+JQ0KICB1bmdyb3VwKCklPiUNCiAgc3VtbWFyaXNlKG1lYW4obWF4VCksDQogICAgICAgICAgICBtZWFuKG1pblQpLA0KICAgICAgICAgICAgc2QobWF4VCksDQogICAgICAgICAgICBzZChtaW5UKSkNCmRhdF90ZW1wJT4lDQogIHN1bW1hcmlzZShtZWFuKFJlbGF0aXZlSHVtaWRpdHkpLHNkKFJlbGF0aXZlSHVtaWRpdHkpKQ0KZGF0X3RlbXAlPiUNCiAgZ3JvdXBfYnkoRGEpDQogIHN1bW1hcmlzZSh1VD1tZWFuKFRlbXBlcmF0dXJlKSkNCg0KYGBgDQoNCg==