home

load libraries


### https://cran.r-project.org/web/packages/udpipe/vignettes/udpipe-usecase-postagging-lemmatisation.html
library(udpipe)
ud_model <- udpipe_download_model(language = "english")
library(tidyverse)
library(tidyr)
library(dplyr)
library(ggplot2)
library(ggrepel)
library(knitr)
library(tm)
library(quanteda)
library(lattice)
library(latticeExtra)
library(plotly)
library(pdp)
library(patchwork)

load the FW functions


### CODE DIRECTLY FROM: https://burtmonroe.github.io/TextAsDataCourse/Tutorials/TADA-FightinWords.nb.html#

fwgroups <- function(dtm, groups, pair = NULL, weights = rep(1,nrow(dtm)), k.prior = .1) {
  
  weights[is.na(weights)] <- 0
  
  weights <- weights/mean(weights)
  
  zero.doc <- rowSums(dtm)==0 | weights==0
  zero.term <- colSums(dtm[!zero.doc,])==0
  
  dtm.nz <- apply(dtm[!zero.doc,!zero.term],2,"*", weights[!zero.doc])
  
  g.prior <- tcrossprod(rowSums(dtm.nz),colSums(dtm.nz))/sum(dtm.nz)
  
  # 
  
  g.posterior <- as.matrix(dtm.nz + k.prior*g.prior)
  
  groups <- groups[!zero.doc]
  groups <- droplevels(groups)
  
  g.adtm <- as.matrix(aggregate(x=g.posterior,by=list(groups=groups),FUN=sum)[,-1])
  rownames(g.adtm) <- levels(groups)
  
  g.ladtm <- log(g.adtm)
  
  g.delta <- t(scale( t(scale(g.ladtm, center=T, scale=F)), center=T, scale=F))
  
  g.adtm_w <- -sweep(g.adtm,1,rowSums(g.adtm)) # terms not w spoken by k
  g.adtm_k <- -sweep(g.adtm,2,colSums(g.adtm)) # w spoken by groups other than k
  g.adtm_kw <- sum(g.adtm) - g.adtm_w - g.adtm_k - g.adtm # total terms not w or k 
  
  g.se <- sqrt(1/g.adtm + 1/g.adtm_w + 1/g.adtm_k + 1/g.adtm_kw)
  
  g.zeta <- g.delta/g.se
  
  g.counts <- as.matrix(aggregate(x=dtm.nz, by = list(groups=groups), FUN=sum)[,-1])
  
  if (!is.null(pair)) {
    pr.delta <- t(scale( t(scale(g.ladtm[pair,], center = T, scale =F)), center=T, scale=F))
    pr.adtm_w <- -sweep(g.adtm[pair,],1,rowSums(g.adtm[pair,]))
    pr.adtm_k <- -sweep(g.adtm[pair,],2,colSums(g.adtm[pair,])) # w spoken by groups other than k
    pr.adtm_kw <- sum(g.adtm[pair,]) - pr.adtm_w - pr.adtm_k - g.adtm[pair,] # total terms not w or k
    pr.se <- sqrt(1/g.adtm[pair,] + 1/pr.adtm_w + 1/pr.adtm_k + 1/pr.adtm_kw)
    pr.zeta <- pr.delta/pr.se
    
    return(list(zeta=pr.zeta[1,], delta=pr.delta[1,],se=pr.se[1,], counts = colSums(dtm.nz), acounts = colSums(g.adtm)))
  } else {
    return(list(zeta=g.zeta,delta=g.delta,se=g.se,counts=g.counts,acounts=g.adtm))
  }
}

############## FIGHTIN' WORDS PLOTTING FUNCTION

# helper function
makeTransparent<-function(someColor, alpha=100)
{
  newColor<-col2rgb(someColor)
  apply(newColor, 2, function(curcoldata){rgb(red=curcoldata[1], green=curcoldata[2],
                                              blue=curcoldata[3],alpha=alpha, maxColorValue=255)})
}

fw.ggplot.groups <- function(fw.ch, groups.use = as.factor(rownames(fw.ch$zeta)), max.words = 50, max.countrank = 400, colorpalette=rep("black",length(groups.use)), sizescale=2, title="Comparison of Terms by Groups", subtitle = "", caption = "Group-specific terms are ordered by Fightin' Words statistic (Monroe, et al. 2008)") {
  if (is.null(dim(fw.ch$zeta))) {## two-group fw object consists of vectors, not matrices
    zetarankmat <- cbind(rank(-fw.ch$zeta),rank(fw.ch$zeta))
    colnames(zetarankmat) <- groups.use
    countrank <- rank(-(fw.ch$counts))
  } else {
    zetarankmat <- apply(-fw.ch$zeta[groups.use,],1,rank)
    countrank <- rank(-colSums(fw.ch$counts))
  }
  wideplotmat <- as_tibble(cbind(zetarankmat,countrank=countrank))
  wideplotmat$term=names(countrank)
  #rankplot <- gather(wideplotmat, party, zetarank, 1:ncol(zetarankmat))
  rankplot <- gather(wideplotmat, groups.use, zetarank, 1:ncol(zetarankmat))
  rankplot$plotsize <- sizescale*(50/(rankplot$zetarank))^(1/4)
  rankplot <- rankplot[rankplot$zetarank < max.words + 1 & rankplot$countrank<max.countrank+1,]
  rankplot$groups.use <- factor(rankplot$groups.use,levels=groups.use)
  
  p <- ggplot(rankplot, aes((nrow(rankplot)-countrank)^1, -(zetarank^1), colour=groups.use)) + 
    geom_point(show.legend=F,size=sizescale/2) + 
    theme_classic() +
    theme(axis.ticks=element_blank(), axis.text=element_blank() ) +
    ylim(-max.words,40) +
    facet_grid(groups.use ~ .) +
    geom_text_repel(aes(label = term), size = rankplot$plotsize, point.padding=.05,
                    box.padding = unit(0.20, "lines"), show.legend=F, max.overlaps = Inf) +
    scale_colour_manual(values = alpha(colorpalette, .7)) + 
#    labs(x="Terms used more frequently overall →", y="Terms used more frequently by group →",  title=title, subtitle=subtitle , caption = caption) 
    labs(x=paste("Terms used more frequently overall -->"), y=paste("Terms used more frequently by group -->"),  title=title, subtitle=subtitle , caption = caption) 
  
}

options(ggrepel.max.overlaps = Inf)

fw.keys <- function(fw.ch,n.keys=10) {
  n.groups <- nrow(fw.ch$zeta)
  keys <- matrix("",n.keys,n.groups)
  colnames(keys) <- rownames(fw.ch$zeta)
  
  for (g in 1:n.groups) {
    keys[,g] <- names(sort(fw.ch$zeta[g,],dec=T)[1:n.keys])
  }
  keys
}

Compare Associated Press 1994-2010: Before and After “extremist” and other query terms

Query search: (activi* | ahbash | akromiya | anjem | ansharut | anticapital* | antidemocr* | antiestablish* | antifa | antigovern* | antimilitar* | antimonarch* | antipatri* | antireli* | antisem* | antisocia* | antisyst* | apost* | atharis | athei* | atheists | außerparlamentari* | authoritar* | bagau* | bigots | bplf | bukhari* | capitulatio* | conspirato* | counterj* | cybercalip* | damigo | dawro* | demon* | deradicaliza* | deviatio* | diqqi | dissid* | djamaat | dotbus* | ecofas* | espou* | ethnonationa* | extrem* | facists | fadaia* | fanat* | fasci* | fetö | fightdem* | freedo* | fundamental* | fuqra | gafatar | gamerga* | gemidzii | ghuluww | globali* | gramsc* | gülen* | hacktiv* | haquna | hardline | harkatul | hatemon* | heimwe* | hezbol* | hinduph* | hindutva | hizbut | hojja* | ideolog* | incitem* | inciters | insurr* | intacti* | islam4uk | islam* | jaljalat | jbakc | jihadi* | jmjb | jrtn | judai* | juhayman | jundu* | kadiza* | kahanism | kahanist | karram* | kaysa* | khalis* | khatmia | khawarij | khomein* | koutla | leftist | leftists | leftwing | liberatio* | madkha* | madkhal* | maimonid* | manosp* | mauras* | mcln | militan* | millatu | monarc* | mudja* | muhaji* | mujahid* | murab* | muttahi* | najjadah | nationali* | neofas* | neona* | opantish | oppositi* | paleolibertar* | paramili* | parliamenta* | pegida | populist | principa* | profe* | prowar | putinist | qadari | quranism | quranist | qutbism | qutbist | qutbists | racis* | radica* | reactioni* | reformis* | reichsbürgerbewe* | rightist | rightw* | rofiq | russoph* | sabireen | sadda* | salaf* | sayaff | scriptura* | secula* | separationi* | sharia4hol* | sikrikim | split* | squadism | strasse* | subver* | suidlan* | sukarn* | suprema* | supremac* | sympathi* | table* | tabliq | takfir | takfir* | takfi* |terror* | theoc* | titoite | triba* | trots* | trotsk* | ukrainoph* | ultraconserva* | ultralib* | ultranationa* | ultrar* | uscmo | wahabbi | wahab* | wahha* | xenoph* | yulde* | zinovie*)

Load and clean the data

text_cleaner<-function(corpus){
  tempcorpus<-Corpus(VectorSource(corpus))
  tempcorpus<-tm_map(tempcorpus,
                    removePunctuation)
  tempcorpus<-tm_map(tempcorpus,
                    stripWhitespace)
  tempcorpus<-tm_map(tempcorpus,
                    removeNumbers)
  tempcorpus<-tm_map(tempcorpus,
                     removeWords, stopwords("english"))
  tempcorpus<-tm_map(tempcorpus, 
                    stemDocument)
  return(tempcorpus)
}

Calculate FW.


e <- dfm(extremecorpus$content)
message(dim(e))
head(e)
Document-feature matrix of: 6 documents, 15,213 features (100.0% sparse).
       features
docs    us struggl uptick american plot attack past month last week
  text1  1       1      1        1    1      0    0     0    0    0
  text2  0       0      0        0    0      1    1     1    1    1
  text3  0       0      0        0    0      0    0     0    0    0
  text4  0       0      0        0    0      0    0     0    0    0
  text5  0       0      0        0    0      0    0     0    0    0
  text6  0       0      0        0    0      0    0     0    0    0
[ reached max_nfeat ... 15,203 more features ]
#############################################

e <- dfm_select(e, pattern = stopwords("english"), selection = "remove")
e <- dfm_select(e, min_nchar = 2)
e <- dfm_trim(e, min_termfreq = 4, min_docfreq = .05, verbose=TRUE)

