Installare R ed Rstudio
Seguite le istruzioni partendo dal sito di posit: https://posit.co/download/rstudio-desktop/. La
pagina web ufficiale del progetto R è invece https://www.r-project.org/ e in particolare CRAN https://cran.r-project.org/.
Al primo avvio, create un progetto in una cartella dedicata ad
esempio 750AA (è sempre buona pratica creare progetti per organizzare il
proprio flusso di lavoro) e caricate il file .Rmd
che
potete scaricare da questa pagina cliccando in alto a destra.
Su Rstudio potete eseguire righe di codice cliccando
Ctrl-Enter
o interi blocchi di codice con
Ctrl-Shift-Enter
.
Presentiamo ora i comandi e le caratteristiche di base di R – per
brevità la presentazione segue quella di https://learnxinyminutes.com/r. Un’ottima referenza
caldamente suggerita per usare R per l’analisi dei dati con maggiore
consapevolezza è R for Data Science (2e) disponibile alla
pagina https://r4ds.hadley.nz/. Una guida per le funzioni più
avanzate di R è Advanced R, https://adv-r.hadley.nz/index.html.
R è a tutti gli effetti un linguaggio di programmazione, ma si può
usare tranquillamente come una calcolatrice con molte funzioni per la
statistica: è facile potenziare R installando pacchetti aggiuntivi con
il comando install.packages()
.
# I commenti si indicano con il simbolo hash #
# installiamo il pacchetto cluster che contiene funzioni per il clustering di dati (lo useremo più avanti nel corso)
install.packages("cluster")
Una volta installato il pacchetto, questo può essere caricato con il
comando library()
.
library("cluster")
Se avete dubbi su un qualsiasi comando, potete cercare nella
documentazione ufficiale (tab Help in basso a destra su Rstudio) oppure
digitando ?(nome comando)
nella console (basso a
sinistra).
?hist
# il comando genera istogrammi a partire da un vettore di osservazioni
R contiene già molti dataset (termine tecnico in R è data
frame) standard pre-caricati, già a disposizione, che useremo negli
esempi (digitare il comando data()
per avere un elenco).
Anche le informazioni sui data frame principali sono accessibili tramite
l’help.
?mtcars
Con il comando head()
visualizziamo solo le prime righe
di un data frame.
head(mtcars)
Attenzione! per i progetti di esame è richiesto di lavorare su dati
nuovi (più avanti vediamo come caricarli). Ma iniziamo dai tipi di dati
più semplici.
Con il comando summary()
otteniamo informazioni circa la
distribuzione delle singole colonne del data frame.
summary(mtcars)
Con il comando plot()
otteniamo uno scatterplot tra
coppie di colonne (più avanti vediamo meglio).
plot(mtcars$mpg, mtcars$cyl)
Con il comando hist()
otteniamo invece un
istogramma.
hist(mtcars$mpg)
Un grafico a barre (utile se la variabile osservata è discreta o
qualitativa) è invece ottenibile con il comando
barplot()
.
barplot(table(factor(mtcars$cyl)))
Con il comando boxplot()
otteniamo un diagramma a
scatola e baffi (box and whiskers).
boxplot(mtcars$hp)
Classi di oggetti
In questa sezione presentiamo i tipi di oggetti importanti di R:
interi, numeri, caratteri, logici e fattori. Ce ne sono altri, ma questi
sono i minimi indispensabili per iniziare. R è abbastanza flessibile
sulle classi, ma se avete dubbi potete usare la funzione
class()
per avere informazioni precise.
?class
Integer
Gli interi (non decimali) sono specificati con L
.
5L
class(5L)
In R, ogni singolo valore, come 5L
, è considerato un
vettore di lunghezza 1.
length(5L)
Per avere vettori di lunghezza maggiore si può usare la funzione
c()
(concatena).
c(4L, 5L, 8L, 3L)
length(c(4L, 5L, 8L, 3L))
class(c(4L, 5L, 8L, 3L))
Numeric
Un numeric è un numero decimale (a precisione doppia)
5
class(5)
Di nuovo, tutto in R è un vettore, quindi possiamo creare un vettore
numerico con più di un elemento
c(3, 3, 3, 2, 2, 1)
Possiamo usare anche la notazione scientifica (AeB
significa \(A \cdot 10^B\))
5e4
6.02e23 # numero di Avogadro
1.6e-35 # lunghezza di Planck
Possiamo avere anche numeri infinitamente grandi con
Inf
.
class(Inf) # "numeric"
class(-Inf) # "numeric"
Un esempio: per ottenere la CDF dalla densità gaussiana (nel punto
\(-1\)): \(\Phi(-1) = \int_{-\infty}^{-1}\exp(-x^2/2)
dx\)
integrate(dnorm, -Inf, -1)
Confronta comunque con il comando pnorm()
:
pnorm(-1)
Fare operazioni su un mix di interi e numeric restituisce un
altro numeric:
10L + 66L
53.2 - 4
2.0 * 2L
3L / 4
3 %% 2
Operazioni non valide restituiscono NaN
, ossia
Not-A-Number.
0 / 0 # NaN
class(NaN) # "numeric"
Da non confondere con NA
, ossia Not-Available
per i dati mancanti.
NA
class(NA)
Possiamo fare operazioni su due vettori con lunghezza maggiore di 1,
# purché la lunghezza del vettore più grande sia un multiplo intero di
quello più piccolo
c(1, 2, 3) + c(1, 2, 3) # 2 4 6
Poiché un singolo numero è un vettore di lunghezza uno, gli scalari
sono applicati elemento per elemento ai vettori.
(4 * c(1, 2, 3) - 2) / 2 # 1 3 5
Ad eccezione degli scalari, attenzione quando eseguite operazioni su
vettori con lunghezze diverse. Anche se si può fare, allineare le
lunghezze è una pratica migliore e più facile da leggere nella maggior
parte dei casi.
c(1, 2, 3, 1, 2, 3) * c(1, 2) # 1 4 3 2 2 6
c(1, 2, 3, 1, 2, 3) * rep(c(1, 2), 3) # 1 4 3 2 2 6
Tante funzioni sono già implementate in R di base:
exp(1) # esponenziale a base naturale
log(3) # logaritmo base naturale
log(3, base=10) # logaritmo in base 10
sin(3) # seno (in radianti)
atan(1.3) # arcotangente
sqrt(2) # radice quadrata
2**(1/2) ## esponenziale
Character
Non c’è differenza tra caratteri e stringhe (sequenze di più
caratteri) in R:
"Statistica"
class("Statistica") # "character"
class("S") # "character"
Questi sono tutti vettori di caratteri di lunghezza 1. Al solito uno
più lungo si può ottenere concatenando.
c("Alice", "Bob", "Carlo", "Davide", "Elisabetta")
length(c("Alice","Bob","Carlo")) # 3
R ha diversi vettori di caratteri incorporati:
letters
LETTERS
month.name
Logic
In R, un oggetto logico è un booleano:
class(TRUE) # "logical"
class(FALSE) # "logical"
class(NA)
Espressioni possono essere confrontate con le operazioni booleane
==
(uguale), !=
, (diverso).
TRUE == TRUE # TRUE
5 == 3
FALSE != FALSE # FALSE
5!= 3
Si usano |
, &
, !
per le
operazioni logiche di disgiunzione (o) congiunzione (e) e negazione.
TRUE | FALSE # TRUE
TRUE & FALSE # FALSE
! (5==3)
Al solito le operazioni si applicano a vettori elemento per
elemento:
c(1,2,3,4)==c(1,5,3,2)
c(TRUE, FALSE) & TRUE
Factor
La classe factor (fattore) è per i dati categorici (variabili
statistiche discrete). I fattori possono essere ordinati, come ad
esempio i giudizi scolastici (ottimo, buono, ecc.) o non ordinati, ad
esempio come i colori. La funzione factor
trasforma un
vettore in un corrispondente vettore di tipo factor. Ad esempio
factor(c("blue", "blue", "green", NA, "blue", "red", "yellow"))
I livelli sono i valori che i dati categoriali
possono assumere. Notiamo che i dati mancanti non entrano nei livelli.
Per accedere ai livelli usiamo la funzione levels()
. La
funzione table()
permette di ottenere invece una
tabella di contingenza contenente le frequenze dei vari
livelli.
levels(factor(c("blue", "blue", "green", NA, "blue", "red", "yellow")))
table(factor(c("blue", "blue", "green", NA, "blue", "red", "yellow")))
NULL
L`oggetto NULL
è un caso strano, si può usare per
svuotare un vettore.
class(NULL) # NULL
pappagallo <- c("becco", "piume", "ali", "occhi")
pappagallo
pappagallo <- NULL
pappagallo
Variabili, cicli e funzioni
Una variabile è come una scatola in cui tenere un oggetto (valore)
per uso successivo. Questa operazione è detta di assegnazione del valore
alla variabile. Una volta che abbiamo variabili, possiamo scrivere cicli
(for, while, ecc.) e funzioni.
Variabili
Ci sono molti modi per assegnare un valore a una variabile.
x = 5 # questo è un modo accettato
y <- "1" # questo è il modo standard suggerito
TRUE -> z # questo pure funziona
Non ci sono convenzioni ufficiali per i nomi delle variabili.
Attenzione! i nomi sono case sensitive (maiuscola/minuscola fa
differenza) È vietato fare iniziare il nome con un numero, ed usare nomi
di oggetti fondamentali (TRUE
, FALSE
,
NULL
, ecc.). Per il resto, è buona pratica usare nomi che
siano esplicativi, ma anche non troppo lunghi. Quando introducete una
variabile possibilmente aggiungete un commento circa il suo ruolo.
DurataLezione <- 3 #Pascal case
durataLezione <- 3 #lower camelCase
durata_lezione <- 3 #lower_case_with_underscores, aka snake_case
Cicli (loops)
Ci sono i cicli for
.
for (i in 1:4) {
print(i)
}
Il comando A:B
definisce il vettore di interi da
A
a B
, estremi inclusi.
(-3):5
10:1
È possibile fare un ciclo su un qualsiasi vettore.
for( animale in c("cane", "gatto", "topo")){ print(animale)}
Ci sono i cicli while
, ossia che si ripetono finché la
condizione tra parentesi è realizzata (TRUE
)
a <- 10
while (a > 4) {
print(a)
a <- a - 1
}
In realtà usare i cicli for
e while
in R è
abbastanza lento, e quando le funzioni vengono applicate su interi
vettori conviene sfruttare il fatto che R lo fa in automatico.
numeri <- 1:10
# una sola riga per calcolare i quadrati dei numeri da 1 a 10
quadratiVeloci <- numeri**2
# usiamo un ciclo for
quadratiLenti <- NULL
for (i in numeri){
quadratiLenti <-c( quadratiLenti, i**2)
}
# il risultato è lo stesso
quadratiLenti == quadratiVeloci
If/else
Si può introdurre operazioni condizionali (if
,
else
)
x <- 4
y <- 5
if (x > y) {
print("x è maggiore di y")
} else if(x==y) {
print("x è uguale ad y")
} else {
print("x è minore di y")
}
Funzioni
È possibile definire nuove funzioni di variabili in questo modo:
sommaNuova <- function(x,y) {
z = x + y
return(z)
}
A questo punto è possibile utilizzarla come qualsiasi altra funzione
di R.
sommaNuova(3, 4)
È possibile definire funzioni con alcuni argomenti opzionali
(dopo quelli obbligatori).
sommaNuova <- function(x, y=0){
return(x+y)
}
sommaNuova(3)
Strutture di dati
Abbiamo già visto che R non distingue tra scalari e vettori (purché
siano dati dello stesso tipo). Vediamo ora ulteriori strutture come
matrici, data frames e liste.
Vettori
Abbiamo già visto i vettori e la funzioni di base
c()
.
vettore <- c(8, 9, 10, 11)
vettore
Ricordate anche il comando :
per sequenze di interi. Per
generare sequenze più complicate, anche non di interi, si può usare il
comando seq()
.
8:11
seq(8, 11, by=1)
seq(8, 11, by=0.5)
Il comando length()
ritorna la dimensione del
vettore.
length(vettore)
È spesso utile anche il comando rep
per replicare un
vettore.
rep(c(1,3), 2)
# crea un vettore di zeri lungo 4
rep(0, 4)
# crea un vettore di TRUE lungo 5
rep(TRUE, 5)
Possiamo chiamare uno o più elementi di un vettore nella posizione
\(k\) indicandoli tra parentesi
quadrate (usando un altro vettore contenente le posizioni da chiamare).
Attenzione! R comincia a contare da \(1\) (altri linguaggi da \(0\)).
vettore[1]
vettore[2]
vettore[c(1,4)]
vettore[1:3]
# ma invece
vettore[5]
vettore[0]
Usando indici negativi invece escludiamo quelle componenti:
vettore[-1]
vettore[-(1:3)]
Inserendo un vettore di valori booleani possiamo
filtrare solo le componenti corrispondenti ai valori
TRUE
.
maschera <- as.logical(vettore %% 2)
maschera
vettore[maschera]
Viceversa, data una condizione che può essere soddisfatta o meno,
possiamo estrarre gli indici per cui vale con il comando
which
.
which(vettore <9 | vettore > 10)
In questo caso mostra gli indici del vettore che corrispondono a
valori minori di \(9\) o maggiori di
\(10\). Può essere utile il comando
which.max()
che trova la posizione del valore massimo e
simimente which.min()
per il minimo.
vettore2 =c(1:10, 15:0)
# il comando max() trova il valore massimo
max(vettore2)
# il comando which.max() trova l'indice corrispondente (argmax, o punto di massimo)
which.max(vettore2)
I comandi `head()
e tail()
permettono di
ottenere le prime o le ultime componenti di un vettore.
head(1:1000)
tail(1:1000, n=10)
Indicatori statistici
Tante funzioni di statistica, in particolare descrittiva, sono già
implementate di base.
mean(vettore2)
var(vettore2)
sd(vettore2)
median(vettore2)
summary(vettore2)
quantile(vettore2, 1/4)
quantile(vettore2, 3/4)
quantile(vettore2, .95)
Per trovare la moda ad esempio di un vettore di fattori (variabile
statistica discreta) basta usare il comando which.max()
in
combinazione con table()
.
colori <- factor(c("black", "red", "blue", "blue", "green", NA, "blue", "red", "yellow"))
table(colori)
moda <- which.max(table(colori))
moda
Notate che il \(2\) non è la
frequenza (che sarebbe \(3\)) bensì la
posizione della moda nel vettore dei livelli.
Matrici
Possiamo creare una matrice con entrate tutte dello stesso tipo
(spesso numeric) cambiando la forma di un vettore, con il
comando matrix()
.
mat <- matrix(c(1, 2, 3, 4, 5, 6), nrow = 3, ncol = 2,)
mat
Diversamente da un vettore, la classe di una matrice è sempre
matrix
(anche se non contiene numeri).
class(mat)
Per accedere alla componente di riga \(i\) e colonna \(j\) (ricordare che si conta da \(1\)) si scrive mat[i,j]
. Per
chiedere l’intero vettore della riga \(i\) invece mat[i,]
, mentre per
la colonna \(j\) si scrive
mat[,j]
.
# prima riga
mat[1, ]
# tutte le colonne tranne la prima
mat[, -1]
# prima e terza riga, seconda colonna
mat[c(1,3), 2]
Operazioni di base su matrici sono già disponibili senza pacchetti
aggiuntivi.
# matrice trasposta
t(mat)
# prodotto di matrici (righe per colonne)
mat %*% t(mat)
# attenzione a non confondere usando solo * (prodotto componente per componente)
mat * mat
I comandi cbind()
e rbind()
uniscono
vettori (o matrici) tra loro, per colonne o per righe rispettivamente.
Attenzione! le matrici devono comunque avere entrate delle stesse classi
(se non lo sono vengono convertite).
matEstesa <- cbind(mat, c(1,3,10))
matEstesa
matEstesaZeri <- rbind(c(0,0,0), matEstesa)
matEstesaZeri
Data frame
Quando una tabella ha colonne di classi diverse, si usa un data
frame. Questa struttura è estremamente utile per rappresentare dati
(una osservazione per ciascuna riga) di cui si osservano caratteristiche
multiple (una caratteristica per ciascuna colonna). La funzione per
definire un data frame partendo da vettori (tutti con la stessa
lunghezza) è data.frame
.
altezza <- c(148, 170, NA, 179, 190, 168, 181, 158, 166)
# creiamo un data frame con i dati di altezza e colore
osservazioni <- data.frame(altezza, colori)
class(osservazioni)
Con il comando head()
possiamo visualizzare le prime
righe. Altri comandi permettono di ottenerne le dimensioni
head(osservazioni)
nrow(osservazioni)
ncol(osservazioni)
dim(osservazioni)
I nomi delle colonne possono essere visualizzati e pure cambiati con
la funzione colnames()
colnames(osservazioni)
colnames(osservazioni) <- c("height", "colors")
head(osservazioni)
Si può accedere alle singole colonne con la sintassi
nomeDataFrame$nomeColonna
. Ad esempio:
osservazioni$height
# la colonna selezionata è ora un vettore
class(osservazioni$height)
Se si vuole selezionare alcune colonne mantenendo la struttura di
data frame, si può indicarne ad esempio il numero.
head(osservazioni[1])
class(osservazioni[1])
Ci sono diverse estensioni della struttura dei data
frames. Una sono le data tables, per gestire grandi quantità di
dati (installare pacchetto data.table
). Un’altra sono le
tibble, per funzioni più intuitive e codice più pulito e
interpretabile (installare la suite tidyverse
https://www.tidyverse.org/).
install.packages("tidyverse")
Liste
Infine, R ha liste di vettori (anche di lunghezze e classi
diverse).
lista <- list(tempo = 1:40)
lista$prezzo = rnorm(10, 1,3)
class(lista)
lista$tempo[1:4]
lista$prezzo[1:4]
Le liste non sono efficienti per lavorare con grandi quantità di
osservazioni (usare data frames o data tables), ma è bene sapere che
alcune funzioni restituiscono una lista.
Plot
Il comando di base per le funzioni grafiche è plot()
.
Questo produce un diagramma a nuvola di punti
(scatterplot).
head(iris)
plot(iris$Sepal.Length, iris$Sepal.Width)
Il comando hist()
produce un istogramma.
hist(iris$Sepal.Length)
Il comando boxplot()
produce un diagramma a scatola.
boxplot(iris$Sepal.Length)
Questi plot tuttavia sono piuttosto di base, il
pacchetto ggplot2
(suite tidyverse
) permette
funzionalità più avanzate e comandi facilmente intepretabili.
Carichiamo il pacchetto (se non è installato usare
install.packages()
)
library("ggplot2")
Visualizziamo i plot di prima aggiungendo colori in base alle specie
(ultima colonna). Scatterplot:
ggplot(data=iris, aes(x=Sepal.Length, y=Sepal.Width, colour=Species)) +
geom_point() +
xlab("Lunghezza sepali") +
ylab("Lunghezza petali") +
ggtitle("Dataset Iris")
Istogramma:
ggplot(data=iris, aes(x=Sepal.Length, fill=Species)) +
geom_histogram(bins=10)+
xlab("Lunghezza sepali") +
ylab("frequenza assoluta") +
ggtitle("Dataset Iris")
Possiamo rappresentare anche con una densità
continua:
ggplot(data=iris, aes(x=Sepal.Length, fill=Species)) +
geom_density(position="stack")+
xlab("Lunghezza sepali") +
ylab("frequenza assoluta") +
ggtitle("Dataset Iris")
Possiamo anche rappresentare tre istogrammi (uno per specie) uno
accanto all’altro.
ggplot(data=iris, aes(x=Sepal.Length, fill=Species)) +
geom_histogram(bins=10, position=position_dodge())+
xlab("Lunghezza sepali") +
ylab("frequenza assoluta") +
ggtitle("Dataset Iris")
E pure le tre densità:
ggplot(data=iris, aes(x=Sepal.Length, fill=Species)) +
geom_density(alpha=0.4)+
xlab("Lunghezza sepali") +
ylab("frequenza assoluta") +
ggtitle("Dataset Iris")
Boxplot:
ggplot(iris, aes(x=Sepal.Length, fill=Species))+
geom_boxplot() +
xlab("Lunghezza sepali") +
ylab("") +
ggtitle("Dataset Iris")
LS0tCnRpdGxlOiAiSW50cm9kdXppb25lIGFkIFIgKG5vdGVib29rIDEpIgphdXRob3I6ICJEYXJpbyBUcmV2aXNhbiIKZGF0ZTogIjI0LzA5LzIwMjUiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGRvd25sb2FkX2hhbmRsZXI6IHRydWUKc3VidGl0bGU6ICJTdGF0aXN0aWNhIElJIC0gNzUwQUEiCi0tLQoKIyBJbnN0YWxsYXJlIFIgZWQgUnN0dWRpbwoKU2VndWl0ZSBsZSBpc3RydXppb25pIHBhcnRlbmRvIGRhbCBzaXRvIGRpIHBvc2l0OiA8aHR0cHM6Ly9wb3NpdC5jby9kb3dubG9hZC9yc3R1ZGlvLWRlc2t0b3AvPi4gTGEgKnBhZ2luYSB3ZWIgdWZmaWNpYWxlKiBkZWwgcHJvZ2V0dG8gUiDDqCBpbnZlY2UgPGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvPiBlIGluIHBhcnRpY29sYXJlIENSQU4gPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLz4uCgpBbCBwcmltbyBhdnZpbywgY3JlYXRlIHVuIHByb2dldHRvIGluIHVuYSBjYXJ0ZWxsYSBkZWRpY2F0YSBhZCBlc2VtcGlvIDc1MEFBICjDqCBzZW1wcmUgYnVvbmEgcHJhdGljYSBjcmVhcmUgcHJvZ2V0dGkgcGVyIG9yZ2FuaXp6YXJlIGlsIHByb3ByaW8gZmx1c3NvIGRpIGxhdm9ybykgZSBjYXJpY2F0ZSBpbCBmaWxlIGAuUm1kYCBjaGUgcG90ZXRlIHNjYXJpY2FyZSBkYSBxdWVzdGEgcGFnaW5hIGNsaWNjYW5kbyBpbiBhbHRvIGEgZGVzdHJhLgoKU3UgUnN0dWRpbyBwb3RldGUgZXNlZ3VpcmUgcmlnaGUgZGkgY29kaWNlIGNsaWNjYW5kbyBgQ3RybC1FbnRlcmAgbyBpbnRlcmkgYmxvY2NoaSBkaSBjb2RpY2UgY29uIGBDdHJsLVNoaWZ0LUVudGVyYC4KClByZXNlbnRpYW1vIG9yYSBpIGNvbWFuZGkgZSBsZSBjYXJhdHRlcmlzdGljaGUgZGkgYmFzZSBkaSBSIC0tIHBlciBicmV2aXTDoCBsYSBwcmVzZW50YXppb25lIHNlZ3VlIHF1ZWxsYSBkaSA8aHR0cHM6Ly9sZWFybnhpbnltaW51dGVzLmNvbS9yPi4gVW4nb3R0aW1hIHJlZmVyZW56YSBjYWxkYW1lbnRlIHN1Z2dlcml0YSBwZXIgdXNhcmUgUiBwZXIgbCdhbmFsaXNpIGRlaSBkYXRpIGNvbiBtYWdnaW9yZSBjb25zYXBldm9sZXp6YSDDqCAqUiBmb3IgRGF0YSBTY2llbmNlICgyZSkqIGRpc3BvbmliaWxlIGFsbGEgcGFnaW5hIDxodHRwczovL3I0ZHMuaGFkbGV5Lm56Lz4uIFVuYSBndWlkYSBwZXIgbGUgZnVuemlvbmkgcGnDuSBhdmFuemF0ZSBkaSBSIMOoICpBZHZhbmNlZCBSKiwgPGh0dHBzOi8vYWR2LXIuaGFkbGV5Lm56L2luZGV4Lmh0bWw+LgoKUiDDqCBhIHR1dHRpIGdsaSBlZmZldHRpIHVuIGxpbmd1YWdnaW8gZGkgcHJvZ3JhbW1hemlvbmUsIG1hIHNpIHB1w7IgdXNhcmUgdHJhbnF1aWxsYW1lbnRlIGNvbWUgdW5hIGNhbGNvbGF0cmljZSBjb24gbW9sdGUgZnVuemlvbmkgcGVyIGxhIHN0YXRpc3RpY2E6IMOoIGZhY2lsZSBwb3RlbnppYXJlIFIgaW5zdGFsbGFuZG8gcGFjY2hldHRpIGFnZ2l1bnRpdmkgY29uIGlsIGNvbWFuZG8gYGluc3RhbGwucGFja2FnZXMoKWAuCgpgYGB7ciBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CiMgSSBjb21tZW50aSBzaSBpbmRpY2FubyBjb24gaWwgc2ltYm9sbyBoYXNoICMKIyBpbnN0YWxsaWFtbyBpbCBwYWNjaGV0dG8gY2x1c3RlciBjaGUgY29udGllbmUgZnVuemlvbmkgcGVyIGlsIGNsdXN0ZXJpbmcgZGkgZGF0aSAobG8gdXNlcmVtbyBwacO5IGF2YW50aSBuZWwgY29yc28pCgppbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIikKYGBgCgpVbmEgdm9sdGEgaW5zdGFsbGF0byBpbCBwYWNjaGV0dG8sIHF1ZXN0byBwdcOyIGVzc2VyZSBjYXJpY2F0byBjb24gaWwgY29tYW5kbyBgbGlicmFyeSgpYC4KCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KbGlicmFyeSgiY2x1c3RlciIpCmBgYAoKU2UgYXZldGUgZHViYmkgc3UgdW4gcXVhbHNpYXNpIGNvbWFuZG8sIHBvdGV0ZSBjZXJjYXJlIG5lbGxhIGRvY3VtZW50YXppb25lIHVmZmljaWFsZSAodGFiIEhlbHAgaW4gYmFzc28gYSBkZXN0cmEgc3UgUnN0dWRpbykgb3BwdXJlIGRpZ2l0YW5kbyBgPyhub21lIGNvbWFuZG8pYCBuZWxsYSBjb25zb2xlIChiYXNzbyBhIHNpbmlzdHJhKS4KCmBgYHtyfQo/aGlzdAojIGlsIGNvbWFuZG8gZ2VuZXJhIGlzdG9ncmFtbWkgYSBwYXJ0aXJlIGRhIHVuIHZldHRvcmUgZGkgb3NzZXJ2YXppb25pCmBgYAoKUiBjb250aWVuZSBnacOgIG1vbHRpIGRhdGFzZXQgKHRlcm1pbmUgdGVjbmljbyBpbiBSIMOoICpkYXRhIGZyYW1lKikgc3RhbmRhcmQgcHJlLWNhcmljYXRpLCBnacOgIGEgZGlzcG9zaXppb25lLCBjaGUgdXNlcmVtbyBuZWdsaSBlc2VtcGkgKGRpZ2l0YXJlIGlsIGNvbWFuZG8gYGRhdGEoKWAgcGVyIGF2ZXJlIHVuIGVsZW5jbykuIEFuY2hlIGxlIGluZm9ybWF6aW9uaSBzdWkgZGF0YSBmcmFtZSBwcmluY2lwYWxpIHNvbm8gYWNjZXNzaWJpbGkgdHJhbWl0ZSBsJ2hlbHAuCgpgYGB7cn0KP210Y2FycwpgYGAKCkNvbiBpbCBjb21hbmRvIGBoZWFkKClgIHZpc3VhbGl6emlhbW8gc29sbyBsZSBwcmltZSByaWdoZSBkaSB1biBkYXRhIGZyYW1lLgoKYGBge3J9CmhlYWQobXRjYXJzKQpgYGAKCkF0dGVuemlvbmUhIHBlciBpIHByb2dldHRpIGRpIGVzYW1lIMOoIHJpY2hpZXN0byBkaSBsYXZvcmFyZSBzdSBkYXRpIG51b3ZpIChwacO5IGF2YW50aSB2ZWRpYW1vIGNvbWUgY2FyaWNhcmxpKS4gTWEgaW5pemlhbW8gZGFpIHRpcGkgZGkgZGF0aSBwacO5IHNlbXBsaWNpLgoKQ29uIGlsIGNvbWFuZG8gYHN1bW1hcnkoKWAgb3R0ZW5pYW1vIGluZm9ybWF6aW9uaSBjaXJjYSBsYSBkaXN0cmlidXppb25lIGRlbGxlIHNpbmdvbGUgY29sb25uZSBkZWwgZGF0YSBmcmFtZS4KCmBgYHtyfQpzdW1tYXJ5KG10Y2FycykKYGBgCgpDb24gaWwgY29tYW5kbyBgcGxvdCgpYCBvdHRlbmlhbW8gdW5vIHNjYXR0ZXJwbG90IHRyYSBjb3BwaWUgZGkgY29sb25uZSAocGnDuSBhdmFudGkgdmVkaWFtbyBtZWdsaW8pLgoKYGBge3J9CnBsb3QobXRjYXJzJG1wZywgbXRjYXJzJGN5bCkKYGBgCgpDb24gaWwgY29tYW5kbyBgaGlzdCgpYCBvdHRlbmlhbW8gaW52ZWNlIHVuIGlzdG9ncmFtbWEuCgpgYGB7cn0KaGlzdChtdGNhcnMkbXBnKQpgYGAKClVuIGdyYWZpY28gYSBiYXJyZSAodXRpbGUgc2UgbGEgdmFyaWFiaWxlIG9zc2VydmF0YSDDqCBkaXNjcmV0YSBvIHF1YWxpdGF0aXZhKSDDqCBpbnZlY2Ugb3R0ZW5pYmlsZSBjb24gaWwgY29tYW5kbyBgYmFycGxvdCgpYC4KCmBgYHtyfQpiYXJwbG90KHRhYmxlKGZhY3RvcihtdGNhcnMkY3lsKSkpCmBgYAoKQ29uIGlsIGNvbWFuZG8gYGJveHBsb3QoKWAgb3R0ZW5pYW1vIHVuIGRpYWdyYW1tYSBhICpzY2F0b2xhIGUgYmFmZmkqIChib3ggYW5kIHdoaXNrZXJzKS4KCmBgYHtyfQpib3hwbG90KG10Y2FycyRocCkKYGBgCgojIENsYXNzaSBkaSBvZ2dldHRpCgpJbiBxdWVzdGEgc2V6aW9uZSBwcmVzZW50aWFtbyBpIHRpcGkgZGkgb2dnZXR0aSBpbXBvcnRhbnRpIGRpIFI6IGludGVyaSwgbnVtZXJpLCBjYXJhdHRlcmksIGxvZ2ljaSBlIGZhdHRvcmkuIENlIG5lIHNvbm8gYWx0cmksIG1hIHF1ZXN0aSBzb25vIGkgbWluaW1pIGluZGlzcGVuc2FiaWxpIHBlciBpbml6aWFyZS4gUiDDqCBhYmJhc3RhbnphIGZsZXNzaWJpbGUgc3VsbGUgY2xhc3NpLCBtYSBzZSBhdmV0ZSBkdWJiaSBwb3RldGUgdXNhcmUgbGEgZnVuemlvbmUgYGNsYXNzKClgIHBlciBhdmVyZSBpbmZvcm1hemlvbmkgcHJlY2lzZS4KCmBgYHtyfQo/Y2xhc3MKYGBgCgojIyBJbnRlZ2VyCgpHbGkgaW50ZXJpIChub24gZGVjaW1hbGkpIHNvbm8gc3BlY2lmaWNhdGkgY29uIGBMYC4KCmBgYHtyfQo1TCAgICAgICAgIApjbGFzcyg1TCkKYGBgCgpJbiBSLCBvZ25pIHNpbmdvbG8gdmFsb3JlLCBjb21lIGA1TGAsIMOoIGNvbnNpZGVyYXRvIHVuIHZldHRvcmUgZGkgbHVuZ2hlenphIDEuCgpgYGB7cn0KbGVuZ3RoKDVMKQpgYGAKClBlciBhdmVyZSB2ZXR0b3JpIGRpIGx1bmdoZXp6YSBtYWdnaW9yZSBzaSBwdcOyIHVzYXJlIGxhIGZ1bnppb25lIGBjKClgIChjb25jYXRlbmEpLgoKYGBge3J9CmMoNEwsIDVMLCA4TCwgM0wpICAgICAgICAgIApsZW5ndGgoYyg0TCwgNUwsIDhMLCAzTCkpICAKY2xhc3MoYyg0TCwgNUwsIDhMLCAzTCkpCmBgYAoKIyMgTnVtZXJpYwoKVW4gKm51bWVyaWMqIMOoIHVuIG51bWVybyBkZWNpbWFsZSAoYSBwcmVjaXNpb25lIGRvcHBpYSkKCmBgYHtyfQo1ICAgICAgICAgICAKY2xhc3MoNSkgICAgCmBgYAoKRGkgbnVvdm8sIHR1dHRvIGluIFIgw6ggdW4gdmV0dG9yZSwgcXVpbmRpIHBvc3NpYW1vIGNyZWFyZSB1biB2ZXR0b3JlIG51bWVyaWNvIGNvbiBwacO5IGRpIHVuIGVsZW1lbnRvCgpgYGB7cn0KYygzLCAzLCAzLCAyLCAyLCAxKQpgYGAKClBvc3NpYW1vIHVzYXJlIGFuY2hlIGxhIG5vdGF6aW9uZSBzY2llbnRpZmljYSAoYEFlQmAgc2lnbmlmaWNhICRBIFxjZG90IDEwXkIkKQoKYGBge3J9CjVlNCAgICAgICAgIAo2LjAyZTIzICAgICAjIG51bWVybyBkaSBBdm9nYWRybwoxLjZlLTM1ICAgICAjIGx1bmdoZXp6YSBkaSBQbGFuY2sKYGBgCgpQb3NzaWFtbyBhdmVyZSBhbmNoZSBudW1lcmkgaW5maW5pdGFtZW50ZSBncmFuZGkgY29uIGBJbmZgLgoKYGBge3J9CmNsYXNzKEluZikgICMgIm51bWVyaWMiCmNsYXNzKC1JbmYpICMgIm51bWVyaWMiCmBgYAoKVW4gZXNlbXBpbzogcGVyIG90dGVuZXJlIGxhIENERiBkYWxsYSBkZW5zaXTDoCBnYXVzc2lhbmEgKG5lbCBwdW50byAkLTEkKTogJFxQaGkoLTEpID0gXGludF97LVxpbmZ0eX1eey0xfVxleHAoLXheMi8yKSBkeCQKCmBgYHtyfQppbnRlZ3JhdGUoZG5vcm0sIC1JbmYsIC0xKQpgYGAKCkNvbmZyb250YSBjb211bnF1ZSBjb24gaWwgY29tYW5kbyBgcG5vcm0oKWA6CgpgYGB7cn0KcG5vcm0oLTEpCmBgYAoKRmFyZSBvcGVyYXppb25pIHN1IHVuIG1peCBkaSBpbnRlcmkgZSAqbnVtZXJpYyogcmVzdGl0dWlzY2UgdW4gYWx0cm8gKm51bWVyaWMqOgoKYGBge3J9CjEwTCArIDY2TCAgIAo1My4yIC0gNCAgIAoyLjAgKiAyTCAgIAozTCAvIDQgICAgIAozICUlIDIKYGBgCgpPcGVyYXppb25pIG5vbiB2YWxpZGUgcmVzdGl0dWlzY29ubyBgTmFOYCwgb3NzaWEgKk5vdC1BLU51bWJlciouCgpgYGB7cn0KMCAvIDAgICAgICAgIyBOYU4KY2xhc3MoTmFOKSAgIyAibnVtZXJpYyIKYGBgCgpEYSBub24gY29uZm9uZGVyZSBjb24gYE5BYCwgb3NzaWEgKk5vdC1BdmFpbGFibGUqIHBlciBpIGRhdGkgbWFuY2FudGkuCgpgYGB7cn0KTkEKY2xhc3MoTkEpCmBgYAoKUG9zc2lhbW8gZmFyZSBvcGVyYXppb25pIHN1IGR1ZSB2ZXR0b3JpIGNvbiBsdW5naGV6emEgbWFnZ2lvcmUgZGkgMSwgXCMgcHVyY2jDqSBsYSBsdW5naGV6emEgZGVsIHZldHRvcmUgcGnDuSBncmFuZGUgc2lhIHVuIG11bHRpcGxvIGludGVybyBkaSBxdWVsbG8gcGnDuSBwaWNjb2xvCgpgYGB7cn0KYygxLCAyLCAzKSArIGMoMSwgMiwgMykgICAgICMgMiA0IDYKYGBgCgpQb2ljaMOpIHVuIHNpbmdvbG8gbnVtZXJvIMOoIHVuIHZldHRvcmUgZGkgbHVuZ2hlenphIHVubywgZ2xpIHNjYWxhcmkgc29ubyBhcHBsaWNhdGkgZWxlbWVudG8gcGVyIGVsZW1lbnRvIGFpIHZldHRvcmkuCgpgYGB7cn0KKDQgKiBjKDEsIDIsIDMpIC0gMikgLyAyICAgICMgMSAzIDUKYGBgCgpBZCBlY2NlemlvbmUgZGVnbGkgc2NhbGFyaSwgYXR0ZW56aW9uZSBxdWFuZG8gZXNlZ3VpdGUgb3BlcmF6aW9uaSBzdSB2ZXR0b3JpIGNvbiBsdW5naGV6emUgZGl2ZXJzZS4gQW5jaGUgc2Ugc2kgcHXDsiBmYXJlLCBhbGxpbmVhcmUgbGUgbHVuZ2hlenplIMOoIHVuYSBwcmF0aWNhIG1pZ2xpb3JlIGUgcGnDuSBmYWNpbGUgZGEgbGVnZ2VyZSBuZWxsYSBtYWdnaW9yIHBhcnRlIGRlaSBjYXNpLgoKYGBge3J9CmMoMSwgMiwgMywgMSwgMiwgMykgKiBjKDEsIDIpICAgICAgICAgICAgICAgIyAxIDQgMyAyIDIgNgpjKDEsIDIsIDMsIDEsIDIsIDMpICogcmVwKGMoMSwgMiksIDMpICAgIyAxIDQgMyAyIDIgNgpgYGAKClRhbnRlIGZ1bnppb25pIHNvbm8gZ2nDoCBpbXBsZW1lbnRhdGUgaW4gUiBkaSBiYXNlOgoKYGBge3J9CgpleHAoMSkgIyBlc3BvbmVuemlhbGUgYSBiYXNlIG5hdHVyYWxlCmxvZygzKSAjIGxvZ2FyaXRtbyBiYXNlIG5hdHVyYWxlCmxvZygzLCBiYXNlPTEwKSAjIGxvZ2FyaXRtbyBpbiBiYXNlIDEwCnNpbigzKSAjIHNlbm8gKGluIHJhZGlhbnRpKQphdGFuKDEuMykgIyBhcmNvdGFuZ2VudGUKc3FydCgyKSAjIHJhZGljZSBxdWFkcmF0YQoyKiooMS8yKSAjIyBlc3BvbmVuemlhbGUKYGBgCgojIyBDaGFyYWN0ZXIKCk5vbiBjJ8OoIGRpZmZlcmVuemEgdHJhIGNhcmF0dGVyaSBlIHN0cmluZ2hlIChzZXF1ZW56ZSBkaSBwacO5IGNhcmF0dGVyaSkgaW4gUjoKCmBgYHtyfQoiU3RhdGlzdGljYSIgICAgICAgICAgCmNsYXNzKCJTdGF0aXN0aWNhIikgICAgIyAiY2hhcmFjdGVyIgpjbGFzcygiUyIpICAgICAgICAgICMgImNoYXJhY3RlciIKYGBgCgpRdWVzdGkgc29ubyB0dXR0aSB2ZXR0b3JpIGRpIGNhcmF0dGVyaSBkaSBsdW5naGV6emEgMS4gQWwgc29saXRvIHVubyBwacO5IGx1bmdvIHNpIHB1w7Igb3R0ZW5lcmUgY29uY2F0ZW5hbmRvLgoKYGBge3J9CmMoIkFsaWNlIiwgIkJvYiIsICJDYXJsbyIsICJEYXZpZGUiLCAiRWxpc2FiZXR0YSIpCmBgYAoKYGBge3J9Cmxlbmd0aChjKCJBbGljZSIsIkJvYiIsIkNhcmxvIikpICMgMwpgYGAKClIgaGEgZGl2ZXJzaSB2ZXR0b3JpIGRpIGNhcmF0dGVyaSBpbmNvcnBvcmF0aToKCmBgYHtyfQpsZXR0ZXJzCkxFVFRFUlMKbW9udGgubmFtZQpgYGAKCiMjIExvZ2ljCgpJbiBSLCB1biBvZ2dldHRvICoqbG9naWNvKiogw6ggdW4gYm9vbGVhbm86CgpgYGB7cn0KY2xhc3MoVFJVRSkgICAgICMgImxvZ2ljYWwiCmNsYXNzKEZBTFNFKSAgICAjICJsb2dpY2FsIgpjbGFzcyhOQSkKYGBgCgpFc3ByZXNzaW9uaSBwb3Nzb25vIGVzc2VyZSBjb25mcm9udGF0ZSBjb24gbGUgb3BlcmF6aW9uaSBib29sZWFuZSBgPT1gICh1Z3VhbGUpLCBgIT1gLCAoZGl2ZXJzbykuCgpgYGB7cn0KVFJVRSA9PSBUUlVFICAgICMgVFJVRQo1ID09IDMKRkFMU0UgIT0gRkFMU0UgICMgRkFMU0UKNSE9IDMKYGBgCgpTaSB1c2FubyBgfGAsIGAmYCwgYCFgIHBlciBsZSBvcGVyYXppb25pIGxvZ2ljaGUgZGkgZGlzZ2l1bnppb25lIChvKSBjb25naXVuemlvbmUgKGUpIGUgbmVnYXppb25lLgoKYGBge3J9ClRSVUUgfCBGQUxTRSAgICAjIFRSVUUKVFJVRSAmIEZBTFNFICAgICMgRkFMU0UKISAoNT09MykKYGBgCgpBbCBzb2xpdG8gbGUgb3BlcmF6aW9uaSBzaSBhcHBsaWNhbm8gYSB2ZXR0b3JpIGVsZW1lbnRvIHBlciBlbGVtZW50bzoKCmBgYHtyfQpjKDEsMiwzLDQpPT1jKDEsNSwzLDIpCgpjKFRSVUUsIEZBTFNFKSAmIFRSVUUKCmBgYAoKIyMgRmFjdG9yCgpMYSBjbGFzc2UgZmFjdG9yIChmYXR0b3JlKSDDqCBwZXIgaSBkYXRpIGNhdGVnb3JpY2kgKHZhcmlhYmlsaSBzdGF0aXN0aWNoZSBkaXNjcmV0ZSkuIEkgZmF0dG9yaSBwb3Nzb25vIGVzc2VyZSBvcmRpbmF0aSwgY29tZSBhZCBlc2VtcGlvIGkgZ2l1ZGl6aSBzY29sYXN0aWNpIChvdHRpbW8sIGJ1b25vLCBlY2MuKSBvIG5vbiBvcmRpbmF0aSwgYWQgZXNlbXBpbyBjb21lIGkgY29sb3JpLiBMYSBmdW56aW9uZSBgZmFjdG9yYCB0cmFzZm9ybWEgdW4gdmV0dG9yZSBpbiB1biBjb3JyaXNwb25kZW50ZSB2ZXR0b3JlIGRpIHRpcG8gZmFjdG9yLiBBZCBlc2VtcGlvCgpgYGB7cn0KZmFjdG9yKGMoImJsdWUiLCAiYmx1ZSIsICJncmVlbiIsIE5BLCAiYmx1ZSIsICJyZWQiLCAieWVsbG93IikpCmBgYAoKSSAqKmxpdmVsbGkqKiBzb25vIGkgdmFsb3JpIGNoZSBpIGRhdGkgY2F0ZWdvcmlhbGkgcG9zc29ubyBhc3N1bWVyZS4gTm90aWFtbyBjaGUgaSBkYXRpIG1hbmNhbnRpIG5vbiBlbnRyYW5vIG5laSBsaXZlbGxpLiBQZXIgYWNjZWRlcmUgYWkgbGl2ZWxsaSB1c2lhbW8gbGEgZnVuemlvbmUgYGxldmVscygpYC4gTGEgZnVuemlvbmUgYHRhYmxlKClgIHBlcm1ldHRlIGRpIG90dGVuZXJlIGludmVjZSB1bmEgKnRhYmVsbGEgZGkgY29udGluZ2VuemEqIGNvbnRlbmVudGUgbGUgZnJlcXVlbnplIGRlaSB2YXJpIGxpdmVsbGkuCgpgYGB7cn0KbGV2ZWxzKGZhY3RvcihjKCJibHVlIiwgImJsdWUiLCAiZ3JlZW4iLCBOQSwgImJsdWUiLCAicmVkIiwgInllbGxvdyIpKSkKdGFibGUoZmFjdG9yKGMoImJsdWUiLCAiYmx1ZSIsICJncmVlbiIsIE5BLCAiYmx1ZSIsICJyZWQiLCAieWVsbG93IikpKQpgYGAKCiMjIE5VTEwKCkxcYG9nZ2V0dG8gYE5VTExgIMOoIHVuIGNhc28gc3RyYW5vLCBzaSBwdcOyIHVzYXJlIHBlciAqc3Z1b3RhcmUqIHVuIHZldHRvcmUuCgpgYGB7cn0KY2xhc3MoTlVMTCkgIyBOVUxMCnBhcHBhZ2FsbG8gPC0gYygiYmVjY28iLCAicGl1bWUiLCAiYWxpIiwgIm9jY2hpIikKcGFwcGFnYWxsbwoKcGFwcGFnYWxsbyA8LSBOVUxMCgpwYXBwYWdhbGxvCmBgYAoKIyBWYXJpYWJpbGksIGNpY2xpIGUgZnVuemlvbmkKClVuYSB2YXJpYWJpbGUgw6ggY29tZSB1bmEgc2NhdG9sYSBpbiBjdWkgdGVuZXJlIHVuIG9nZ2V0dG8gKHZhbG9yZSkgcGVyIHVzbyBzdWNjZXNzaXZvLiBRdWVzdGEgb3BlcmF6aW9uZSDDqCBkZXR0YSBkaSBhc3NlZ25hemlvbmUgZGVsIHZhbG9yZSBhbGxhIHZhcmlhYmlsZS4gVW5hIHZvbHRhIGNoZSBhYmJpYW1vIHZhcmlhYmlsaSwgcG9zc2lhbW8gc2NyaXZlcmUgY2ljbGkgKGZvciwgd2hpbGUsIGVjYy4pIGUgZnVuemlvbmkuCgojIyBWYXJpYWJpbGkKCkNpIHNvbm8gbW9sdGkgbW9kaSBwZXIgYXNzZWduYXJlIHVuIHZhbG9yZSBhIHVuYSB2YXJpYWJpbGUuCgpgYGB7cn0KeCA9IDUgICAgICAgIyBxdWVzdG8gw6ggdW4gbW9kbyBhY2NldHRhdG8KeSA8LSAiMSIgICAgIyBxdWVzdG8gw6ggaWwgbW9kbyBzdGFuZGFyZCBzdWdnZXJpdG8KVFJVRSAtPiB6ICAgIyBxdWVzdG8gcHVyZSBmdW56aW9uYQpgYGAKCk5vbiBjaSBzb25vIGNvbnZlbnppb25pIHVmZmljaWFsaSBwZXIgaSBub21pIGRlbGxlIHZhcmlhYmlsaS4gQXR0ZW56aW9uZSEgaSBub21pIHNvbm8gKmNhc2Ugc2Vuc2l0aXZlKiAobWFpdXNjb2xhL21pbnVzY29sYSBmYSBkaWZmZXJlbnphKSDDiCB2aWV0YXRvIGZhcmUgaW5pemlhcmUgaWwgbm9tZSBjb24gdW4gbnVtZXJvLCBlZCB1c2FyZSBub21pIGRpIG9nZ2V0dGkgZm9uZGFtZW50YWxpIChgVFJVRWAsIGBGQUxTRWAsIGBOVUxMYCwgZWNjLikuIFBlciBpbCByZXN0bywgw6ggYnVvbmEgcHJhdGljYSB1c2FyZSBub21pIGNoZSBzaWFubyBlc3BsaWNhdGl2aSwgbWEgYW5jaGUgbm9uIHRyb3BwbyBsdW5naGkuIFF1YW5kbyBpbnRyb2R1Y2V0ZSB1bmEgdmFyaWFiaWxlIHBvc3NpYmlsbWVudGUgYWdnaXVuZ2V0ZSB1biBjb21tZW50byBjaXJjYSBpbCBzdW8gcnVvbG8uCgpgYGB7cn0KRHVyYXRhTGV6aW9uZSA8LSAzICNQYXNjYWwgY2FzZQpkdXJhdGFMZXppb25lIDwtIDMgI2xvd2VyIGNhbWVsQ2FzZQpkdXJhdGFfbGV6aW9uZSA8LSAzICNsb3dlcl9jYXNlX3dpdGhfdW5kZXJzY29yZXMsIGFrYSBzbmFrZV9jYXNlCmBgYAoKIyMgQ2ljbGkgKGxvb3BzKQoKQ2kgc29ubyBpIGNpY2xpIGBmb3JgLgoKYGBge3J9CmZvciAoaSBpbiAxOjQpIHsKCXByaW50KGkpCn0KYGBgCgpJbCBjb21hbmRvIGBBOkJgIGRlZmluaXNjZSBpbCB2ZXR0b3JlIGRpIGludGVyaSBkYSBgQWAgYSBgQmAsIGVzdHJlbWkgaW5jbHVzaS4KCmBgYHtyfQooLTMpOjUKCjEwOjEKYGBgCgrDiCBwb3NzaWJpbGUgZmFyZSB1biBjaWNsbyBzdSB1biBxdWFsc2lhc2kgdmV0dG9yZS4KCmBgYHtyfQoKZm9yKCBhbmltYWxlIGluIGMoImNhbmUiLCAiZ2F0dG8iLCAidG9wbyIpKXsgcHJpbnQoYW5pbWFsZSl9CgpgYGAKCkNpIHNvbm8gaSBjaWNsaSBgd2hpbGVgLCBvc3NpYSBjaGUgc2kgcmlwZXRvbm8gZmluY2jDqSBsYSBjb25kaXppb25lIHRyYSBwYXJlbnRlc2kgw6ggcmVhbGl6emF0YSAoYFRSVUVgKQoKYGBge3J9CmEgPC0gMTAKd2hpbGUgKGEgPiA0KSB7CglwcmludChhKQoJYSA8LSBhIC0gMQp9CmBgYAoKSW4gcmVhbHTDoCB1c2FyZSBpIGNpY2xpIGBmb3JgIGUgYHdoaWxlYCBpbiBSIMOoIGFiYmFzdGFuemEgbGVudG8sIGUgcXVhbmRvIGxlIGZ1bnppb25pIHZlbmdvbm8gYXBwbGljYXRlIHN1IGludGVyaSB2ZXR0b3JpIGNvbnZpZW5lIHNmcnV0dGFyZSBpbCBmYXR0byBjaGUgUiBsbyBmYSBpbiBhdXRvbWF0aWNvLgoKYGBge3J9CgpudW1lcmkgPC0gMToxMAoKIyB1bmEgc29sYSByaWdhIHBlciBjYWxjb2xhcmUgaSBxdWFkcmF0aSBkZWkgbnVtZXJpIGRhIDEgYSAxMApxdWFkcmF0aVZlbG9jaSA8LSBudW1lcmkqKjIKCiMgdXNpYW1vIHVuIGNpY2xvIGZvcgpxdWFkcmF0aUxlbnRpIDwtIE5VTEwKZm9yIChpIGluIG51bWVyaSl7CiAgcXVhZHJhdGlMZW50aSA8LWMoIHF1YWRyYXRpTGVudGksIGkqKjIpCn0KCiMgaWwgcmlzdWx0YXRvIMOoIGxvIHN0ZXNzbwpxdWFkcmF0aUxlbnRpID09IHF1YWRyYXRpVmVsb2NpCgpgYGAKCiMjIElmL2Vsc2UKClNpIHB1w7IgaW50cm9kdXJyZSBvcGVyYXppb25pIGNvbmRpemlvbmFsaSAoYGlmYCwgYGVsc2VgKQoKYGBge3J9CnggPC0gNAp5IDwtIDUKCmlmICh4ID4geSkgewoJcHJpbnQoInggw6ggbWFnZ2lvcmUgZGkgeSIpCn0gZWxzZSBpZih4PT15KSB7CglwcmludCgieCDDqCB1Z3VhbGUgYWQgeSIpCn0gZWxzZSB7CiAgcHJpbnQoInggw6ggbWlub3JlIGRpIHkiKQp9CmBgYAoKIyMgRnVuemlvbmkKCsOIIHBvc3NpYmlsZSBkZWZpbmlyZSBudW92ZSBmdW56aW9uaSBkaSB2YXJpYWJpbGkgaW4gcXVlc3RvIG1vZG86CgpgYGB7cn0Kc29tbWFOdW92YSA8LSBmdW5jdGlvbih4LHkpIHsKCXogPSB4ICArIHkgCglyZXR1cm4oeikKfQpgYGAKCkEgcXVlc3RvIHB1bnRvIMOoIHBvc3NpYmlsZSB1dGlsaXp6YXJsYSBjb21lIHF1YWxzaWFzaSBhbHRyYSBmdW56aW9uZSBkaSBSLgoKYGBge3J9CnNvbW1hTnVvdmEoMywgNCkKYGBgCgrDiCBwb3NzaWJpbGUgZGVmaW5pcmUgZnVuemlvbmkgY29uIGFsY3VuaSBhcmdvbWVudGkgKm9wemlvbmFsaSogKGRvcG8gcXVlbGxpIG9iYmxpZ2F0b3JpKS4KCmBgYHtyfQoKc29tbWFOdW92YSA8LSBmdW5jdGlvbih4LCB5PTApewogIHJldHVybih4K3kpCn0KCnNvbW1hTnVvdmEoMykKYGBgCgojIFN0cnV0dHVyZSBkaSBkYXRpCgpBYmJpYW1vIGdpw6AgdmlzdG8gY2hlIFIgbm9uIGRpc3Rpbmd1ZSB0cmEgc2NhbGFyaSBlIHZldHRvcmkgKHB1cmNow6kgc2lhbm8gZGF0aSBkZWxsbyBzdGVzc28gdGlwbykuIFZlZGlhbW8gb3JhIHVsdGVyaW9yaSBzdHJ1dHR1cmUgY29tZSBtYXRyaWNpLCAqZGF0YSBmcmFtZXMqIGUgbGlzdGUuCgojIyBWZXR0b3JpCgpBYmJpYW1vIGdpw6AgdmlzdG8gaSB2ZXR0b3JpIGUgbGEgZnVuemlvbmkgZGkgYmFzZSBgYygpYC4KCmBgYHtyfQp2ZXR0b3JlIDwtIGMoOCwgOSwgMTAsIDExKQp2ZXR0b3JlCmBgYAoKUmljb3JkYXRlIGFuY2hlIGlsIGNvbWFuZG8gYDpgIHBlciBzZXF1ZW56ZSBkaSBpbnRlcmkuIFBlciBnZW5lcmFyZSBzZXF1ZW56ZSBwacO5IGNvbXBsaWNhdGUsIGFuY2hlIG5vbiBkaSBpbnRlcmksIHNpIHB1w7IgdXNhcmUgaWwgY29tYW5kbyBgc2VxKClgLgoKYGBge3J9Cjg6MTEKc2VxKDgsIDExLCBieT0xKQpzZXEoOCwgMTEsIGJ5PTAuNSkKYGBgCgpJbCBjb21hbmRvIGBsZW5ndGgoKWAgcml0b3JuYSBsYSBkaW1lbnNpb25lIGRlbCB2ZXR0b3JlLgoKYGBge3J9Cmxlbmd0aCh2ZXR0b3JlKQpgYGAKCsOIIHNwZXNzbyB1dGlsZSBhbmNoZSBpbCBjb21hbmRvIGByZXBgIHBlciByZXBsaWNhcmUgdW4gdmV0dG9yZS4KCmBgYHtyfQoKcmVwKGMoMSwzKSwgMikKCiMgY3JlYSB1biB2ZXR0b3JlIGRpIHplcmkgbHVuZ28gNAoKcmVwKDAsIDQpCgojIGNyZWEgdW4gdmV0dG9yZSBkaSBUUlVFIGx1bmdvIDUKCnJlcChUUlVFLCA1KQoKYGBgCgpQb3NzaWFtbyBjaGlhbWFyZSB1bm8gbyBwacO5IGVsZW1lbnRpIGRpIHVuIHZldHRvcmUgbmVsbGEgcG9zaXppb25lICRrJCBpbmRpY2FuZG9saSB0cmEgcGFyZW50ZXNpIHF1YWRyYXRlICh1c2FuZG8gdW4gYWx0cm8gdmV0dG9yZSBjb250ZW5lbnRlIGxlIHBvc2l6aW9uaSBkYSBjaGlhbWFyZSkuIEF0dGVuemlvbmUhIFIgY29taW5jaWEgYSBjb250YXJlIGRhICQxJCAoYWx0cmkgbGluZ3VhZ2dpIGRhICQwJCkuCgpgYGB7cn0KdmV0dG9yZVsxXQp2ZXR0b3JlWzJdCnZldHRvcmVbYygxLDQpXQp2ZXR0b3JlWzE6M10KCiMgbWEgaW52ZWNlCnZldHRvcmVbNV0KdmV0dG9yZVswXQpgYGAKClVzYW5kbyBpbmRpY2kgbmVnYXRpdmkgaW52ZWNlIGVzY2x1ZGlhbW8gcXVlbGxlIGNvbXBvbmVudGk6CgpgYGB7cn0KdmV0dG9yZVstMV0KdmV0dG9yZVstKDE6MyldCmBgYAoKSW5zZXJlbmRvIHVuIHZldHRvcmUgZGkgdmFsb3JpIGJvb2xlYW5pIHBvc3NpYW1vICoqZmlsdHJhcmUqKiBzb2xvIGxlIGNvbXBvbmVudGkgY29ycmlzcG9uZGVudGkgYWkgdmFsb3JpIGBUUlVFYC4KCmBgYHtyfQoKCm1hc2NoZXJhIDwtIGFzLmxvZ2ljYWwodmV0dG9yZSAlJSAyKQoKbWFzY2hlcmEKCnZldHRvcmVbbWFzY2hlcmFdCgpgYGAKClZpY2V2ZXJzYSwgZGF0YSB1bmEgY29uZGl6aW9uZSBjaGUgcHXDsiBlc3NlcmUgc29kZGlzZmF0dGEgbyBtZW5vLCBwb3NzaWFtbyBlc3RyYXJyZSBnbGkgaW5kaWNpIHBlciBjdWkgdmFsZSBjb24gaWwgY29tYW5kbyBgd2hpY2hgLgoKYGBge3J9CndoaWNoKHZldHRvcmUgPDkgfCB2ZXR0b3JlID4gMTApCmBgYAoKSW4gcXVlc3RvIGNhc28gbW9zdHJhIGdsaSBpbmRpY2kgZGVsIHZldHRvcmUgY2hlIGNvcnJpc3BvbmRvbm8gYSB2YWxvcmkgbWlub3JpIGRpICQ5JCBvIG1hZ2dpb3JpIGRpICQxMCQuIFB1w7IgZXNzZXJlIHV0aWxlIGlsIGNvbWFuZG8gYHdoaWNoLm1heCgpYCBjaGUgdHJvdmEgbGEgcG9zaXppb25lIGRlbCB2YWxvcmUgbWFzc2ltbyBlIHNpbWltZW50ZSBgd2hpY2gubWluKClgIHBlciBpbCBtaW5pbW8uCgpgYGB7cn0KCnZldHRvcmUyID1jKDE6MTAsIDE1OjApCgojIGlsIGNvbWFuZG8gbWF4KCkgdHJvdmEgaWwgdmFsb3JlIG1hc3NpbW8KCm1heCh2ZXR0b3JlMikKCiMgaWwgY29tYW5kbyB3aGljaC5tYXgoKSB0cm92YSBsJ2luZGljZSBjb3JyaXNwb25kZW50ZSAoYXJnbWF4LCBvIHB1bnRvIGRpIG1hc3NpbW8pCndoaWNoLm1heCh2ZXR0b3JlMikKCmBgYAoKSSBjb21hbmRpIFxgYGhlYWQoKWAgZSBgdGFpbCgpYCBwZXJtZXR0b25vIGRpIG90dGVuZXJlIGxlIHByaW1lIG8gbGUgdWx0aW1lIGNvbXBvbmVudGkgZGkgdW4gdmV0dG9yZS4KCmBgYHtyfQpoZWFkKDE6MTAwMCkKdGFpbCgxOjEwMDAsIG49MTApCmBgYAoKIyMgSW5kaWNhdG9yaSBzdGF0aXN0aWNpCgpUYW50ZSBmdW56aW9uaSBkaSBzdGF0aXN0aWNhLCBpbiBwYXJ0aWNvbGFyZSBkZXNjcml0dGl2YSwgc29ubyBnacOgIGltcGxlbWVudGF0ZSBkaSBiYXNlLgoKYGBge3J9Cm1lYW4odmV0dG9yZTIpCnZhcih2ZXR0b3JlMikKc2QodmV0dG9yZTIpCm1lZGlhbih2ZXR0b3JlMikKc3VtbWFyeSh2ZXR0b3JlMikKcXVhbnRpbGUodmV0dG9yZTIsIDEvNCkKcXVhbnRpbGUodmV0dG9yZTIsIDMvNCkKcXVhbnRpbGUodmV0dG9yZTIsIC45NSkKYGBgCgpQZXIgdHJvdmFyZSBsYSBtb2RhIGFkIGVzZW1waW8gZGkgdW4gdmV0dG9yZSBkaSBmYXR0b3JpICh2YXJpYWJpbGUgc3RhdGlzdGljYSBkaXNjcmV0YSkgYmFzdGEgdXNhcmUgaWwgY29tYW5kbyBgd2hpY2gubWF4KClgIGluIGNvbWJpbmF6aW9uZSBjb24gYHRhYmxlKClgLgoKYGBge3J9Cgpjb2xvcmkgPC0gZmFjdG9yKGMoImJsYWNrIiwgInJlZCIsICJibHVlIiwgImJsdWUiLCAiZ3JlZW4iLCBOQSwgImJsdWUiLCAicmVkIiwgInllbGxvdyIpKQp0YWJsZShjb2xvcmkpCgptb2RhIDwtIHdoaWNoLm1heCh0YWJsZShjb2xvcmkpKQptb2RhCmBgYAoKTm90YXRlIGNoZSBpbCAkMiQgbm9uIMOoIGxhIGZyZXF1ZW56YSAoY2hlIHNhcmViYmUgJDMkKSBiZW5zw6wgbGEgcG9zaXppb25lIGRlbGxhIG1vZGEgbmVsIHZldHRvcmUgZGVpIGxpdmVsbGkuCgojIyBNYXRyaWNpCgpQb3NzaWFtbyBjcmVhcmUgdW5hIG1hdHJpY2UgY29uIGVudHJhdGUgdHV0dGUgZGVsbG8gc3Rlc3NvIHRpcG8gKHNwZXNzbyAqbnVtZXJpYyopIGNhbWJpYW5kbyBsYSBmb3JtYSBkaSB1biB2ZXR0b3JlLCBjb24gaWwgY29tYW5kbyBgbWF0cml4KClgLgoKYGBge3J9Cm1hdCA8LSBtYXRyaXgoYygxLCAyLCAzLCA0LCA1LCA2KSwgbnJvdyA9IDMsIG5jb2wgPSAyLCkKbWF0CmBgYAoKRGl2ZXJzYW1lbnRlIGRhIHVuIHZldHRvcmUsIGxhIGNsYXNzZSBkaSB1bmEgbWF0cmljZSDDqCBzZW1wcmUgYG1hdHJpeGAgKGFuY2hlIHNlIG5vbiBjb250aWVuZSBudW1lcmkpLgoKYGBge3J9CmNsYXNzKG1hdCkKYGBgCgpQZXIgYWNjZWRlcmUgYWxsYSBjb21wb25lbnRlIGRpIHJpZ2EgJGkkIGUgY29sb25uYSAkaiQgKHJpY29yZGFyZSBjaGUgc2kgY29udGEgZGEgJDEkKSBzaSBzY3JpdmUgYG1hdFtpLGpdYC4gUGVyIGNoaWVkZXJlIGwnaW50ZXJvIHZldHRvcmUgZGVsbGEgcmlnYSAkaSQgaW52ZWNlIGBtYXRbaSxdYCwgbWVudHJlIHBlciBsYSBjb2xvbm5hICRqJCBzaSBzY3JpdmUgYG1hdFssal1gLgoKYGBge3J9CiMgcHJpbWEgcmlnYQptYXRbMSwgXSAgICAgICAKIyB0dXR0ZSBsZSBjb2xvbm5lIHRyYW5uZSBsYSBwcmltYQogbWF0WywgLTFdICAKIyAgcHJpbWEgZSB0ZXJ6YSByaWdhLCBzZWNvbmRhIGNvbG9ubmEKbWF0W2MoMSwzKSwgMl0gICAgICAKYGBgCgpPcGVyYXppb25pIGRpIGJhc2Ugc3UgbWF0cmljaSBzb25vIGdpw6AgZGlzcG9uaWJpbGkgc2VuemEgcGFjY2hldHRpIGFnZ2l1bnRpdmkuCgpgYGB7cn0KIAojIG1hdHJpY2UgdHJhc3Bvc3RhCnQobWF0KQoKIyBwcm9kb3R0byBkaSBtYXRyaWNpIChyaWdoZSBwZXIgY29sb25uZSkKCm1hdCAlKiUgdChtYXQpCgojIGF0dGVuemlvbmUgYSBub24gY29uZm9uZGVyZSB1c2FuZG8gc29sbyAqIChwcm9kb3R0byBjb21wb25lbnRlIHBlciBjb21wb25lbnRlKQoKbWF0ICogbWF0CgpgYGAKCkkgY29tYW5kaSBgY2JpbmQoKWAgZSBgcmJpbmQoKWAgdW5pc2Nvbm8gdmV0dG9yaSAobyBtYXRyaWNpKSB0cmEgbG9ybywgcGVyIGNvbG9ubmUgbyBwZXIgcmlnaGUgcmlzcGV0dGl2YW1lbnRlLiBBdHRlbnppb25lISBsZSBtYXRyaWNpIGRldm9ubyBjb211bnF1ZSBhdmVyZSBlbnRyYXRlIGRlbGxlIHN0ZXNzZSBjbGFzc2kgKHNlIG5vbiBsbyBzb25vIHZlbmdvbm8gY29udmVydGl0ZSkuCgpgYGB7cn0KCm1hdEVzdGVzYSA8LSBjYmluZChtYXQsIGMoMSwzLDEwKSkKCm1hdEVzdGVzYQoKbWF0RXN0ZXNhWmVyaSA8LSByYmluZChjKDAsMCwwKSwgbWF0RXN0ZXNhKQoKbWF0RXN0ZXNhWmVyaQoKYGBgCgojIyBEYXRhIGZyYW1lCgpRdWFuZG8gdW5hIHRhYmVsbGEgaGEgY29sb25uZSBkaSBjbGFzc2kgZGl2ZXJzZSwgc2kgdXNhIHVuICpkYXRhIGZyYW1lKi4gUXVlc3RhIHN0cnV0dHVyYSDDqCBlc3RyZW1hbWVudGUgdXRpbGUgcGVyIHJhcHByZXNlbnRhcmUgZGF0aSAodW5hIG9zc2VydmF6aW9uZSBwZXIgY2lhc2N1bmEgcmlnYSkgZGkgY3VpIHNpIG9zc2VydmFubyBjYXJhdHRlcmlzdGljaGUgbXVsdGlwbGUgKHVuYSBjYXJhdHRlcmlzdGljYSBwZXIgY2lhc2N1bmEgY29sb25uYSkuIExhIGZ1bnppb25lIHBlciBkZWZpbmlyZSB1biBkYXRhIGZyYW1lIHBhcnRlbmRvIGRhIHZldHRvcmkgKHR1dHRpIGNvbiBsYSBzdGVzc2EgbHVuZ2hlenphKSDDqCBgZGF0YS5mcmFtZWAuCgpgYGB7cn0KCgphbHRlenphIDwtIGMoMTQ4LCAxNzAsIE5BLCAxNzksIDE5MCwgMTY4LCAxODEsIDE1OCwgMTY2KQoKIyBjcmVpYW1vIHVuIGRhdGEgZnJhbWUgY29uIGkgZGF0aSBkaSBhbHRlenphIGUgY29sb3JlIAoKb3NzZXJ2YXppb25pIDwtIGRhdGEuZnJhbWUoYWx0ZXp6YSwgY29sb3JpKQoKY2xhc3Mob3NzZXJ2YXppb25pKQpgYGAKCkNvbiBpbCBjb21hbmRvIGBoZWFkKClgIHBvc3NpYW1vIHZpc3VhbGl6emFyZSBsZSBwcmltZSByaWdoZS4gQWx0cmkgY29tYW5kaSBwZXJtZXR0b25vIGRpIG90dGVuZXJuZSBsZSBkaW1lbnNpb25pCgpgYGB7cn0KCmhlYWQob3NzZXJ2YXppb25pKQpucm93KG9zc2VydmF6aW9uaSkKbmNvbChvc3NlcnZhemlvbmkpICAKZGltKG9zc2VydmF6aW9uaSkgICAKCmBgYAoKSSBub21pIGRlbGxlIGNvbG9ubmUgcG9zc29ubyBlc3NlcmUgdmlzdWFsaXp6YXRpIGUgcHVyZSBjYW1iaWF0aSBjb24gbGEgZnVuemlvbmUgYGNvbG5hbWVzKClgCgpgYGB7cn0KY29sbmFtZXMob3NzZXJ2YXppb25pKQpjb2xuYW1lcyhvc3NlcnZhemlvbmkpIDwtIGMoImhlaWdodCIsICJjb2xvcnMiKQpoZWFkKG9zc2VydmF6aW9uaSkKYGBgCgpTaSBwdcOyIGFjY2VkZXJlIGFsbGUgc2luZ29sZSBjb2xvbm5lIGNvbiBsYSBzaW50YXNzaSBgbm9tZURhdGFGcmFtZSRub21lQ29sb25uYWAuIEFkIGVzZW1waW86CgpgYGB7cn0Kb3NzZXJ2YXppb25pJGhlaWdodAoKIyBsYSBjb2xvbm5hIHNlbGV6aW9uYXRhIMOoIG9yYSB1biB2ZXR0b3JlCgpjbGFzcyhvc3NlcnZhemlvbmkkaGVpZ2h0KQoKCmBgYAoKU2Ugc2kgdnVvbGUgc2VsZXppb25hcmUgYWxjdW5lIGNvbG9ubmUgbWFudGVuZW5kbyBsYSBzdHJ1dHR1cmEgZGkgZGF0YSBmcmFtZSwgc2kgcHXDsiBpbmRpY2FybmUgYWQgZXNlbXBpbyBpbCBudW1lcm8uCgpgYGB7cn0KaGVhZChvc3NlcnZhemlvbmlbMV0pCgpjbGFzcyhvc3NlcnZhemlvbmlbMV0pCgpgYGAKCkNpIHNvbm8gZGl2ZXJzZSAqKmVzdGVuc2lvbmkqKiBkZWxsYSBzdHJ1dHR1cmEgZGVpIGRhdGEgZnJhbWVzLiBVbmEgc29ubyBsZSAqZGF0YSB0YWJsZXMqLCBwZXIgZ2VzdGlyZSBncmFuZGkgcXVhbnRpdMOgIGRpIGRhdGkgKGluc3RhbGxhcmUgcGFjY2hldHRvIGBkYXRhLnRhYmxlYCkuIFVuJ2FsdHJhIHNvbm8gbGUgKnRpYmJsZSosIHBlciBmdW56aW9uaSBwacO5IGludHVpdGl2ZSBlIGNvZGljZSBwacO5IHB1bGl0byBlIGludGVycHJldGFiaWxlIChpbnN0YWxsYXJlIGxhIHN1aXRlIGB0aWR5dmVyc2VgIDxodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLz4pLgoKYGBge3IgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQoKaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKCmBgYAoKIyMgTGlzdGUKCkluZmluZSwgUiBoYSBsaXN0ZSBkaSB2ZXR0b3JpIChhbmNoZSBkaSBsdW5naGV6emUgZSBjbGFzc2kgZGl2ZXJzZSkuCgpgYGB7cn0KbGlzdGEgPC0gbGlzdCh0ZW1wbyA9IDE6NDApCmxpc3RhJHByZXp6byA9IHJub3JtKDEwLCAxLDMpCgpjbGFzcyhsaXN0YSkKCmxpc3RhJHRlbXBvWzE6NF0KbGlzdGEkcHJlenpvWzE6NF0KYGBgCgpMZSBsaXN0ZSBub24gc29ubyBlZmZpY2llbnRpIHBlciBsYXZvcmFyZSBjb24gZ3JhbmRpIHF1YW50aXTDoCBkaSBvc3NlcnZhemlvbmkgKHVzYXJlIGRhdGEgZnJhbWVzIG8gZGF0YSB0YWJsZXMpLCBtYSDDqCBiZW5lIHNhcGVyZSBjaGUgYWxjdW5lIGZ1bnppb25pIHJlc3RpdHVpc2Nvbm8gdW5hIGxpc3RhLgoKIyBDYXJpY2FyZSBlIHNhbHZhcmUgZGF0aSAoY29tYW5kaSBkaSBiYXNlKQoKTGUgZnVuemlvbmkgZGkgaW5wdXQvb3V0cHV0IHBvc3Nvbm8gZXNzZXJlIGNvbXBsaWNhdGUgZGFsIGZhdHRvIGNoZSBjaSBzb25vIG1vbHRpIGZvcm1hdGkgcGVyIHNhbHZhcmUgaSBkYXRpLiBTZSBzaSB1c2FubyBmb3JtYXRpIHN0YW5kYXJkIGNvbWUgKi5jc3YqIChjb21tYSBzZXBhcmF0ZWQgdmFsdWVzKSBvICoudHN2KiAodGFiIHNlcGFyYXRlZCB2YWx1ZXMpIHNpIHB1w7IgdXNhcmUgbGEgZnVuemlvbmUgZGkgYmFzZSBgcmVhZC5jc3YoKWAgY29uIGxhIHNpbnRhc3NpIGBub21lRGF0YUZyYW1lIDwtIHJlYWQuY3N2KCJub21lRmlsZS5jc3YiKWAuIEwnaW1wb3J0YW50ZSDDqCBjaGUgaWwgZmlsZSBzaSB0cm92aSBuZWxsYSBjYXJ0ZWxsYSBkaSBsYXZvcm8gY29ycmVudGUgKHdvcmtpbmcgZGlyZWN0b3J5KS4KCmBgYHtyfQojIHBlciBzYXBlcmUgbGEgY2FydGVsbGEgZGkgbGF2b3JvIChkaSBkZWZhdWx0IHF1ZWxsYSBkZWwgcHJvZ2V0dG8gUiBzdSBjdWkgc3RhdGUgbGF2b3JhbmRvKQoKZ2V0d2QoKQoKIyBwZXIgbW9kaWZpY2FyZSBsYSBjYXJ0ZWxsYSBkaSBsYXZvcm8gdXNhcmUgc2V0d2QoKQoKP3NldHdkCgojIHNlIGlsIGZpbGUgbm9uIMOoIHNlcGFyYXRvIGRhIHZpcmdvbGEgbWEgZGEgYWx0cmkgc2ltYm9saSwgY29uc3VsdGFyZSBsJ2hlbHAKCj9yZWFkLmNzdgpgYGAKClBlciBzYWx2YXJlIHVuIGRhdGEgZnJhbWUgY29tZSBmaWxlICouY3N2KiBiYXN0YSB1c2FyZSBpbCBjb21hbmRvIGB3cml0ZS5jc3Yobm9tZURhdGFGcmFtZSwgIm5vbWVGaWxlLmNzdiIpYC4gVmVkcmVtbyBlc2VtcGkgcGnDuSBhdmFudGkgKGFuY2hlIHN1IGNvbWUgY2FyaWNhcmUgYWx0cmkgZm9ybWF0aSBhZCBlc2VtcGlvIEV4Y2VsICoqLnhsc3gqKikKCiMgUGxvdAoKSWwgY29tYW5kbyBkaSBiYXNlIHBlciBsZSBmdW56aW9uaSBncmFmaWNoZSDDqCBgcGxvdCgpYC4gUXVlc3RvIHByb2R1Y2UgdW4gZGlhZ3JhbW1hIGEgKipudXZvbGEgZGkgcHVudGkqKiAoc2NhdHRlcnBsb3QpLgoKYGBge3J9CgpoZWFkKGlyaXMpCgpwbG90KGlyaXMkU2VwYWwuTGVuZ3RoLCBpcmlzJFNlcGFsLldpZHRoKQoKYGBgCgpJbCBjb21hbmRvIGBoaXN0KClgIHByb2R1Y2UgdW4gaXN0b2dyYW1tYS4KCmBgYHtyfQpoaXN0KGlyaXMkU2VwYWwuTGVuZ3RoKQpgYGAKCklsIGNvbWFuZG8gYGJveHBsb3QoKWAgcHJvZHVjZSB1biBkaWFncmFtbWEgYSBzY2F0b2xhLgoKYGBge3J9CmJveHBsb3QoaXJpcyRTZXBhbC5MZW5ndGgpCmBgYAoKUXVlc3RpIHBsb3QgdHV0dGF2aWEgc29ubyBwaXV0dG9zdG8gKipkaSBiYXNlKiosIGlsIHBhY2NoZXR0byBgZ2dwbG90MmAgKHN1aXRlIGB0aWR5dmVyc2VgKSBwZXJtZXR0ZSBmdW56aW9uYWxpdMOgIHBpw7kgYXZhbnphdGUgZSBjb21hbmRpIGZhY2lsbWVudGUgaW50ZXByZXRhYmlsaS4KCkNhcmljaGlhbW8gaWwgcGFjY2hldHRvIChzZSBub24gw6ggaW5zdGFsbGF0byB1c2FyZSBgaW5zdGFsbC5wYWNrYWdlcygpYCkKCmBgYHtyfQpsaWJyYXJ5KCJnZ3Bsb3QyIikKYGBgCgpWaXN1YWxpenppYW1vIGkgcGxvdCBkaSBwcmltYSBhZ2dpdW5nZW5kbyBjb2xvcmkgaW4gYmFzZSBhbGxlIHNwZWNpZSAodWx0aW1hIGNvbG9ubmEpLiBTY2F0dGVycGxvdDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1pcmlzLCBhZXMoeD1TZXBhbC5MZW5ndGgsIHk9U2VwYWwuV2lkdGgsIGNvbG91cj1TcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgeGxhYigiTHVuZ2hlenphIHNlcGFsaSIpICsKICB5bGFiKCJMdW5naGV6emEgcGV0YWxpIikgKwogIGdndGl0bGUoIkRhdGFzZXQgSXJpcyIpIAoKYGBgCgpJc3RvZ3JhbW1hOgoKYGBge3J9CmdncGxvdChkYXRhPWlyaXMsIGFlcyh4PVNlcGFsLkxlbmd0aCwgZmlsbD1TcGVjaWVzKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MTApKwogIHhsYWIoIkx1bmdoZXp6YSBzZXBhbGkiKSArCiAgeWxhYigiZnJlcXVlbnphIGFzc29sdXRhIikgKwogIGdndGl0bGUoIkRhdGFzZXQgSXJpcyIpIAoKYGBgCgpQb3NzaWFtbyByYXBwcmVzZW50YXJlIGFuY2hlIGNvbiB1bmEgKipkZW5zaXTDoCoqIGNvbnRpbnVhOgoKYGBge3J9CmdncGxvdChkYXRhPWlyaXMsIGFlcyh4PVNlcGFsLkxlbmd0aCwgZmlsbD1TcGVjaWVzKSkgKwogIGdlb21fZGVuc2l0eShwb3NpdGlvbj0ic3RhY2siKSsKICB4bGFiKCJMdW5naGV6emEgc2VwYWxpIikgKwogIHlsYWIoImZyZXF1ZW56YSBhc3NvbHV0YSIpICsKICBnZ3RpdGxlKCJEYXRhc2V0IElyaXMiKSAKCmBgYAoKUG9zc2lhbW8gYW5jaGUgcmFwcHJlc2VudGFyZSB0cmUgaXN0b2dyYW1taSAodW5vIHBlciBzcGVjaWUpIHVubyBhY2NhbnRvIGFsbCdhbHRyby4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1pcmlzLCBhZXMoeD1TZXBhbC5MZW5ndGgsIGZpbGw9U3BlY2llcykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zPTEwLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpKSsKICB4bGFiKCJMdW5naGV6emEgc2VwYWxpIikgKwogIHlsYWIoImZyZXF1ZW56YSBhc3NvbHV0YSIpICsKICBnZ3RpdGxlKCJEYXRhc2V0IElyaXMiKSAKCmBgYAoKRSBwdXJlIGxlIHRyZSBkZW5zaXTDoDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1pcmlzLCBhZXMoeD1TZXBhbC5MZW5ndGgsIGZpbGw9U3BlY2llcykpICsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC40KSsKICB4bGFiKCJMdW5naGV6emEgc2VwYWxpIikgKwogIHlsYWIoImZyZXF1ZW56YSBhc3NvbHV0YSIpICsKICBnZ3RpdGxlKCJEYXRhc2V0IElyaXMiKSAKCmBgYAoKQm94cGxvdDoKCmBgYHtyfQoKZ2dwbG90KGlyaXMsIGFlcyh4PVNlcGFsLkxlbmd0aCwgZmlsbD1TcGVjaWVzKSkrCiAgZ2VvbV9ib3hwbG90KCkgKwogIHhsYWIoIkx1bmdoZXp6YSBzZXBhbGkiKSArCiAgeWxhYigiIikgKwogIGdndGl0bGUoIkRhdGFzZXQgSXJpcyIpIAoKYGBgCg==