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.

Caricare e salvare dati (comandi di base)

Le funzioni di input/output possono essere complicate dal fatto che ci sono molti formati per salvare i dati. Se si usano formati standard come .csv (comma separated values) o .tsv (tab separated values) si può usare la funzione di base read.csv() con la sintassi nomeDataFrame <- read.csv("nomeFile.csv"). L’importante è che il file si trovi nella cartella di lavoro corrente (working directory).

# per sapere la cartella di lavoro (di default quella del progetto R su cui state lavorando)

getwd()

# per modificare la cartella di lavoro usare setwd()

?setwd

# se il file non è separato da virgola ma da altri simboli, consultare l'help

?read.csv

Per salvare un data frame come file .csv basta usare il comando write.csv(nomeDataFrame, "nomeFile.csv"). Vedremo esempi più avanti (anche su come caricare altri formati ad esempio Excel .xlsx)

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==