#dim(e)
# sparsity(e)

#############################################

extrem_dtm <- convert(e, to='data.frame')
extrem_dtm <- extrem_dtm[-c(1)]
w <- which( sapply(extrem_dtm, class ) == 'character' )

#############################################

fw.extrem <- fwgroups(extrem_dtm, groups=extrem_NYT.dfm.long$Context)

rm(extrem_dtm)

Get and show the top words per group by zeta.


fwkeys.extrem <- fw.keys(fw.extrem, n.keys=20)
cols <- rev(colnames(fwkeys.extrem))
fwkeys.extrem <- fwkeys.extrem[,cols]
kable(fwkeys.extrem)
Context.before Context.after
islamic group
palestinian hussein
war attack
iraqi parti
suspect elect
crack leader
jemaah jihad
crackdown milit
main movement
alleg said
support guerrilla
evid network
albanian regim
prodemocraci armi
sept organ
megawati politician
muslim outsid
prevent univers
kosovo bomb
battl rocket
NA

Plot: Before in Blue, After in Red


p.fw.extrem <- fw.ggplot.groups(fw.extrem,sizescale=4,max.words=200,
                                max.countrank=400,colorpalette=c("red","blue"),
                                title = 'Comparison of Terms Before and After Query Word')
p.fw.extrem

Calculate by query item/search term


extrem_NYT.dfm.long$Query.item <- as.factor(extrem_NYT.dfm.long$Query.item)

top_n <-as.data.frame(sort(table(extrem_NYT.dfm.long$Query.item), decreasing = TRUE)[1:5]) 
message(dim(top_n))
colnames(top_n) <- c('term', 'Freq')
message(top_n)

extrem_dtm_topn <- convert(e, to='data.frame')
extrem_dtm_topn$Number.of.hit <- extrem_NYT.dfm.long$Number.of.hit

topn_terms <- extrem_NYT.dfm.long %>%
      filter(Query.item %in% top_n$term)

extrem_dtm_topn_keep <- extrem_dtm_topn %>% 
    filter(Number.of.hit %in% topn_terms$Number.of.hit)

r <- sum(length(extrem_dtm_topn_keep))

extrem_dtm_topn_keep <- extrem_dtm_topn_keep[-c(1, r)]

fw.query_item <- fwgroups(extrem_dtm_topn_keep,groups = topn_terms$Query.item)
fwkeys.query_item <- fw.keys(fw.query_item, n.keys=15)
kable(fwkeys.query_item)
Islamic militants opposition Saddam terrorist
milit islamic parti hussein attack
hama palestinian democrat iraqi organ
jihad kill parliament iraq sept
group israeli leader presid unite
movement suspect elect regim state
extremist gaza vote saddam activ
front israel opposit baghdad alqaida
republ attack lawmak un act
insurg kashmir polit kuwait network
radic taliban main war group
law arafat candid captur suspect
fundamentalist forc coalit oust laden
revolut pakistani conserv weapon bin
somalia fire allianc bush connect
hardlin pakistan protest us financ

rm(r)
rm(extrem_dtm_topn)

########################################################

p.fw.query_item <- fw.ggplot.groups(fw.query_item,sizescale=3.2,max.words=150,max.countrank=400,
                                    colorpalette=c("darkgreen","darkgreen","darkgreen","darkgreen","darkgreen"),
                                    title = 'Comparison of Terms by Overall Top Terms')
p.fw.query_item


extrem_dtm_topn <- convert(e, to='data.frame')
extrem_dtm_topn$Number.of.hit <- extrem_NYT.dfm.long$Number.of.hit
extrem_dtm_topn$Context <- extrem_NYT.dfm.long$Context

topn_terms <- extrem_NYT.dfm.long %>%
      filter(Query.item %in% top_n$term)

extrem_dtm_topn_keep <- extrem_dtm_topn %>% 
    filter(Number.of.hit %in% topn_terms$Number.of.hit)

extrem_dtm_topn_keep_before <- extrem_dtm_topn_keep[grep("before",extrem_dtm_topn_keep$Context),]
extrem_dtm_topn_keep_after <- extrem_dtm_topn_keep[grep("after", extrem_dtm_topn_keep$Context),]

rr <-dim(topn_terms)[1]
r <- sum(length(extrem_dtm_topn_keep_before))
topn_terms_before <- topn_terms[seq(1,rr,2),]
topn_terms_after <- topn_terms[seq(2,rr,2),]

extrem_dtm_topn_keep_before <- extrem_dtm_topn_keep_before[-c(1, r-1, r)]
extrem_dtm_topn_keep_after <- extrem_dtm_topn_keep_after[-c(1, r-1, r)]

rm(rr)
rm(r)
rm(extrem_dtm_topn)

#############################################
fw.query_item_before <- fwgroups(extrem_dtm_topn_keep_before,groups = topn_terms_before$Query.item)
fwkeys.query_item_before <- fw.keys(fw.query_item_before, n.keys=15)
kable(fwkeys.query_item_before, caption = "Top 15 Words for Query Term: BEFORE")
Top 15 Words for Query Term: BEFORE
Islamic militants opposition Saddam terrorist
milit islamic main iraqi sept
strict palestinian elect presid alqaida
radic kill polit iraq unite
somalia suspect parliament war bin
hama taliban opposit saddam laden
organiz attack parti oust involv
secular israeli minist baghdad suspect
hardlin muslim vote fall link
iran gaza democrat bush state
turkey armi prime hussein consid
extremist troop strong captur connect
malaysia israel percent regim appar
suprem pakistan despit usled osama
gaza forc poll former charg
fundamentalist arafat voic death list

p.fw.query_item_before <- fw.ggplot.groups(fw.query_item_before,sizescale=2,max.words=150,max.countrank=400,
                                           colorpalette = c('blue','blue','blue', 'blue','blue'),
                                           title = 'Comparison of Terms by Overall Top Terms: BEFORE')
p.fw.query_item_before


#############################################
fw.query_item_after <- fwgroups(extrem_dtm_topn_keep_after,groups = topn_terms_after$Query.item)
fwkeys.query_item_after <- fw.keys(fw.query_item_after, n.keys=15)
kable(fwkeys.query_item_after, caption = "Top 15 Words for Query Term: AFTER")
Top 15 Words for Query Term: AFTER
Islamic militants opposition Saddam terrorist
milit israeli parti hussein attack
group israel leader regim organ
jihad kashmir democrat iraq unite
hama gaza vote iraqi state
movement palestinian parliament un activ
law kill lawmak weapon group
insurg attack protest saddam act
extremist region candid son network
front southern politician captur bomb
revolut fire coalit kuwait suspect
republ rocket elect mass threat
islamic pakistani social baghdad financ
court fight conserv us sept
fundamentalist border win resolut link
radic area opposit palac cell

p.fw.query_item_after <- fw.ggplot.groups(fw.query_item_after,sizescale=2,max.words=150,max.countrank=400,
                                           colorpalette = c('red', 'red','red','red','red'),
                                          title = 'Comparison of Terms by Overall Top Terms: AFTER')
p.fw.query_item_after

NA
NA

Calculate Parts of speech by before and after

Calculate FW and keys


ud_model <- udpipe_load_model(ud_model$file_model)

txt <-as.character(extrem_NYT.dfm.long$context.text)

x_udp <- udpipe_annotate(ud_model, x = txt, doc_id = seq_along(txt))
x <- as.data.frame(x_udp)

x$doc_id <-as.integer(x$doc_id)

x_odd.before <- x[x$doc_id %% 2 == 1,]
x_even.after <-x[x$doc_id %% 2 == 0, ]

A few barchart functions


## UNIVERSAL PoS
UPOS_barchart <- function(df1, df2){
  stats1 <- txt_freq(df1$upos)
  stats1$key <- factor(stats1$key, levels = rev(stats1$key))
  
  stats2 <- txt_freq(df2$upos)
  stats2$key <- factor(stats2$key, levels = rev(stats2$key))
  
  c(barchart(key ~ freq, data = stats1, col = "cadetblue", 
        main = "UPOS (Universal Parts of Speech)\n frequency of occurrence: BEFORE vs AFTER", 
         xlab = "Freq"), 
    barchart(key ~ freq, data = stats2, col =  'skyblue',
         xlab = "Freq"))
}



## NOUNS
NOUNS_barchart <- function(df1, df2){
  
  stats1 <- subset(df1, upos %in% c("NOUN")) 
  stats1 <- txt_freq(stats1$token)
  stats1$key <- factor(stats1$key, levels = rev(stats1$key))
  
  stats2 <- subset(df2, upos %in% c("NOUN")) 
  stats2 <- txt_freq(stats2$token)
  stats2$key <- factor(stats2$key, levels = rev(stats2$key))
  
  c(barchart(key ~ freq, data = head(stats1, 20), col = "cadetblue", 
           main = "Most occurring nouns: BEFORE vs AFTER", xlab = "Freq"),
      barchart(key ~ freq, data = head(stats2, 20), col = "skyblue", 
            xlab = "Freq"))
}

## ADJECTIVES
ADJ_barchart <- function(df1, df2){
  
  stats1 <- subset(df1, upos %in% c("ADJ")) 
  stats1 <- txt_freq(stats1$token)
  stats1$key <- factor(stats1$key, levels = rev(stats1$key))
  
  stats2 <- subset(df2, upos %in% c("ADJ")) 
  stats2 <- txt_freq(stats2$token)
  stats2$key <- factor(stats2$key, levels = rev(stats2$key))
  
  c(barchart(key ~ freq, data = head(stats1, 20), col = "cadetblue", 
           main = "Most occurring adjectives: BEFORE vs AFTER", xlab = "Freq"),
      barchart(key ~ freq, data = head(stats2, 20), col = "skyblue", 
         xlab = "Freq"))
}

## Using RAKE to find keywords
RAKE_KW_barchart <- function(df1,df2){
  
  stats1 <- keywords_rake(x = df1, term = "lemma", group = "doc_id", 
                         relevant = df1$upos %in% c("NOUN", "ADJ"))
  stats1$key <- factor(stats1$keyword, levels = rev(stats1$keyword))
  
  stats2 <- keywords_rake(x = df2, term = "lemma", group = "doc_id", 
                         relevant = df2$upos %in% c("NOUN", "ADJ"))
  stats2$key <- factor(stats2$keyword, levels = rev(stats2$keyword))
  
  
  c(barchart(key ~ rake, data = head(subset(stats1, freq > 3), 20), col = "cadetblue", 
           main = "Keywords identified by RAKE: BEFORE vs AFTER", 
           xlab = "Rake"),
    barchart(key ~ rake, data = head(subset(stats2, freq > 3), 20), col = "skyblue", 
           xlab = "Rake"))
}

## Using Pointwise Mutual Information Collocations
PWI_barchart <- function(df1, df2){
  
  df1$word <- tolower(df1$token)
  stats1 <- keywords_collocation(x = df1, term = "word", group = "doc_id")
  stats1$key <- factor(stats1$keyword, levels = rev(stats1$keyword))
  
  df2$word <- tolower(df2$token)
  stats2 <- keywords_collocation(x = df2, term = "word", group = "doc_id")
  stats2$key <- factor(stats2$keyword, levels = rev(stats2$keyword))
  
  c(barchart(key ~ pmi, data = head(subset(stats1, freq > 3), 20), col = "cadetblue", 
           main = "Keywords identified by PMI Collocation: BEFORE vs AFTER", 
           xlab = "PMI (Pointwise Mutual Information)"),
      barchart(key ~ pmi, data = head(subset(stats2, freq > 3), 20), col = "skyblue", 
           xlab = "PMI (Pointwise Mutual Information)"))
}

## Using a sequence of POS tags (noun phrases / verb phrases)
POS_barchart <- function(df1, df2){
  
  df1$phrase_tag <- as_phrasemachine(df1$upos, type = "upos")
  stats1 <- keywords_phrases(x = df1$phrase_tag, term = tolower(df1$token), 
                            pattern = "(A|N)*N(P+D*(A|N)*N)*", 
                            is_regex = TRUE, detailed = FALSE)
  stats1 <- subset(stats1, ngram > 1 & freq > 3)
  stats1$key <- factor(stats1$keyword, levels = rev(stats1$keyword))
  
  df2$phrase_tag <- as_phrasemachine(df2$upos, type = "upos")
  stats2 <- keywords_phrases(x = df2$phrase_tag, term = tolower(df2$token), 
                            pattern = "(A|N)*N(P+D*(A|N)*N)*", 
                            is_regex = TRUE, detailed = FALSE)
  stats2 <- subset(stats2, ngram > 1 & freq > 3)
  stats2$key <- factor(stats2$keyword, levels = rev(stats2$keyword))
  
  c(barchart(key ~ freq, data = head(stats1, 20), col = "cadetblue", 
           main = "Keywords - simple noun phrases: BEFORE vs AFTER", xlab = "Frequency"),
      barchart(key ~ freq, data = head(stats2, 20), col = "skyblue", 
               xlab = "Frequency"))
}

Bar Charts from Functions Above


UPOS_barchart(x_odd.before, x_even.after)

NOUNS_barchart(x_odd.before, x_even.after)

ADJ_barchart(x_odd.before, x_even.after)

RAKE_KW_barchart(x_odd.before, x_even.after)

PWI_barchart(x_odd.before, x_even.after)

POS_barchart(x_odd.before, x_even.after)

Cooccurences

Cooccurences (part 2)


Corrs <- function(df){
  df$id <- unique_identifier(df, fields = c("sentence_id", "doc_id"))
  dtm <- subset(df, upos %in% c("NOUN", "ADJ"))
  dtm <- document_term_frequencies(dtm, document = "id", term = "lemma")
  dtm <- document_term_matrix(dtm)
  dtm <- dtm_remove_lowfreq(dtm, minfreq = 5)
  termcorrelations <- dtm_cor(dtm)
  y <- as_cooccurrence(termcorrelations)
  y <- subset(y, term1 < term2 & abs(cooc) > 0.2)
  y <- y[order(abs(y$cooc), decreasing = TRUE), ]
  print(y[1:25,])
}

Corrs(x_odd.before)

Corrs(x_even.after)
NA

rm(list=ls())

home

LS0tCnRpdGxlOiAiRXh0cmVtKGlzdCArKSBGaWdodGluJyBXb3JkcyIKYXV0aG9yOiAiQnJlYW5uYSBFLiBHcmVlbiIKc3VidGl0bGU6Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBwYWdlZAogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKLS0tCgpbaG9tZV0oaHR0cHM6Ly9icmVncmVlbi5naXRodWIuaW8vKQoKIyMgbG9hZCBsaWJyYXJpZXMKCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30KCiMjIyBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdWRwaXBlL3ZpZ25ldHRlcy91ZHBpcGUtdXNlY2FzZS1wb3N0YWdnaW5nLWxlbW1hdGlzYXRpb24uaHRtbApsaWJyYXJ5KHVkcGlwZSkKdWRfbW9kZWwgPC0gdWRwaXBlX2Rvd25sb2FkX21vZGVsKGxhbmd1YWdlID0gImVuZ2xpc2giKQoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRtKQpsaWJyYXJ5KHF1YW50ZWRhKQpsaWJyYXJ5KGxhdHRpY2UpCmxpYnJhcnkobGF0dGljZUV4dHJhKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShwZHApCmxpYnJhcnkocGF0Y2h3b3JrKQoKYGBgCgojIyBsb2FkIHRoZSBGVyBmdW5jdGlvbnMKCmBgYHtyIGxvYWRfZndfZnVuY3Rpb25zfQoKIyMjIENPREUgRElSRUNUTFkgRlJPTTogaHR0cHM6Ly9idXJ0bW9ucm9lLmdpdGh1Yi5pby9UZXh0QXNEYXRhQ291cnNlL1R1dG9yaWFscy9UQURBLUZpZ2h0aW5Xb3Jkcy5uYi5odG1sIwoKZndncm91cHMgPC0gZnVuY3Rpb24oZHRtLCBncm91cHMsIHBhaXIgPSBOVUxMLCB3ZWlnaHRzID0gcmVwKDEsbnJvdyhkdG0pKSwgay5wcmlvciA9IC4xKSB7CiAgCiAgd2VpZ2h0c1tpcy5uYSh3ZWlnaHRzKV0gPC0gMAogIAogIHdlaWdodHMgPC0gd2VpZ2h0cy9tZWFuKHdlaWdodHMpCiAgCiAgemVyby5kb2MgPC0gcm93U3VtcyhkdG0pPT0wIHwgd2VpZ2h0cz09MAogIHplcm8udGVybSA8LSBjb2xTdW1zKGR0bVshemVyby5kb2MsXSk9PTAKICAKICBkdG0ubnogPC0gYXBwbHkoZHRtWyF6ZXJvLmRvYywhemVyby50ZXJtXSwyLCIqIiwgd2VpZ2h0c1shemVyby5kb2NdKQogIAogIGcucHJpb3IgPC0gdGNyb3NzcHJvZChyb3dTdW1zKGR0bS5ueiksY29sU3VtcyhkdG0ubnopKS9zdW0oZHRtLm56KQogIAogICMgCiAgCiAgZy5wb3N0ZXJpb3IgPC0gYXMubWF0cml4KGR0bS5ueiArIGsucHJpb3IqZy5wcmlvcikKICAKICBncm91cHMgPC0gZ3JvdXBzWyF6ZXJvLmRvY10KICBncm91cHMgPC0gZHJvcGxldmVscyhncm91cHMpCiAgCiAgZy5hZHRtIDwtIGFzLm1hdHJpeChhZ2dyZWdhdGUoeD1nLnBvc3RlcmlvcixieT1saXN0KGdyb3Vwcz1ncm91cHMpLEZVTj1zdW0pWywtMV0pCiAgcm93bmFtZXMoZy5hZHRtKSA8LSBsZXZlbHMoZ3JvdXBzKQogIAogIGcubGFkdG0gPC0gbG9nKGcuYWR0bSkKICAKICBnLmRlbHRhIDwtIHQoc2NhbGUoIHQoc2NhbGUoZy5sYWR0bSwgY2VudGVyPVQsIHNjYWxlPUYpKSwgY2VudGVyPVQsIHNjYWxlPUYpKQogIAogIGcuYWR0bV93IDwtIC1zd2VlcChnLmFkdG0sMSxyb3dTdW1zKGcuYWR0bSkpICMgdGVybXMgbm90IHcgc3Bva2VuIGJ5IGsKICBnLmFkdG1fayA8LSAtc3dlZXAoZy5hZHRtLDIsY29sU3VtcyhnLmFkdG0pKSAjIHcgc3Bva2VuIGJ5IGdyb3VwcyBvdGhlciB0aGFuIGsKICBnLmFkdG1fa3cgPC0gc3VtKGcuYWR0bSkgLSBnLmFkdG1fdyAtIGcuYWR0bV9rIC0gZy5hZHRtICMgdG90YWwgdGVybXMgbm90IHcgb3IgayAKICAKICBnLnNlIDwtIHNxcnQoMS9nLmFkdG0gKyAxL2cuYWR0bV93ICsgMS9nLmFkdG1fayArIDEvZy5hZHRtX2t3KQogIAogIGcuemV0YSA8LSBnLmRlbHRhL2cuc2UKICAKICBnLmNvdW50cyA8LSBhcy5tYXRyaXgoYWdncmVnYXRlKHg9ZHRtLm56LCBieSA9IGxpc3QoZ3JvdXBzPWdyb3VwcyksIEZVTj1zdW0pWywtMV0pCiAgCiAgaWYgKCFpcy5udWxsKHBhaXIpKSB7CiAgICBwci5kZWx0YSA8LSB0KHNjYWxlKCB0KHNjYWxlKGcubGFkdG1bcGFpcixdLCBjZW50ZXIgPSBULCBzY2FsZSA9RikpLCBjZW50ZXI9VCwgc2NhbGU9RikpCiAgICBwci5hZHRtX3cgPC0gLXN3ZWVwKGcuYWR0bVtwYWlyLF0sMSxyb3dTdW1zKGcuYWR0bVtwYWlyLF0pKQogICAgcHIuYWR0bV9rIDwtIC1zd2VlcChnLmFkdG1bcGFpcixdLDIsY29sU3VtcyhnLmFkdG1bcGFpcixdKSkgIyB3IHNwb2tlbiBieSBncm91cHMgb3RoZXIgdGhhbiBrCiAgICBwci5hZHRtX2t3IDwtIHN1bShnLmFkdG1bcGFpcixdKSAtIHByLmFkdG1fdyAtIHByLmFkdG1fayAtIGcuYWR0bVtwYWlyLF0gIyB0b3RhbCB0ZXJtcyBub3QgdyBvciBrCiAgICBwci5zZSA8LSBzcXJ0KDEvZy5hZHRtW3BhaXIsXSArIDEvcHIuYWR0bV93ICsgMS9wci5hZHRtX2sgKyAxL3ByLmFkdG1fa3cpCiAgICBwci56ZXRhIDwtIHByLmRlbHRhL3ByLnNlCiAgICAKICAgIHJldHVybihsaXN0KHpldGE9cHIuemV0YVsxLF0sIGRlbHRhPXByLmRlbHRhWzEsXSxzZT1wci5zZVsxLF0sIGNvdW50cyA9IGNvbFN1bXMoZHRtLm56KSwgYWNvdW50cyA9IGNvbFN1bXMoZy5hZHRtKSkpCiAgfSBlbHNlIHsKICAgIHJldHVybihsaXN0KHpldGE9Zy56ZXRhLGRlbHRhPWcuZGVsdGEsc2U9Zy5zZSxjb3VudHM9Zy5jb3VudHMsYWNvdW50cz1nLmFkdG0pKQogIH0KfQoKIyMjIyMjIyMjIyMjIyMgRklHSFRJTicgV09SRFMgUExPVFRJTkcgRlVOQ1RJT04KCiMgaGVscGVyIGZ1bmN0aW9uCm1ha2VUcmFuc3BhcmVudDwtZnVuY3Rpb24oc29tZUNvbG9yLCBhbHBoYT0xMDApCnsKICBuZXdDb2xvcjwtY29sMnJnYihzb21lQ29sb3IpCiAgYXBwbHkobmV3Q29sb3IsIDIsIGZ1bmN0aW9uKGN1cmNvbGRhdGEpe3JnYihyZWQ9Y3VyY29sZGF0YVsxXSwgZ3JlZW49Y3VyY29sZGF0YVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsdWU9Y3VyY29sZGF0YVszXSxhbHBoYT1hbHBoYSwgbWF4Q29sb3JWYWx1ZT0yNTUpfSkKfQoKZncuZ2dwbG90Lmdyb3VwcyA8LSBmdW5jdGlvbihmdy5jaCwgZ3JvdXBzLnVzZSA9IGFzLmZhY3Rvcihyb3duYW1lcyhmdy5jaCR6ZXRhKSksIG1heC53b3JkcyA9IDUwLCBtYXguY291bnRyYW5rID0gNDAwLCBjb2xvcnBhbGV0dGU9cmVwKCJibGFjayIsbGVuZ3RoKGdyb3Vwcy51c2UpKSwgc2l6ZXNjYWxlPTIsIHRpdGxlPSJDb21wYXJpc29uIG9mIFRlcm1zIGJ5IEdyb3VwcyIsIHN1YnRpdGxlID0gIiIsIGNhcHRpb24gPSAiR3JvdXAtc3BlY2lmaWMgdGVybXMgYXJlIG9yZGVyZWQgYnkgRmlnaHRpbicgV29yZHMgc3RhdGlzdGljIChNb25yb2UsIGV0IGFsLiAyMDA4KSIpIHsKICBpZiAoaXMubnVsbChkaW0oZncuY2gkemV0YSkpKSB7IyMgdHdvLWdyb3VwIGZ3IG9iamVjdCBjb25zaXN0cyBvZiB2ZWN0b3JzLCBub3QgbWF0cmljZXMKICAgIHpldGFyYW5rbWF0IDwtIGNiaW5kKHJhbmsoLWZ3LmNoJHpldGEpLHJhbmsoZncuY2gkemV0YSkpCiAgICBjb2xuYW1lcyh6ZXRhcmFua21hdCkgPC0gZ3JvdXBzLnVzZQogICAgY291bnRyYW5rIDwtIHJhbmsoLShmdy5jaCRjb3VudHMpKQogIH0gZWxzZSB7CiAgICB6ZXRhcmFua21hdCA8LSBhcHBseSgtZncuY2gkemV0YVtncm91cHMudXNlLF0sMSxyYW5rKQogICAgY291bnRyYW5rIDwtIHJhbmsoLWNvbFN1bXMoZncuY2gkY291bnRzKSkKICB9CiAgd2lkZXBsb3RtYXQgPC0gYXNfdGliYmxlKGNiaW5kKHpldGFyYW5rbWF0LGNvdW50cmFuaz1jb3VudHJhbmspKQogIHdpZGVwbG90bWF0JHRlcm09bmFtZXMoY291bnRyYW5rKQogICNyYW5rcGxvdCA8LSBnYXRoZXIod2lkZXBsb3RtYXQsIHBhcnR5LCB6ZXRhcmFuaywgMTpuY29sKHpldGFyYW5rbWF0KSkKICByYW5rcGxvdCA8LSBnYXRoZXIod2lkZXBsb3RtYXQsIGdyb3Vwcy51c2UsIHpldGFyYW5rLCAxOm5jb2woemV0YXJhbmttYXQpKQogIHJhbmtwbG90JHBsb3RzaXplIDwtIHNpemVzY2FsZSooNTAvKHJhbmtwbG90JHpldGFyYW5rKSleKDEvNCkKICByYW5rcGxvdCA8LSByYW5rcGxvdFtyYW5rcGxvdCR6ZXRhcmFuayA8IG1heC53b3JkcyArIDEgJiByYW5rcGxvdCRjb3VudHJhbms8bWF4LmNvdW50cmFuaysxLF0KICByYW5rcGxvdCRncm91cHMudXNlIDwtIGZhY3RvcihyYW5rcGxvdCRncm91cHMudXNlLGxldmVscz1ncm91cHMudXNlKQogIAogIHAgPC0gZ2dwbG90KHJhbmtwbG90LCBhZXMoKG5yb3cocmFua3Bsb3QpLWNvdW50cmFuayleMSwgLSh6ZXRhcmFua14xKSwgY29sb3VyPWdyb3Vwcy51c2UpKSArIAogICAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZD1GLHNpemU9c2l6ZXNjYWxlLzIpICsgCiAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgdGhlbWUoYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dD1lbGVtZW50X2JsYW5rKCkgKSArCiAgICB5bGltKC1tYXgud29yZHMsNDApICsKICAgIGZhY2V0X2dyaWQoZ3JvdXBzLnVzZSB+IC4pICsKICAgIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSB0ZXJtKSwgc2l6ZSA9IHJhbmtwbG90JHBsb3RzaXplLCBwb2ludC5wYWRkaW5nPS4wNSwKICAgICAgICAgICAgICAgICAgICBib3gucGFkZGluZyA9IHVuaXQoMC4yMCwgImxpbmVzIiksIHNob3cubGVnZW5kPUYsIG1heC5vdmVybGFwcyA9IEluZikgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBhbHBoYShjb2xvcnBhbGV0dGUsIC43KSkgKyAKIyAgICBsYWJzKHg9IlRlcm1zIHVzZWQgbW9yZSBmcmVxdWVudGx5IG92ZXJhbGwg4oaSIiwgeT0iVGVybXMgdXNlZCBtb3JlIGZyZXF1ZW50bHkgYnkgZ3JvdXAg4oaSIiwgIHRpdGxlPXRpdGxlLCBzdWJ0aXRsZT1zdWJ0aXRsZSAsIGNhcHRpb24gPSBjYXB0aW9uKSAKICAgIGxhYnMoeD1wYXN0ZSgiVGVybXMgdXNlZCBtb3JlIGZyZXF1ZW50bHkgb3ZlcmFsbCAtLT4iKSwgeT1wYXN0ZSgiVGVybXMgdXNlZCBtb3JlIGZyZXF1ZW50bHkgYnkgZ3JvdXAgLS0+IiksICB0aXRsZT10aXRsZSwgc3VidGl0bGU9c3VidGl0bGUgLCBjYXB0aW9uID0gY2FwdGlvbikgCiAgCn0KCm9wdGlvbnMoZ2dyZXBlbC5tYXgub3ZlcmxhcHMgPSBJbmYpCgpmdy5rZXlzIDwtIGZ1bmN0aW9uKGZ3LmNoLG4ua2V5cz0xMCkgewogIG4uZ3JvdXBzIDwtIG5yb3coZncuY2gkemV0YSkKICBrZXlzIDwtIG1hdHJpeCgiIixuLmtleXMsbi5ncm91cHMpCiAgY29sbmFtZXMoa2V5cykgPC0gcm93bmFtZXMoZncuY2gkemV0YSkKICAKICBmb3IgKGcgaW4gMTpuLmdyb3VwcykgewogICAga2V5c1ssZ10gPC0gbmFtZXMoc29ydChmdy5jaCR6ZXRhW2csXSxkZWM9VClbMTpuLmtleXNdKQogIH0KICBrZXlzCn0KYGBgCgoKIyMgQ29tcGFyZSBBc3NvY2lhdGVkIFByZXNzIDE5OTQtMjAxMDogQmVmb3JlIGFuZCBBZnRlciAiZXh0cmVtaXN0IiBhbmQgb3RoZXIgcXVlcnkgdGVybXMKCioqUXVlcnkgc2VhcmNoOioqCihhY3RpdmkqIHwgYWhiYXNoIHwgYWtyb21peWEgfCBhbmplbSB8IGFuc2hhcnV0IHwgYW50aWNhcGl0YWwqIHwgYW50aWRlbW9jciogfCBhbnRpZXN0YWJsaXNoKiB8IGFudGlmYSB8IGFudGlnb3Zlcm4qIHwgYW50aW1pbGl0YXIqIHwgYW50aW1vbmFyY2gqIHwgYW50aXBhdHJpKiB8IGFudGlyZWxpKiB8IGFudGlzZW0qIHwgYW50aXNvY2lhKiB8IGFudGlzeXN0KiB8IGFwb3N0KiB8IGF0aGFyaXMgfCBhdGhlaSogfCBhdGhlaXN0cyB8IGF1w59lcnBhcmxhbWVudGFyaSogfCBhdXRob3JpdGFyKiB8IGJhZ2F1KiB8IGJpZ290cyB8IGJwbGYgfCBidWtoYXJpKiB8IGNhcGl0dWxhdGlvKiB8IGNvbnNwaXJhdG8qIHwgY291bnRlcmoqIHwgY3liZXJjYWxpcCogfCBkYW1pZ28gfCBkYXdybyogfCBkZW1vbiogfCBkZXJhZGljYWxpemEqIHwgZGV2aWF0aW8qIHwgZGlxcWkgfCBkaXNzaWQqIHwgZGphbWFhdCB8IGRvdGJ1cyogfCBlY29mYXMqIHwgZXNwb3UqIHwgZXRobm9uYXRpb25hKiB8IGV4dHJlbSogfCBmYWNpc3RzIHwgZmFkYWlhKiB8IGZhbmF0KiB8IGZhc2NpKiB8IGZldMO2IHwgZmlnaHRkZW0qIHwgZnJlZWRvKiB8IGZ1bmRhbWVudGFsKiB8IGZ1cXJhIHwgZ2FmYXRhciB8IGdhbWVyZ2EqIHwgZ2VtaWR6aWkgfCBnaHVsdXd3IHwgZ2xvYmFsaSogfCBncmFtc2MqIHwgZ8O8bGVuKiB8IGhhY2t0aXYqIHwgaGFxdW5hIHwgaGFyZGxpbmUgfCBoYXJrYXR1bCB8IGhhdGVtb24qIHwgaGVpbXdlKiB8IGhlemJvbCogfCBoaW5kdXBoKiB8IGhpbmR1dHZhIHwgaGl6YnV0IHwgaG9qamEqIHwgaWRlb2xvZyogfCBpbmNpdGVtKiB8IGluY2l0ZXJzIHwgaW5zdXJyKiB8IGludGFjdGkqIHwgaXNsYW00dWsgfCBpc2xhbSogfCBqYWxqYWxhdCB8IGpiYWtjIHwgamloYWRpKiB8IGptamIgfCBqcnRuIHwganVkYWkqIHwganVoYXltYW4gfCBqdW5kdSogfCBrYWRpemEqIHwga2FoYW5pc20gfCBrYWhhbmlzdCB8IGthcnJhbSogfCBrYXlzYSogfCBraGFsaXMqIHwga2hhdG1pYSB8IGtoYXdhcmlqIHwga2hvbWVpbiogfCBrb3V0bGEgfCBsZWZ0aXN0IHwgbGVmdGlzdHMgfCBsZWZ0d2luZyB8IGxpYmVyYXRpbyogfCBtYWRraGEqIHwgbWFka2hhbCogfCBtYWltb25pZCogfCBtYW5vc3AqIHwgbWF1cmFzKiB8IG1jbG4gfCBtaWxpdGFuKiB8IG1pbGxhdHUgfCBtb25hcmMqIHwgbXVkamEqIHwgbXVoYWppKiB8IG11amFoaWQqIHwgbXVyYWIqIHwgbXV0dGFoaSogfCBuYWpqYWRhaCB8IG5hdGlvbmFsaSogfCBuZW9mYXMqIHwgbmVvbmEqIHwgb3BhbnRpc2ggfCBvcHBvc2l0aSogfCBwYWxlb2xpYmVydGFyKiB8IHBhcmFtaWxpKiB8IHBhcmxpYW1lbnRhKiB8IHBlZ2lkYSB8IHBvcHVsaXN0IHwgcHJpbmNpcGEqIHwgcHJvZmUqIHwgcHJvd2FyIHwgcHV0aW5pc3QgfCBxYWRhcmkgfCBxdXJhbmlzbSB8IHF1cmFuaXN0IHwgcXV0YmlzbSB8IHF1dGJpc3QgfCBxdXRiaXN0cyB8IHJhY2lzKiB8IHJhZGljYSogfCByZWFjdGlvbmkqIHwgcmVmb3JtaXMqIHwgcmVpY2hzYsO8cmdlcmJld2UqIHwgcmlnaHRpc3QgfCByaWdodHcqIHwgcm9maXEgfCBydXNzb3BoKiB8IHNhYmlyZWVuIHwgc2FkZGEqIHwgc2FsYWYqIHwgc2F5YWZmIHwgc2NyaXB0dXJhKiB8IHNlY3VsYSogfCBzZXBhcmF0aW9uaSogfCBzaGFyaWE0aG9sKiB8IHNpa3Jpa2ltIHwgc3BsaXQqIHwgc3F1YWRpc20gfCBzdHJhc3NlKiB8IHN1YnZlciogfCBzdWlkbGFuKiB8IHN1a2FybiogfCBzdXByZW1hKiB8IHN1cHJlbWFjKiB8IHN5bXBhdGhpKiB8IHRhYmxlKiB8IHRhYmxpcSB8IHRha2ZpciB8IHRha2ZpciogfCB0YWtmaSogfHRlcnJvciogfCB0aGVvYyogfCB0aXRvaXRlIHwgdHJpYmEqIHwgdHJvdHMqIHwgdHJvdHNrKiB8IHVrcmFpbm9waCogfCB1bHRyYWNvbnNlcnZhKiB8IHVsdHJhbGliKiB8IHVsdHJhbmF0aW9uYSogfCB1bHRyYXIqIHwgdXNjbW8gfCB3YWhhYmJpIHwgd2FoYWIqIHwgd2FoaGEqIHwgeGVub3BoKiB8IHl1bGRlKiB8IHppbm92aWUqKQoKCioqTG9hZCBhbmQgY2xlYW4gdGhlIGRhdGEqKgoKICAqIHRvIHN0cmluZyAmIGxvd2VyIHRleHQKICAqIHBpdm90IHRvIGxvbmcgZm9ybWF0CiAgKiBhcHBseSB0ZXh0X2NsZWFuZXIgdG8gb25lIGNvbHVtbiAiY29udGV4dC50ZXh0IgoKYGBge3IsICByZXN1bHRzPSdhc2lzJ30KdGV4dF9jbGVhbmVyPC1mdW5jdGlvbihjb3JwdXMpewogIHRlbXBjb3JwdXM8LUNvcnB1cyhWZWN0b3JTb3VyY2UoY29ycHVzKSkKICB0ZW1wY29ycHVzPC10bV9tYXAodGVtcGNvcnB1cywKICAgICAgICAgICAgICAgICAgICByZW1vdmVQdW5jdHVhdGlvbikKICB0ZW1wY29ycHVzPC10bV9tYXAodGVtcGNvcnB1cywKICAgICAgICAgICAgICAgICAgICBzdHJpcFdoaXRlc3BhY2UpCiAgdGVtcGNvcnB1czwtdG1fbWFwKHRlbXBjb3JwdXMsCiAgICAgICAgICAgICAgICAgICAgcmVtb3ZlTnVtYmVycykKICB0ZW1wY29ycHVzPC10bV9tYXAodGVtcGNvcnB1cywKICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKQogIHRlbXBjb3JwdXM8LXRtX21hcCh0ZW1wY29ycHVzLCAKICAgICAgICAgICAgICAgICAgICBzdGVtRG9jdW1lbnQpCiAgcmV0dXJuKHRlbXBjb3JwdXMpCn0KCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9IEZBTFNFfQoKZXh0cmVtX0FQLmRmbV9hbGwgPC1yZWFkLmRlbGltKCJ+L0RvY3VtZW50cy9HaXRIdWIvNzBjb3JyX2V4dHJlbWlzdF85NDEwX0FQLnR4dCIsIGhlYWRlcj1UUlVFLCBzZXA9Ilx0IikKZXh0cmVtX0FQLmRmbV9hbGwkcHViZGF0ZSA9IHN1YnN0cihleHRyZW1fQVAuZGZtX2FsbCRUZXh0LklELDksMTYpCmV4dHJlbV9BUC5kZm1fYWxsJHB1YmRhdGUgPC0gYXMuUE9TSVhjdChleHRyZW1fQVAuZGZtX2FsbCRwdWJkYXRlLCBmb3JtYXQgPSAiJVklbSVkIikKZXh0cmVtX0FQLmRmbV9hbGwkcHViZGF0ZSA8LSBhcy5EYXRlKGV4dHJlbV9BUC5kZm1fYWxsJHB1YmRhdGUsIGZvcm1hdD0iJVktJW0tJWQiKQpleHRyZW1fQXAuZGZtX2FsbCRwdWJ5ZWFyIDwtIHllYXIoZXh0cmVtX0FQLmRmbV9hbGwkcHViZGF0ZSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpzZXQuc2VlZCgyMjE3KQpleHRyZW1fQVAuZGZtIDwtIGFzLmRhdGEuZnJhbWUoc2FtcGxlX24oZXh0cmVtX0FQLmRmbV9hbGwsIDE1MDAwKSkKcm0oZXh0cmVtX0FQLmRmbV9hbGwpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBleHRyZW1fTllULmRmbSRDb250ZXh0LmJlZm9yZSA9IGxhcHBseShleHRyZW1fTllULmRmbSRDb250ZXh0LmJlZm9yZSwgdG9TdHJpbmcpCiMgZXh0cmVtX05ZVC5kZm0kQ29udGV4dC5iZWZvcmUgPSBsYXBwbHkoZXh0cmVtX05ZVC5kZm0kQ29udGV4dC5iZWZvcmUsIHRvbG93ZXIpCiMgCiMgZXh0cmVtX05ZVC5kZm0kQ29udGV4dC5hZnRlciA9IGxhcHBseShleHRyZW1fTllULmRmbSRDb250ZXh0LmFmdGVyLCB0b1N0cmluZykKIyBleHRyZW1fTllULmRmbSRDb250ZXh0LmFmdGVyID0gbGFwcGx5KGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYWZ0ZXIsIHRvbG93ZXIpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmV4dHJlbV9BUC5kZm0gPC0gZXh0cmVtX0FQLmRmbSAlPiUgZGlzdGluY3QoQ29udGV4dC5iZWZvcmUsIC5rZWVwX2FsbCA9IFRSVUUpCgpleHRyZW1fQVAuZGZtLmxvbmcgPC0gcGl2b3RfbG9uZ2VyKGV4dHJlbV9BUC5kZm0sIGNvbHM9YyhDb250ZXh0LmJlZm9yZSwgQ29udGV4dC5hZnRlciksIG5hbWVzX3RvID0gIkNvbnRleHQiLCB2YWx1ZXNfdG8gPSAiY29udGV4dC50ZXh0IikKCmV4dHJlbV9BUC5kZm0ubG9uZyRDb250ZXh0IDwtIGFzLmZhY3RvcihleHRyZW1fQVAuZGZtLmxvbmckQ29udGV4dCkKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKZXh0cmVtZWNvcnB1cyA8LXRleHRfY2xlYW5lcihleHRyZW1fQVAuZGZtLmxvbmckY29udGV4dC50ZXh0KQoKYGBgCgoKQ2FsY3VsYXRlIEZXLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CgplIDwtIGRmbShleHRyZW1lY29ycHVzJGNvbnRlbnQpCm1lc3NhZ2UoZGltKGUpKQpoZWFkKGUpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmUgPC0gZGZtX3NlbGVjdChlLCBwYXR0ZXJuID0gc3RvcHdvcmRzKCJlbmdsaXNoIiksIHNlbGVjdGlvbiA9ICJyZW1vdmUiKQplIDwtIGRmbV9zZWxlY3QoZSwgbWluX25jaGFyID0gMikKZSA8LSBkZm1fdHJpbShlLCBtaW5fdGVybWZyZXEgPSA0LCBtaW5fZG9jZnJlcSA9IC4wNSwgdmVyYm9zZT1UUlVFKQoKI2RpbShlKQojIHNwYXJzaXR5KGUpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmV4dHJlbV9kdG0gPC0gY29udmVydChlLCB0bz0nZGF0YS5mcmFtZScpCmV4dHJlbV9kdG0gPC0gZXh0cmVtX2R0bVstYygxKV0KdyA8LSB3aGljaCggc2FwcGx5KGV4dHJlbV9kdG0sIGNsYXNzICkgPT0gJ2NoYXJhY3RlcicgKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpmdy5leHRyZW0gPC0gZndncm91cHMoZXh0cmVtX2R0bSwgZ3JvdXBzPWV4dHJlbV9BUC5kZm0ubG9uZyRDb250ZXh0KQoKcm0oZXh0cmVtX2R0bSkKCmBgYAoKCioqR2V0IGFuZCBzaG93IHRoZSB0b3Agd29yZHMgcGVyIGdyb3VwIGJ5IHpldGEuKioKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0iYXNpcyJ9Cgpmd2tleXMuZXh0cmVtIDwtIGZ3LmtleXMoZncuZXh0cmVtLCBuLmtleXM9MjApCmNvbHMgPC0gcmV2KGNvbG5hbWVzKGZ3a2V5cy5leHRyZW0pKQpmd2tleXMuZXh0cmVtIDwtIGZ3a2V5cy5leHRyZW1bLGNvbHNdCmthYmxlKGZ3a2V5cy5leHRyZW0pCgpgYGAKClBsb3Q6IEJlZm9yZSBpbiBCbHVlLCBBZnRlciBpbiBSZWQKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD00fQoKcC5mdy5leHRyZW0gPC0gZncuZ2dwbG90Lmdyb3Vwcyhmdy5leHRyZW0sc2l6ZXNjYWxlPTQsbWF4LndvcmRzPTIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguY291bnRyYW5rPTQwMCxjb2xvcnBhbGV0dGU9YygicmVkIiwiYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gJ0NvbXBhcmlzb24gb2YgVGVybXMgQmVmb3JlIGFuZCBBZnRlciBRdWVyeSBXb3JkJykKcC5mdy5leHRyZW0KYGBgCgojIyBDYWxjdWxhdGUgYnkgcXVlcnkgaXRlbS9zZWFyY2ggdGVybQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTR9CgpleHRyZW1fQVAuZGZtLmxvbmckUXVlcnkuaXRlbSA8LSBhcy5mYWN0b3IoZXh0cmVtX0FQLmRmbS5sb25nJFF1ZXJ5Lml0ZW0pCgp0b3BfbiA8LWFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShleHRyZW1fQVAuZGZtLmxvbmckUXVlcnkuaXRlbSksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjVdKSAKbWVzc2FnZShkaW0odG9wX24pKQpjb2xuYW1lcyh0b3BfbikgPC0gYygndGVybScsICdGcmVxJykKbWVzc2FnZSh0b3BfbikKCmV4dHJlbV9kdG1fdG9wbiA8LSBjb252ZXJ0KGUsIHRvPSdkYXRhLmZyYW1lJykKZXh0cmVtX2R0bV90b3BuJE51bWJlci5vZi5oaXQgPC0gZXh0cmVtX0FQLmRmbS5sb25nJE51bWJlci5vZi5oaXQKCnRvcG5fdGVybXMgPC0gZXh0cmVtX0FQLmRmbS5sb25nICU+JQogICAgICBmaWx0ZXIoUXVlcnkuaXRlbSAlaW4lIHRvcF9uJHRlcm0pCgpleHRyZW1fZHRtX3RvcG5fa2VlcCA8LSBleHRyZW1fZHRtX3RvcG4gJT4lIAogICAgZmlsdGVyKE51bWJlci5vZi5oaXQgJWluJSB0b3BuX3Rlcm1zJE51bWJlci5vZi5oaXQpCgpyIDwtIHN1bShsZW5ndGgoZXh0cmVtX2R0bV90b3BuX2tlZXApKQoKZXh0cmVtX2R0bV90b3BuX2tlZXAgPC0gZXh0cmVtX2R0bV90b3BuX2tlZXBbLWMoMSwgcildCgpmdy5xdWVyeV9pdGVtIDwtIGZ3Z3JvdXBzKGV4dHJlbV9kdG1fdG9wbl9rZWVwLGdyb3VwcyA9IHRvcG5fdGVybXMkUXVlcnkuaXRlbSkKZndrZXlzLnF1ZXJ5X2l0ZW0gPC0gZncua2V5cyhmdy5xdWVyeV9pdGVtLCBuLmtleXM9MTUpCmthYmxlKGZ3a2V5cy5xdWVyeV9pdGVtKQoKcm0ocikKcm0oZXh0cmVtX2R0bV90b3BuKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnAuZncucXVlcnlfaXRlbSA8LSBmdy5nZ3Bsb3QuZ3JvdXBzKGZ3LnF1ZXJ5X2l0ZW0sc2l6ZXNjYWxlPTMuMixtYXgud29yZHM9MTUwLG1heC5jb3VudHJhbms9NDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnBhbGV0dGU9YygiZGFya2dyZWVuIiwiZGFya2dyZWVuIiwiZGFya2dyZWVuIiwiZGFya2dyZWVuIiwiZGFya2dyZWVuIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gJ0NvbXBhcmlzb24gb2YgVGVybXMgYnkgT3ZlcmFsbCBUb3AgVGVybXMnKQpwLmZ3LnF1ZXJ5X2l0ZW0KCmBgYAoKCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NH0KCmV4dHJlbV9kdG1fdG9wbiA8LSBjb252ZXJ0KGUsIHRvPSdkYXRhLmZyYW1lJykKZXh0cmVtX2R0bV90b3BuJE51bWJlci5vZi5oaXQgPC0gZXh0cmVtX0FQLmRmbS5sb25nJE51bWJlci5vZi5oaXQKZXh0cmVtX2R0bV90b3BuJENvbnRleHQgPC0gZXh0cmVtX0FQLmRmbS5sb25nJENvbnRleHQKCnRvcG5fdGVybXMgPC0gZXh0cmVtX0FQLmRmbS5sb25nICU+JQogICAgICBmaWx0ZXIoUXVlcnkuaXRlbSAlaW4lIHRvcF9uJHRlcm0pCgpleHRyZW1fZHRtX3RvcG5fa2VlcCA8LSBleHRyZW1fZHRtX3RvcG4gJT4lIAogICAgZmlsdGVyKE51bWJlci5vZi5oaXQgJWluJSB0b3BuX3Rlcm1zJE51bWJlci5vZi5oaXQpCgpleHRyZW1fZHRtX3RvcG5fa2VlcF9iZWZvcmUgPC0gZXh0cmVtX2R0bV90b3BuX2tlZXBbZ3JlcCgiYmVmb3JlIixleHRyZW1fZHRtX3RvcG5fa2VlcCRDb250ZXh0KSxdCmV4dHJlbV9kdG1fdG9wbl9rZWVwX2FmdGVyIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwW2dyZXAoImFmdGVyIiwgZXh0cmVtX2R0bV90b3BuX2tlZXAkQ29udGV4dCksXQoKcnIgPC1kaW0odG9wbl90ZXJtcylbMV0KciA8LSBzdW0obGVuZ3RoKGV4dHJlbV9kdG1fdG9wbl9rZWVwX2JlZm9yZSkpCnRvcG5fdGVybXNfYmVmb3JlIDwtIHRvcG5fdGVybXNbc2VxKDEscnIsMiksXQp0b3BuX3Rlcm1zX2FmdGVyIDwtIHRvcG5fdGVybXNbc2VxKDIscnIsMiksXQoKZXh0cmVtX2R0bV90b3BuX2tlZXBfYmVmb3JlIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwX2JlZm9yZVstYygxLCByLTEsIHIpXQpleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlciA8LSBleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlclstYygxLCByLTEsIHIpXQoKcm0ocnIpCnJtKHIpCnJtKGV4dHJlbV9kdG1fdG9wbikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpmdy5xdWVyeV9pdGVtX2JlZm9yZSA8LSBmd2dyb3VwcyhleHRyZW1fZHRtX3RvcG5fa2VlcF9iZWZvcmUsZ3JvdXBzID0gdG9wbl90ZXJtc19iZWZvcmUkUXVlcnkuaXRlbSkKZndrZXlzLnF1ZXJ5X2l0ZW1fYmVmb3JlIDwtIGZ3LmtleXMoZncucXVlcnlfaXRlbV9iZWZvcmUsIG4ua2V5cz0xNSkKa2FibGUoZndrZXlzLnF1ZXJ5X2l0ZW1fYmVmb3JlLCBjYXB0aW9uID0gIlRvcCAxNSBXb3JkcyBmb3IgUXVlcnkgVGVybTogQkVGT1JFIikKCnAuZncucXVlcnlfaXRlbV9iZWZvcmUgPC0gZncuZ2dwbG90Lmdyb3Vwcyhmdy5xdWVyeV9pdGVtX2JlZm9yZSxzaXplc2NhbGU9MixtYXgud29yZHM9MTUwLG1heC5jb3VudHJhbms9NDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JwYWxldHRlID0gYygnYmx1ZScsJ2JsdWUnLCdibHVlJywgJ2JsdWUnLCdibHVlJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICdDb21wYXJpc29uIG9mIFRlcm1zIGJ5IE92ZXJhbGwgVG9wIFRlcm1zOiBCRUZPUkUnKQpwLmZ3LnF1ZXJ5X2l0ZW1fYmVmb3JlCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZncucXVlcnlfaXRlbV9hZnRlciA8LSBmd2dyb3VwcyhleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlcixncm91cHMgPSB0b3BuX3Rlcm1zX2FmdGVyJFF1ZXJ5Lml0ZW0pCmZ3a2V5cy5xdWVyeV9pdGVtX2FmdGVyIDwtIGZ3LmtleXMoZncucXVlcnlfaXRlbV9hZnRlciwgbi5rZXlzPTE1KQprYWJsZShmd2tleXMucXVlcnlfaXRlbV9hZnRlciwgY2FwdGlvbiA9ICJUb3AgMTUgV29yZHMgZm9yIFF1ZXJ5IFRlcm06IEFGVEVSIikKCnAuZncucXVlcnlfaXRlbV9hZnRlciA8LSBmdy5nZ3Bsb3QuZ3JvdXBzKGZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIsc2l6ZXNjYWxlPTIsbWF4LndvcmRzPTE1MCxtYXguY291bnRyYW5rPTQwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycGFsZXR0ZSA9IGMoJ3JlZCcsICdyZWQnLCdyZWQnLCdyZWQnLCdyZWQnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAnQ29tcGFyaXNvbiBvZiBUZXJtcyBieSBPdmVyYWxsIFRvcCBUZXJtczogQUZURVInKQpwLmZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIKCgpgYGAKCgoKIyMgQ2FsY3VsYXRlIFBhcnRzIG9mIHNwZWVjaCBieSBiZWZvcmUgYW5kIGFmdGVyCgpDYWxjdWxhdGUgRlcgYW5kIGtleXMKYGBge3IsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nPUZBTFNFfQoKdWRfbW9kZWwgPC0gdWRwaXBlX2xvYWRfbW9kZWwodWRfbW9kZWwkZmlsZV9tb2RlbCkKCnR4dCA8LWFzLmNoYXJhY3RlcihleHRyZW1fQVAuZGZtLmxvbmckY29udGV4dC50ZXh0KQoKeF91ZHAgPC0gdWRwaXBlX2Fubm90YXRlKHVkX21vZGVsLCB4ID0gdHh0LCBkb2NfaWQgPSBzZXFfYWxvbmcodHh0KSkKeCA8LSBhcy5kYXRhLmZyYW1lKHhfdWRwKQoKeCRkb2NfaWQgPC1hcy5pbnRlZ2VyKHgkZG9jX2lkKQoKeF9vZGQuYmVmb3JlIDwtIHhbeCRkb2NfaWQgJSUgMiA9PSAxLF0KeF9ldmVuLmFmdGVyIDwteFt4JGRvY19pZCAlJSAyID09IDAsIF0KCmBgYAoKCipBIGZldyBiYXJjaGFydCBmdW5jdGlvbnMqCgpgYGB7ciwgcmVzdWx0cz0naGlkZSd9CgojIyBVTklWRVJTQUwgUG9TClVQT1NfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIHN0YXRzMSA8LSB0eHRfZnJlcShkZjEkdXBvcykKICBzdGF0czEka2V5IDwtIGZhY3RvcihzdGF0czEka2V5LCBsZXZlbHMgPSByZXYoc3RhdHMxJGtleSkpCiAgCiAgc3RhdHMyIDwtIHR4dF9mcmVxKGRmMiR1cG9zKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXksIGxldmVscyA9IHJldihzdGF0czIka2V5KSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBzdGF0czEsIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICBtYWluID0gIlVQT1MgKFVuaXZlcnNhbCBQYXJ0cyBvZiBTcGVlY2gpXG4gZnJlcXVlbmN5IG9mIG9jY3VycmVuY2U6IEJFRk9SRSB2cyBBRlRFUiIsIAogICAgICAgICB4bGFiID0gIkZyZXEiKSwgCiAgICBiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gc3RhdHMyLCBjb2wgPSAgJ3NreWJsdWUnLAogICAgICAgICB4bGFiID0gIkZyZXEiKSkKfQoKCgojIyBOT1VOUwpOT1VOU19iYXJjaGFydCA8LSBmdW5jdGlvbihkZjEsIGRmMil7CiAgCiAgc3RhdHMxIDwtIHN1YnNldChkZjEsIHVwb3MgJWluJSBjKCJOT1VOIikpIAogIHN0YXRzMSA8LSB0eHRfZnJlcShzdGF0czEkdG9rZW4pCiAgc3RhdHMxJGtleSA8LSBmYWN0b3Ioc3RhdHMxJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXkpKQogIAogIHN0YXRzMiA8LSBzdWJzZXQoZGYyLCB1cG9zICVpbiUgYygiTk9VTiIpKSAKICBzdGF0czIgPC0gdHh0X2ZyZXEoc3RhdHMyJHRva2VuKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXksIGxldmVscyA9IHJldihzdGF0czIka2V5KSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMSwgMjApLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgICAgbWFpbiA9ICJNb3N0IG9jY3VycmluZyBub3VuczogQkVGT1JFIHZzIEFGVEVSIiwgeGxhYiA9ICJGcmVxIiksCiAgICAgIGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMiwgMjApLCBjb2wgPSAic2t5Ymx1ZSIsIAogICAgICAgICAgICB4bGFiID0gIkZyZXEiKSkKfQoKIyMgQURKRUNUSVZFUwpBREpfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIAogIHN0YXRzMSA8LSBzdWJzZXQoZGYxLCB1cG9zICVpbiUgYygiQURKIikpIAogIHN0YXRzMSA8LSB0eHRfZnJlcShzdGF0czEkdG9rZW4pCiAgc3RhdHMxJGtleSA8LSBmYWN0b3Ioc3RhdHMxJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXkpKQogIAogIHN0YXRzMiA8LSBzdWJzZXQoZGYyLCB1cG9zICVpbiUgYygiQURKIikpIAogIHN0YXRzMiA8LSB0eHRfZnJlcShzdGF0czIkdG9rZW4pCiAgc3RhdHMyJGtleSA8LSBmYWN0b3Ioc3RhdHMyJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMiRrZXkpKQogIAogIGMoYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMxLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIk1vc3Qgb2NjdXJyaW5nIGFkamVjdGl2ZXM6IEJFRk9SRSB2cyBBRlRFUiIsIHhsYWIgPSAiRnJlcSIpLAogICAgICBiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gaGVhZChzdGF0czIsIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgeGxhYiA9ICJGcmVxIikpCn0KCiMjIFVzaW5nIFJBS0UgdG8gZmluZCBrZXl3b3JkcwpSQUtFX0tXX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSxkZjIpewogIAogIHN0YXRzMSA8LSBrZXl3b3Jkc19yYWtlKHggPSBkZjEsIHRlcm0gPSAibGVtbWEiLCBncm91cCA9ICJkb2NfaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGV2YW50ID0gZGYxJHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKQogIHN0YXRzMSRrZXkgPC0gZmFjdG9yKHN0YXRzMSRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMxJGtleXdvcmQpKQogIAogIHN0YXRzMiA8LSBrZXl3b3Jkc19yYWtlKHggPSBkZjIsIHRlcm0gPSAibGVtbWEiLCBncm91cCA9ICJkb2NfaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGV2YW50ID0gZGYyJHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMyJGtleXdvcmQpKQogIAogIAogIGMoYmFyY2hhcnQoa2V5IH4gcmFrZSwgZGF0YSA9IGhlYWQoc3Vic2V0KHN0YXRzMSwgZnJlcSA+IDMpLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIktleXdvcmRzIGlkZW50aWZpZWQgYnkgUkFLRTogQkVGT1JFIHZzIEFGVEVSIiwgCiAgICAgICAgICAgeGxhYiA9ICJSYWtlIiksCiAgICBiYXJjaGFydChrZXkgfiByYWtlLCBkYXRhID0gaGVhZChzdWJzZXQoc3RhdHMyLCBmcmVxID4gMyksIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgICB4bGFiID0gIlJha2UiKSkKfQoKIyMgVXNpbmcgUG9pbnR3aXNlIE11dHVhbCBJbmZvcm1hdGlvbiBDb2xsb2NhdGlvbnMKUFdJX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSwgZGYyKXsKICAKICBkZjEkd29yZCA8LSB0b2xvd2VyKGRmMSR0b2tlbikKICBzdGF0czEgPC0ga2V5d29yZHNfY29sbG9jYXRpb24oeCA9IGRmMSwgdGVybSA9ICJ3b3JkIiwgZ3JvdXAgPSAiZG9jX2lkIikKICBzdGF0czEka2V5IDwtIGZhY3RvcihzdGF0czEka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXl3b3JkKSkKICAKICBkZjIkd29yZCA8LSB0b2xvd2VyKGRmMiR0b2tlbikKICBzdGF0czIgPC0ga2V5d29yZHNfY29sbG9jYXRpb24oeCA9IGRmMiwgdGVybSA9ICJ3b3JkIiwgZ3JvdXAgPSAiZG9jX2lkIikKICBzdGF0czIka2V5IDwtIGZhY3RvcihzdGF0czIka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMiRrZXl3b3JkKSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IHBtaSwgZGF0YSA9IGhlYWQoc3Vic2V0KHN0YXRzMSwgZnJlcSA+IDMpLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIktleXdvcmRzIGlkZW50aWZpZWQgYnkgUE1JIENvbGxvY2F0aW9uOiBCRUZPUkUgdnMgQUZURVIiLCAKICAgICAgICAgICB4bGFiID0gIlBNSSAoUG9pbnR3aXNlIE11dHVhbCBJbmZvcm1hdGlvbikiKSwKICAgICAgYmFyY2hhcnQoa2V5IH4gcG1pLCBkYXRhID0gaGVhZChzdWJzZXQoc3RhdHMyLCBmcmVxID4gMyksIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgICB4bGFiID0gIlBNSSAoUG9pbnR3aXNlIE11dHVhbCBJbmZvcm1hdGlvbikiKSkKfQoKIyMgVXNpbmcgYSBzZXF1ZW5jZSBvZiBQT1MgdGFncyAobm91biBwaHJhc2VzIC8gdmVyYiBwaHJhc2VzKQpQT1NfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIAogIGRmMSRwaHJhc2VfdGFnIDwtIGFzX3BocmFzZW1hY2hpbmUoZGYxJHVwb3MsIHR5cGUgPSAidXBvcyIpCiAgc3RhdHMxIDwtIGtleXdvcmRzX3BocmFzZXMoeCA9IGRmMSRwaHJhc2VfdGFnLCB0ZXJtID0gdG9sb3dlcihkZjEkdG9rZW4pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiKEF8TikqTihQK0QqKEF8TikqTikqIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19yZWdleCA9IFRSVUUsIGRldGFpbGVkID0gRkFMU0UpCiAgc3RhdHMxIDwtIHN1YnNldChzdGF0czEsIG5ncmFtID4gMSAmIGZyZXEgPiAzKQogIHN0YXRzMSRrZXkgPC0gZmFjdG9yKHN0YXRzMSRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMxJGtleXdvcmQpKQogIAogIGRmMiRwaHJhc2VfdGFnIDwtIGFzX3BocmFzZW1hY2hpbmUoZGYyJHVwb3MsIHR5cGUgPSAidXBvcyIpCiAgc3RhdHMyIDwtIGtleXdvcmRzX3BocmFzZXMoeCA9IGRmMiRwaHJhc2VfdGFnLCB0ZXJtID0gdG9sb3dlcihkZjIkdG9rZW4pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiKEF8TikqTihQK0QqKEF8TikqTikqIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19yZWdleCA9IFRSVUUsIGRldGFpbGVkID0gRkFMU0UpCiAgc3RhdHMyIDwtIHN1YnNldChzdGF0czIsIG5ncmFtID4gMSAmIGZyZXEgPiAzKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMyJGtleXdvcmQpKQogIAogIGMoYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMxLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIktleXdvcmRzIC0gc2ltcGxlIG5vdW4gcGhyYXNlczogQkVGT1JFIHZzIEFGVEVSIiwgeGxhYiA9ICJGcmVxdWVuY3kiKSwKICAgICAgYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMyLCAyMCksIGNvbCA9ICJza3libHVlIiwgCiAgICAgICAgICAgICAgIHhsYWIgPSAiRnJlcXVlbmN5IikpCn0KYGBgCgoKIyMgQmFyIENoYXJ0cyBmcm9tIEZ1bmN0aW9ucyBBYm92ZQoKYGBge3IgUE9TYmFyY2hhcnRzLCBlY2hvPVRSVUUsIGZpZy53aWR0aD04fQoKVVBPU19iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKTk9VTlNfYmFyY2hhcnQoeF9vZGQuYmVmb3JlLCB4X2V2ZW4uYWZ0ZXIpCkFESl9iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKUkFLRV9LV19iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKUFdJX2JhcmNoYXJ0KHhfb2RkLmJlZm9yZSwgeF9ldmVuLmFmdGVyKQpQT1NfYmFyY2hhcnQoeF9vZGQuYmVmb3JlLCB4X2V2ZW4uYWZ0ZXIpCgpgYGAKCgojIyBDb29jY3VyZW5jZXMKCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9OH0KCkNPX09DX25vdW5fYWRqX3NhbWVfc2VudC5iZWZvcmUgPC0gZnVuY3Rpb24oZGYxKXsKICAKICBsaWJyYXJ5KGlncmFwaCkKICBsaWJyYXJ5KGdncmFwaCkKICBsaWJyYXJ5KGdncGxvdDIpCiAgCiAgY29vYyA8LSBjb29jY3VycmVuY2UoeCA9IHN1YnNldChkZjEsIHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgdGVybSA9ICJsZW1tYSIsIAogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYygiZG9jX2lkIiwgInBhcmFncmFwaF9pZCIsICJzZW50ZW5jZV9pZCIpKQoKICB3b3JkbmV0d29yayA8LSBoZWFkKGNvb2MsIDYwKQogIHdvcmRuZXR3b3JrIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSh3b3JkbmV0d29yaykKICAKICBnZ3JhcGgod29yZG5ldHdvcmssIGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IGNvb2MsIGVkZ2VfYWxwaGEgPSBjb29jKSwgZWRnZV9jb2xvdXIgPSAicGluayIpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCBjb2wgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDQpICsKICAgIHRoZW1lX2dyYXBoKGJhc2VfZmFtaWx5ID0gIkFyaWFsIE5hcnJvdyIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgbGFicyh0aXRsZSA9ICJDb29jY3VycmVuY2VzIHdpdGhpbiBzZW50ZW5jZTogQkVGT1JFIiwgc3VidGl0bGUgPSAiTm91bnMgJiBBZGplY3RpdmUiKQogIAp9CgpDT19PQ19ub3VuX2Fkal9zYW1lX3NlbnQuYWZ0ZXIgPC0gZnVuY3Rpb24oZGYyKXsKICAKICBsaWJyYXJ5KGlncmFwaCkKICBsaWJyYXJ5KGdncmFwaCkKICBsaWJyYXJ5KGdncGxvdDIpCiAgCiAgY29vYyA8LSBjb29jY3VycmVuY2UoeCA9IHN1YnNldChkZjIsIHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgdGVybSA9ICJsZW1tYSIsIAogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYygiZG9jX2lkIiwgInBhcmFncmFwaF9pZCIsICJzZW50ZW5jZV9pZCIpKQoKICB3b3JkbmV0d29yayA8LSBoZWFkKGNvb2MsIDYwKQogIHdvcmRuZXR3b3JrIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSh3b3JkbmV0d29yaykKICAKICBnZ3JhcGgod29yZG5ldHdvcmssIGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IGNvb2MsIGVkZ2VfYWxwaGEgPSBjb29jKSwgZWRnZV9jb2xvdXIgPSAibGlnaHRncmVlbiIpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCBjb2wgPSAiZGFya2JsdWUiLCBzaXplID0gNCkgKwogICAgdGhlbWVfZ3JhcGgoYmFzZV9mYW1pbHkgPSAiQXJpYWwgTmFycm93IikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBsYWJzKHRpdGxlID0gIkNvb2NjdXJyZW5jZXMgd2l0aGluIHNlbnRlbmNlOiBBRlRFUiIsIHN1YnRpdGxlID0gIk5vdW5zICYgQWRqZWN0aXZlIikKICAKfQoKCkNPX09DX25vdW5fYWRqX3NhbWVfc2VudC5iZWZvcmUoeF9vZGQuYmVmb3JlKQpDT19PQ19ub3VuX2Fkal9zYW1lX3NlbnQuYWZ0ZXIoeF9ldmVuLmFmdGVyKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKQ09fT0Nfbm91bl9hZGpfZm9sbG93aW5nLmJlZm9yZSA8LSBmdW5jdGlvbihkZil7CiAgY29vYyA8LSBjb29jY3VycmVuY2UoZGYkbGVtbWEsIHJlbGV2YW50ID0gZGYkdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIiksIHNraXBncmFtID0gMSkKICBoZWFkKGNvb2MpCiAgCiAgd29yZG5ldHdvcmsgPC0gaGVhZChjb29jLCA2MCkKICB3b3JkbmV0d29yayA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUod29yZG5ldHdvcmspCiAgZ2dyYXBoKHdvcmRuZXR3b3JrLCBsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBjb29jLCBlZGdlX2FscGhhID0gY29vYyksIGVkZ2VfY29sb3VyID0gImxpZ2h0Z3JlZW4iKSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgY29sID0gImRhcmtncmVlbiIsIHNpemUgPSA0KSArCiAgICB0aGVtZV9ncmFwaChiYXNlX2ZhbWlseSA9ICJBcmlhbCBOYXJyb3ciKSArCiAgICBsYWJzKHRpdGxlID0gIldvcmRzIGZvbGxvd2luZyBvbmUgYW5vdGhlcjogQkVGT1JFIiwgc3VidGl0bGUgPSAiTm91bnMgJiBBZGplY3RpdmUiKQp9CgpDT19PQ19ub3VuX2Fkal9mb2xsb3dpbmcuYWZ0ZXIgPC0gZnVuY3Rpb24oZGYpewogIGNvb2MgPC0gY29vY2N1cnJlbmNlKGRmJGxlbW1hLCByZWxldmFudCA9IGRmJHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpLCBza2lwZ3JhbSA9IDEpCiAgaGVhZChjb29jKQogIAogIHdvcmRuZXR3b3JrIDwtIGhlYWQoY29vYywgNjApCiAgd29yZG5ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHdvcmRuZXR3b3JrKQogIGdncmFwaCh3b3JkbmV0d29yaywgbGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gY29vYywgZWRnZV9hbHBoYSA9IGNvb2MpLCBlZGdlX2NvbG91ciA9ICJza3libHVlIikgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbCA9ICJkYXJrYmx1ZSIsIHNpemUgPSA0KSArCiAgICB0aGVtZV9ncmFwaChiYXNlX2ZhbWlseSA9ICJBcmlhbCBOYXJyb3ciKSArCiAgICBsYWJzKHRpdGxlID0gIldvcmRzIGZvbGxvd2luZyBvbmUgYW5vdGhlcjogQUZURVIiLCBzdWJ0aXRsZSA9ICJOb3VucyAmIEFkamVjdGl2ZSIpCn0KCgpDT19PQ19ub3VuX2Fkal9mb2xsb3dpbmcuYmVmb3JlKHhfb2RkLmJlZm9yZSkKQ09fT0Nfbm91bl9hZGpfZm9sbG93aW5nLmFmdGVyKHhfZXZlbi5hZnRlcikKCmBgYAoKIyMgQ29vY2N1cmVuY2VzIChwYXJ0IDIpCmBgYHtyfQoKQ29ycnMgPC0gZnVuY3Rpb24oZGYpewogIGRmJGlkIDwtIHVuaXF1ZV9pZGVudGlmaWVyKGRmLCBmaWVsZHMgPSBjKCJzZW50ZW5jZV9pZCIsICJkb2NfaWQiKSkKICBkdG0gPC0gc3Vic2V0KGRmLCB1cG9zICVpbiUgYygiTk9VTiIsICJBREoiKSkKICBkdG0gPC0gZG9jdW1lbnRfdGVybV9mcmVxdWVuY2llcyhkdG0sIGRvY3VtZW50ID0gImlkIiwgdGVybSA9ICJsZW1tYSIpCiAgZHRtIDwtIGRvY3VtZW50X3Rlcm1fbWF0cml4KGR0bSkKICBkdG0gPC0gZHRtX3JlbW92ZV9sb3dmcmVxKGR0bSwgbWluZnJlcSA9IDUpCiAgdGVybWNvcnJlbGF0aW9ucyA8LSBkdG1fY29yKGR0bSkKICB5IDwtIGFzX2Nvb2NjdXJyZW5jZSh0ZXJtY29ycmVsYXRpb25zKQogIHkgPC0gc3Vic2V0KHksIHRlcm0xIDwgdGVybTIgJiBhYnMoY29vYykgPiAwLjIpCiAgeSA8LSB5W29yZGVyKGFicyh5JGNvb2MpLCBkZWNyZWFzaW5nID0gVFJVRSksIF0KICBwcmludCh5WzE6MjUsXSkKfQoKYGBgCmBgYHtyIGNvcnJzfQoKQ29ycnMoeF9vZGQuYmVmb3JlKQoKQ29ycnMoeF9ldmVuLmFmdGVyKQoKYGBgCgpgYGB7ciBmaW5hbH0KCnJtKGxpc3Q9bHMoKSkKCmBgYAoKCgoKCltob21lXShodHRwczovL2JyZWdyZWVuLmdpdGh1Yi5pby8pCg==