1 I primi passi con R

1.1 Operazioni matematiche

La scrittura di operazione complesse in R avviene utilizzando le parentesi per dare priorità ad alcune parti. Per il resto R segue quella che è la normale priorità delle operazioni matematiche. Prova a scrivere in R queste operazioni:

  1. \(\frac{(45+21)^3+\frac{3}{4}}{\sqrt{32-\frac{12}{17}}}\)

  2. \(\frac{\sqrt{7-\pi}}{3\ (45-34)}\)

  3. \(\sqrt[3]{12-e^2}+\ln(10\pi)\)

  4. \(\frac{\sin(\frac{3}{4}\pi)^2+\cos(\frac{3}{2}\pi)}{\log_7{e^{\frac{3}{2}}}}\)

  5. \(\frac{\sum_{n=1}^{10} n}{10}\)

Note per la risoluzione degli esercizi:

  • In R la radice quadrata si ottine con la funzione sqrt() mentre per radici di indici diversi si utilizza la notazione esponenziale (\(\sqrt[3]{x}\) è dato da x^(1/3)).
  • Il valore di \(\pi\) si ottiene con pi.
  • Il valore di \(e\) si ottiene con exp(1).
  • In R per i logaritmi si usa la funzione log(x, base=a), di base viene considerato il logaritmo naturale.

1.2 Operazioni logiche

  1. Definisci una proposizione che ti permetta di valutare se un numero è pari. Definisci un’altra proposizione per i numeri dispari (tip: cosa ti ricorda %%?).

  2. Definisci una proposizione per valutare la seguente condizione (ricordati di testare tutti i possibili scenari) “x è un numero compreso tra -4 e -2 oppure è un numero compreso tra 2 e 4”.

  3. Esegui le seguenti operazioni 4 ^ 3 %in% c(2,3,4) e 4 * 3 %in% c(2,3,4). Cosa osservi nell’ordine di esecuzione degli operatori?

  4. Esegui le seguenti operazioni logiche tra stringhe e prova prevedere il risultato prima di eseguire il codice:

"ciao" == "ciao"
"R" == "r"
"SPSS" != "SpSS"
"1" == 1
"1" > "ciao"
"a" > "b"
"ciao" > "zebra"

2 Strutture dati

2.1 Vettori

2.1.1 Creare vettori

  1. Crea il vettore x contenente i numeri 4, 6, 12, 34,

  2. Crea il vettore y contenente tutti i numeri pari compresi tra 1 e 25 (?seq())

  3. Crea il vettore z contenente tutti i primi 10 multipli di 7 partendo da 14 (?seq() in particolare l’argomento length.out =)

  4. Crea il vettore s in cui le lettere "A","B" e "C" vengono ripetute nel medesimo ordine 4 volte (?rep())

  5. Crea il vettore t in cui le letter "A","B" e "C" vengono ripetute ognuna 4 volte (?rep())

  6. Genera il seguente output in modo pigro, ovvero scrivendo meno codice possibile ;)

## [1] "foo" "foo" "bar" "bar" "foo" "foo" "bar" "bar"

2.1.2 Indicizzare vettori

  1. Del vettore x seleziona il 2°, 3° e 5° elemento

  2. Del vettore x seleziona i valori 34 e 4

  3. Dato il vettore my_vector = c(2,4,6,8) commenta il risultato del comando my_vector[my_vector]

my_vector = c(2,4,6,8)
my_vector[my_vector]
  1. Del vettore y seleziona tutti i valori minori di 13 o maggiori di 19

  2. Del vettore z seleziona tutti i valori compresi tra 24 e 50

  3. Del vettore s seleziona tutti gli elementi uguali ad "A"

  4. Del vettore t seleziona tutti gli elementi diversi da "B"

  5. Crea un nuovo vettore u identico a s ma dove le "A" sono sostituite con la lettera "U"

  6. Elimina dal vettore z i valori 28 e 42

  7. Spesso è necessario creare delle stringhe random per codificare un partecipante in modo univoco ma senza usare informazioni sensibili. Genera 1 codice univoco formati da id_string_num dove:

    • id è un numero casuale tra 1 e 10
    • string è una stringa formata da 5 lettere casuali
    • nums è un numero casuale tra 100 e 999 Ad esempio 1_adrtv_100 è un id valido.

Tips: - vedi la funzione sample o runif e round per generare i numeri - vedi l’oggetto letters già disponibile in R - vedi la funzione paste0() o sprintf()

2.2 Fattori

  1. Crea la variabile categoriale genere così definita:
## [1] M F M F M F F F M
## Levels: F M
  1. Rinomina i livelli della variabile genere rispettivamente in "donne" e "uomini".

  2. Crea la variabile categoriale intervento così definita:

## [1] CBT         Psicanalisi CBT         Psicanalisi CBT         Psicanalisi
## [7] Controllo   Controllo   CBT        
## Levels: CBT Controllo Psicanalisi
  1. Correggi nella variabile intervento la 7° e 8° osservazione con la voce Farmaci. Notate qualcosa di strano?

  2. Aggiungi alla variabile intervento le seguenti nuove osservazioni:

c("Farmaci","Controllo","Farmaci")
## [1] "Farmaci"   "Controllo" "Farmaci"

2.3 Matrici

  1. Crea la matrice A così definita:

\[ \begin{matrix} 2 & 34 & 12 & 7\\ 46 & 93 & 27 & 99\\ 23 & 38 & 7 & 04 \end{matrix} \]

  1. Crea la matrice B contenente tutti i primi 12 numeri dispari disposti su 4 righe e 3 colonne.
  2. Crea la matrice C contenente i primi 12 multipli di 9 disposti su 3 righe e 4 colonne.
  3. Crea la matrice D formata da 3 colonne in cui le lettere "A","B" e "C" vengano ripetute 4 volte ciascuna rispettivamente nella prima, seconda e terza colonna.
  4. Crea la matrice E formata da 3 righe in cui le lettere "A","B" e "C" vengano ripetute 4 volte ciascuna rispettivamente nella prima, seconda e terza riga.
  5. Utilizzando gli indici di riga e di colonna seleziona il numero 27 della matrice A
  6. Selziona gli elementi compresi tra la seconda e quarta riga, seconda e terza colonna della matrice B
  7. Seleziona solo gli elementi pari della matrice A (Nota: utilizza l’operazione resto %%)
  8. Elimina dalla matrice C la terza riga e la terza colonna
  9. Seleziona tutti gli elementi della seconda e terza riga della matrice B
  10. Seleziona tutti gli elementi diversi da “B” appartenenti alla matrice D
  11. Crea la matrice G unendo alla matrice A le prime due colonne della matrice C
  12. Crea la matrice H unendo alla matrice C le prime due righe della matrice trasposta di B
  13. Ridefinisci la matrice A eliminando la seconda colonna. Ridefinisci la matrice B eliminando la prima riga. Verifica che le matrici così ottenute abbiano la stessa dimensione.
  14. Commenta i differenti risultati che otteniamo nelle operazioni A*B, B*A, A%*%B e B%*%A.
  15. Assegna i seguenti nomi alle colonne e alle righe della matrice C: "col_1", "col_2", "col_3", "col_4", "row_1", "row_2", "row_3".

2.4 Liste

  1. Crea una named list con questi elementi:
  • un vettore numerico (x) con i numeri da 1 a 100
  • un vettore di caratteri (y) con le prime 15 lettere dell’alfabeto (vedi l’oggetto letters)
  • la matrice B creata nell’esercizio precedente
  • un fattore creato nell’esercizio precedente
  1. Aggiungi alla lista un vettore logico TRUE/FALSE testando che il vettore numerico creato in precedenza abbia numeri pari (TRUE) o dispari (FALSE)
  2. Aggiungi un altro elemento alla lista come vettore numerico creato facendo una selezione degli elementi di x
  3. Sovrascrivi il vettore y con lo stesso vettore di caratteri ma ripetendo ogni elemento 4 volte
  4. Seleziona i numeri da 1 a 50 del vettore x all’interno della lista
  5. Aggiungi alla lista un’ulteriore lista selezionando i primi 3 elementi della lista originale
  6. Aggiungi alla lista nested un vettore numerico selezionando solo i numeri di x divisibili per 3 (ricordi la funzione modulo %%?)

2.5 Dataframe

  1. Ricrea questo dataframe in R:
data_long<-data.frame(Id=rep(c("subj_1","subj_2","subj_3"),each=3),
                      age=rep(c(21,23,19),each=3),
                      gender=rep(c("F","M","F"),each=3),
                      item=rep(1:3,3),
                      response=c(2,1,1,0,2,1,2,0,1))
  1. Utilizzando il dataframe dataframe_example1.rds esegui le seguenti operazioni:
  • selezionare le persone con età maggiore di 30 anni
  • selezionare le persone con età maggiore di 30 anni e minore di 21
  • selezionare le persone che studiano al dams con voto di laurea maggiore di 100 e almeno 1 fratello/sorella
  • selezionare le persone che studiano psicologia e non hanno fratelli
  • selezionare le persone che hanno punteggi di depressione maggiori di 50 e punteggi di ansia maggiori di 20
  1. Utilizza la funzione put_random_na(data, n) dove data è il dataframe e n è il numero di NA da generare. Ora:
  • seleziona le persone con NA per la colonna degree e per la colonna età
  • seleziona le persone con NA per la colonna email e abitanti del veneto o campania
  • seleziona le persone che non hanno nessun NA (vedi la funzione complete.cases())
  1. Utilizzando il dataset dataframe_example2.csv
  • importa il dataframe dal formato csv
  • crea una nuova colonna id che identifichi in modo univoco ogni soggetto progressivamente
  • controlla che tutte le colonne siano del tipo appropriato (usa la famiglia is.*)
  • seleziona tutte le persone a cui non piace nessuna festa. Noti qualche problema?
  • seleziona tutte le persone a cui piace il colore blu e piace il natale
  • per risolvere i problemi con le stringhe, trova un modo per portare tutte le stringhe ad un formato comparabile
  • fai un subset del dataset tenendo solo le colonne numeriche (usa la is.* family)

3 Programmazione

3.1 Funzioni

  1. definisci una funzione che trasformi la temperatura da Celsius a Fahrenheit

\[ Fahrenheit = Celsius * 1.8 + 32 \]

  1. Definisci una funzione che permetta di fare gli auguri di buon natale e buona pasqua ad una persona. Prova a utilizzare e capire le funzioni paste() e print().
  2. Definisci una funzione n_and_media() che, dato un vettore di valori numerici, calcoli il numero di elementi e la loro media e restituisca entrambe in una frase. Ad esempio n_and_media(x) deve restituire “la media di x è … e la lunghezza di x è …”. Anche qui sono utili le funzioni paste()/sprintf e print().

3.2 Condizionali

score <- runif(100, 0, 1)
  1. Definisci una funzione per assegnare un voto in base alla percentuale di risposte corrette (score) segui le seguenti indicazioni:
  • score < .55 insufficiente
  • .55 <= score < .65 - sufficiente
  • .65 <= score < .75 - buono
  • .75 <= score < .85 - distinto
  • .85 <= score - ottimo

La funzione quindi deve ricevere in input un valore e restituire la stringa corrispondente in base al valore stesso.

  1. Usa una serie di ifelse() nested per creare una variabile score_chr con le etichette corrispondenti all’esercizio precedente.
  2. Definisci una funzione che determini se un numero è pari o dispari.
  3. Definisci una funzione che determini se un numero è un multiplo di 3, 4, o 5.

3.3 Loop

3.3.1 Scrivi un loop che:

Scrivi un loop che scorre le colonne del dataset iris (lo trovate direttamente in R) e stampa il nome della colonna assieme al numero di caratteri che compone la stringa. Ad esempio Sepal.Length (12). Potete usare le funzioni print(), paste() and nchar().

3.3.2 Scrivi un loop che:

Scrivi un loop che scorre le colonne del dataset iris (lo trovate direttamente in R) e resituisce la media se la colonna è numerica (ricordate le is.* family) oppure la tabella di frequenza (comando table()) se la colonna è stringa/fattore

3.3.3 *apply()

Usando la funzione più adatta dell*apply() family, calcola la deviazione standard di ogni colonna del dataset mtcars (lo trovate direttamente in R)

3.3.4 Popola la matrice con un for

Popoliamo una matrice usando il ciclo for. La matrice ha un numero totale di elementi. Usando il vettore x che contiene il numero di elementi totali della matrice come possiamo popolare la matrice partendo da una vuota matrix(NA, 10, 10). La matrice è quadrata di dimensione 10x10:

# matrice 10 x 10 quindi 100 elementi totali
my_mat <- matrix(NA, 10, 10) # matrice vuota 10x10

Suggerimento: puoi fare un doppio ciclo for e generare un numero casuale da inserire con rnorm(1)

3.4 Ri-creiamo le funzioni in R

Un ottimo esercizio per affinare le nostre competenze di codice è quello di ricreare le funzioni di R che diamo per scontate diciamo. La funzione mean() ad esempio si può facilmente create con un ciclo for e qualche altra aggiunta.

3.4.1 sum()

Ricrea la funzione sum() e testala sul seguente vettore:

x <- rnorm(100)
sum(x)
## [1] -11.31083

3.4.2 mean()

Ricrea la funzione mean() testala sul seguente vettore confrontando il risultato con la funzione interna (hint: puoi usare la funzione sum() creata prima):

x <- rnorm(100)
mean(x)
## [1] 0.08861665

3.4.3 median()

La mediana è definita come il valore che divide a metà una distribuzione di valori. Ricreala in R facendo attenzione che la formula cambia a seconda che il vettore sia di lunghezza pari o dispari (hint: puoi usare un if per controllare queste condizioni)

median(x)
## [1] -0.1333679

3.4.4 Moda

In R non è prevista una funzione per calcolare la moda. In statistica la moda è definita come il valore/i associato a maggiore frequenza. Ad esempio se abbiamo un vettore \(x\) come c(1,1,1,2,3,5) la moda sarà 1.

  • come si potrebbe calcolare la moda in R?
  • ci sono dei pacchetti che l’hanno implementata?
  • possono esserci situazioni dove abbiamo più mode. La funzione che hai trovato/scritto gestisce questo scenario? eventualmente come puoi modificarla?

3.4.5 sd()

La deviazione standard è facilmente implementata in R con la funzione sd(). Prova a ricrearla con tutti gli strumenti che abbiamo imparato fino ad ora. La formula matematica è la seguente:

\[ SD = \sqrt{\frac{\sum_{i = 1}^{N} (x_i - \mu_x)^2}{N}} \] Attenzione che R utilizza un denominatore diverso per la funzione sd() rispetto alla formula proposta. Cerca di capire cosa utilizza R aprendo la documentazione di sd() e implementa la versione di R e quella della formula. Crea poi una terza versione della funzione my_sd(x, versione) dove viene calcolata una o l’altra versione in base a cosa viene messo come argomento.

3.4.6 Trova il numero all’interno del vettore

Scrivi una funzione che dato in input un vettore x restituisca il numero di volte che compare il numero \(3\):

  • per testare la funzione genera un vettore di numeri casuali con round(runif(20, 1, 10))

3.4.7 Numeri pari

Scrivi una funzione che prenda in input un vettore x e restituisca il numero di numeri pari all’interno del vettore. Scrivi una funzione che utilizzi un for loop mentre un’altra che usi un’operazione vettorizzata.

  • usa l’operatore divisione intera %%

3.4.8 complete.cases()

Cerca di capire cosa fa la funzione complete.cases() e di creare una funzione personalizzata my_complete_cases() con cui ottenere lo stesso output.

complete.cases(iris[1:10, ])
##  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

Suggerimenti:

  • usa la funzione any()
  • puoi usare un ciclo for oppure una funzione dell’*apply family

3.4.9 Troviamo gli outlier

Uno step importante quando si analizzano i dati è quello di cercare valori anomali (ad esempio che superano una certa soglia oppure che sono troppo oltre gli indici di tendenza centrale).

  • Scrivi una funzione is_outlier(x) che dato in input un vettore x, restituisca un vettore logico dove TRUE corrisponde ad un valore che è maggiore della media più una volta la deviazione standard di x.
  • Scrivi una funzione has_outlier che applichi is_outlier() ad ogni colonna di un dataframe e restituisca i nomi delle colonne dove è presente almeno 1 outlier.
    • puoi usare la funzione any()
    • puoi usare una funzione dentro un’altra funzione
    • usa il dataframe mtcars
  • modifica la funzione has_outliers() per funzionare solo sulle colonne del dataframe che sono numeriche (no fattori o caratteri)

Usa il dataframe my_mtcars dove sono state aggiunte delle variabili non numeriche:

my_mtcars <- mtcars
my_mtcars$factor1 <- factor(rep(c("a", "b", "c"), c(10, 10, 12)))
my_mtcars$factor2 <- rep(c("d", "e", "f"), c(10, 10, 12))

3.4.10 Bootstrapping

Il bootstrapping è una tecnica statistica molto utilizzata. La logica della programmazione però è molto semplice ma interessante (e potrebbe esservi anche utile). Scrivete una funzione prepare_bootstrap(data, nrow, n) che prenda in input un dataframe data, il numero di righe da estrarre nrow e quanti dataframe creare n e che fornisca quindi n dataframe dove n righe sono estratte casualmente.

  • usa il dataset mtcars
  • usa la funzione sample()

3.4.11 Leave-one-out

Il leave-one-out è una procedura statistica usata in vari contesti. Ad esempio, per capire se ci sono delle osservazioni influenti in un certo modello statistico, si ripete l’analisi rimuovendo un’osservazione alla volta. Se abbiamo 100 osservazioni otteniamo 100 dataset ognuno formato da 99 osservazioni perchè una è stata rimossa.

  1. Scrivete una funzione leave1out(data) che riceve in input un dataset e restituisce una lista di dataset, ognuno dove è stata tolta un’osservazione. Usate un ciclo for.

  2. Scrivete una versione più compatta usando l’*apply family.

  3. Ampliate la funzione creata al punto 1 in modo che la funziona tolga ogni volta \(n\) osservazioni casuali, dove \(n\) è un parametro della funzione. Ad esempio, la funzione deve creare 100 dataset, dove ogni volta toglie 3 osservazioni.

Potete usare il dataset iris

3.4.12 Popolare una matrice

Scrivi una funzione populate_matrix(matrix, data) che presi in input i dei dati e una matrice vuota, popoli la matrice vuota usando un nested loop.

x <- rnorm(100)
mat <- matrix(nrow = 10, ncol = 10)

4 Esercizi avanzati dataframe

In questi esercizi vedremo come gestire un dataset più o meno complesso in modo da applicare le nozioni che abbiamo imparato. Gli obiettivi sono:

  • applicare il più possibile i concetti del corso
  • cercare soluzioni online per le cose non chiare
  • scrivere funzioni dove possibile
  • produrre uno script di pulizia dati

In questo corso non abbiamo visto aspetti di statistica o manipolazione dati in senso stretto, tuttavia i concetti che abbiamo imparato sono applicabili anche senza queste nozioni. Ad esempio non abbiamo affrontato nel dettaglio la pulizia dei dati, i pacchetti per gestire dataset complessi ma sappiamo che ad esempio un dataframe è una lista, e se vogliamo applicare una funzione ad una lista possiamo usare l’*apply family.

4.1 Questionari

Proviamo a fare lo scoring di un questionario rappresentato in R come dataframe. Questo è un caso molto comune dove dobbiamo lavorare con indici di riga/colonna.

  • importare il dataset item_data.rds
  • esplorare il dataset (struttura, numero di colonne/righe, tipo di colonne)
  • vedere i valori massimi e minimi degli item
    • se ci sono valori anomali (minori di 1 o maggiori di 5) mettiamo i rispettivi valori minimi e massimi. Ad esempio se c’è 0 mettiamo 1 mentre 10 mettiamo 5
  • vedere se ci sono NA. Se un soggetto ha più di 1 NA, eliminiamo il soggetto altrimenti mettiamo il valore 3 al posto degli NA

Prima di fare lo scoring dobbiamo invertire (ricodificare) alcuni item. In particolare dobbiamo invertire gli item 1, 10, 11, 13, 14, 15, 20. Per invertire semplicemente i punteggi 1 diventano 5, 2 –> 4 e così via.

Tips:

  • scrivere una funzione per ricodificare (vedi case_when()) e poi applicarla a tutti gli item

Questo questionario è formato da 30 item. Ci sono 5 sottoscale dove:

  • sub1 = items 20 25 17 18 22
  • sub2 = items 30 3 11 16 19
  • sub3 = items 4 14 8 21 23
  • sub4 = items 26 29 9 13 10
  • sub5 = items 5 12 6 28 24
  • sub6 = items 7 15 27 2 1

Per fare lo scoring dobbiamo:

  • mediare gli item per le sottoscale sub1, sub2 e sub3
  • sommare gli item per le sottoscale sub4, sub5 e sub6

In entrambi i casi creiamo delle nuove colonne che si chiamano come le sottoscale da attaccare al dataframe principale.

4.2 Netflix

Questo dataset è stato preso dal sito Kaggle(un ottima risorsa per trovare datasets). E’ un dataset relativamente grande con 7787 righe e 12 colonne. Il dataset contiene informazioni su serie-tv e film presenti sulla piattaforma, in particolare:

  • show_id: in indice numerico per ogni film/serie-tv
  • type: identifica se il prodotto è una serie-tv o un film
  • title: il titolo
  • country: il paese di produzione
  • duration: la durata
  • users_rating: le valutazioni degli utenti netflix
  • imdb_rating: le valutazioni del portale imdb
  • total_cost: il costo di produzione in milioni
  • release_date: la data di rilascio originale della serie o del film
  • insert_date: la data di inserimento nel catalogo netflix
head(netflix)
##   show_id    type title          director       country  duration users_rating
## 1      s1 TV Show    3%              <NA>        Brazil 4 Seasons            5
## 2      s2   Movie  7:19 Jorge Michel Grau        Mexico    93 min            7
## 3      s3   Movie 23:59      Gilbert Chan     Singapore    78 min            6
## 4      s4   Movie     9       Shane Acker United States    80 min            8
## 5      s5   Movie    21    Robert Luketic United States   123 min            1
## 6      s6 TV Show    46       Serdar Akar        Turkey  1 Season            0
##   imdb_rating total_cost duration_episode release_date insert_date
## 1           2         35               91   2019-05-07  2019-09-24
## 2           5        105               NA   2019-01-01  2019-03-31
## 3           3        127               NA   2019-10-23  2020-01-30
## 4           2         95               NA   2020-06-02  2020-06-09
## 5           5         39               NA   2019-07-26  2019-08-30
## 6           3         51               60   2019-11-09  2020-03-25

4.2.1 Passaggi da fare:

  • Importare il file netflix.txt capendo quale funzione utilizzare, come assegnare la prima riga come nomi e usando il separatore giusto
  • Scrivere una funzione che prenda in input il dataset e restituisca la tipologia di dato presente in ogni colonna
  • Ragionare sul tipo di dato in base alla descrizione ed eventualmente cambiare la tipologia dove appropriato
  • Scrivere una funzione che prenda in input il dataset e fornisca per ogni colonna il numero di valori NA
  • Modificare la funzione precedente con un opzione chiamata perc che se inserita come TRUE restituisce la percentuale di NA
  • Creare una nuova colonna con la differenza tra release_date e insert_date. Per questo può essere utile cercare online come R gestisce le date.

4.2.2 Passaggi avanzati

Solitamente le colonne di un dataset contengono informazioni rindondanti o non adeguatamente organizzate. Ad esempio la colonna duration ha una duplice informazione: la durata in minuti (per i film) e la durata in stagioni per le serie. In un’altra colonna abbiamo invece la durata degli episodi chiaramente non rilevante per i film. In questo caso potrebbe essere utile:

  • creare una colonna durata che contiene la durata in minuti per i film e la durata dell’episodio per le le serie-tv
  • creare una colonna durata_stagioni che contenga il numero di stagioni per ogni serie-tv mentre il valore 0 per i film.

4.3 Advanced Cleaning

Nonstante possano sembrare aspetti superficiali, la pulizia dei dataset è fondamentale. Nomi di colonne appropriati, rimuovere caratteri speciali, verificare la struttura e la tipologia delle colonne etc. Il dataframe inequality_sub.csv è un esempio di un ottimo dataset di partenza ma con la possibilità di migliorare i nomi delle colonne e alcuni dettagli generali.

Vedi un esempio di pre-processing di un dataset a questo link.

5 Credits

Alcuni esercizi sono stati presi o inspirati da:

LS0tCnRpdGxlOiAiRXNlcmNpemkiCm91dHB1dDogCiAgICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQpjc3M6IFsiLi4vZmlsZXMvY3NzL2NvdXJzZV9odG1sLmNzcyJdCgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3IgcGFja2FnZXMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoaGVyZSkKYGBgCgpgYGB7ciBsb2FkLWRhdGEsIGluY2x1ZGUgPSBGfQpuZXRmbGl4IDwtIHJlYWQudGFibGUoaGVyZSgiZXhlcmNpc2VzIiwgImRhdGEiLCAibmV0ZmxpeC50eHQiKSwgaGVhZGVyID0gVCwgc2VwID0gIjsiKQpgYGAKCiMgSSBwcmltaSBwYXNzaSBjb24gUgoKIyMgT3BlcmF6aW9uaSBtYXRlbWF0aWNoZQoKTGEgc2NyaXR0dXJhIGRpIG9wZXJhemlvbmUgY29tcGxlc3NlIGluIFIgYXZ2aWVuZSB1dGlsaXp6YW5kbyBsZSBwYXJlbnRlc2kgcGVyIGRhcmUgcHJpb3JpdMOgIGFkIGFsY3VuZSBwYXJ0aS4gUGVyIGlsIHJlc3RvIFIgc2VndWUgcXVlbGxhIGNoZSDDqCBsYSBub3JtYWxlIHByaW9yaXTDoCBkZWxsZSBvcGVyYXppb25pIG1hdGVtYXRpY2hlLiBQcm92YSBhIHNjcml2ZXJlIGluIFIgcXVlc3RlIG9wZXJhemlvbmk6CgoxLiAkXGZyYWN7KDQ1KzIxKV4zK1xmcmFjezN9ezR9fXtcc3FydHszMi1cZnJhY3sxMn17MTd9fX0kCgoKCgoyLiAkXGZyYWN7XHNxcnR7Ny1ccGl9fXszXCAoNDUtMzQpfSQKCgoKMy4gJFxzcXJ0WzNdezEyLWVeMn0rXGxuKDEwXHBpKSQKCgoKNC4gJFxmcmFje1xzaW4oXGZyYWN7M317NH1ccGkpXjIrXGNvcyhcZnJhY3szfXsyfVxwaSl9e1xsb2dfN3tlXntcZnJhY3szfXsyfX19fSQKCgoKNS4gJFxmcmFje1xzdW1fe249MX1eezEwfSBufXsxMH0kCgoKCk5vdGUgcGVyIGxhIHJpc29sdXppb25lIGRlZ2xpIGVzZXJjaXppOgoKLSBJbiBSIGxhIHJhZGljZSBxdWFkcmF0YSBzaSBvdHRpbmUgY29uIGxhIGZ1bnppb25lIGBzcXJ0KClgIG1lbnRyZSBwZXIgcmFkaWNpIGRpIGluZGljaSBkaXZlcnNpIHNpIHV0aWxpenphIGxhIG5vdGF6aW9uZSBlc3BvbmVuemlhbGUgKCRcc3FydFszXXt4fSQgw6ggZGF0byBkYSBgeF4oMS8zKWApLgotIElsIHZhbG9yZSBkaSAkXHBpJCBzaSBvdHRpZW5lIGNvbiBgcGlgLgotIElsIHZhbG9yZSBkaSAkZSQgc2kgb3R0aWVuZSBjb24gYGV4cCgxKWAuCi0gSW4gUiBwZXIgaSBsb2dhcml0bWkgc2kgdXNhIGxhIGZ1bnppb25lIGBsb2coeCwgYmFzZT1hKWAsIGRpIGJhc2UgdmllbmUgIGNvbnNpZGVyYXRvIGlsIGxvZ2FyaXRtbyBuYXR1cmFsZS4KCiMjIE9wZXJhemlvbmkgbG9naWNoZQoKMS4gRGVmaW5pc2NpIHVuYSBwcm9wb3NpemlvbmUgY2hlIHRpIHBlcm1ldHRhIGRpIHZhbHV0YXJlIHNlIHVuIG51bWVybyDDqCBwYXJpLiBEZWZpbmlzY2kgdW4nYWx0cmEgcHJvcG9zaXppb25lIHBlciBpIG51bWVyaSBkaXNwYXJpICh0aXA6IGNvc2EgdGkgcmljb3JkYSBgJSVgPykuCgoKMi4gRGVmaW5pc2NpIHVuYSBwcm9wb3NpemlvbmUgcGVyIHZhbHV0YXJlIGxhIHNlZ3VlbnRlIGNvbmRpemlvbmUgKHJpY29yZGF0aSBkaSB0ZXN0YXJlIHR1dHRpIGkgcG9zc2liaWxpIHNjZW5hcmkpICIqeCDDqCB1biBudW1lcm8gY29tcHJlc28gdHJhIC00IGUgLTIgb3BwdXJlIMOoIHVuIG51bWVybyBjb21wcmVzbyB0cmEgMiBlIDQqIi4KCgozLiBFc2VndWkgbGUgc2VndWVudGkgb3BlcmF6aW9uaSBgNCBeIDMgJWluJSBjKDIsMyw0KWAgZSBgNCAqIDMgJWluJSBjKDIsMyw0KWAuIENvc2Egb3NzZXJ2aSBuZWxsJ29yZGluZSBkaSBlc2VjdXppb25lIGRlZ2xpIG9wZXJhdG9yaT8KCgo0LiBFc2VndWkgbGUgc2VndWVudGkgb3BlcmF6aW9uaSBsb2dpY2hlIHRyYSBzdHJpbmdoZSBlIHByb3ZhIHByZXZlZGVyZSBpbCByaXN1bHRhdG8gcHJpbWEgZGkgZXNlZ3VpcmUgaWwgY29kaWNlOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KImNpYW8iID09ICJjaWFvIgoiUiIgPT0gInIiCiJTUFNTIiAhPSAiU3BTUyIKIjEiID09IDEKIjEiID4gImNpYW8iCiJhIiA+ICJiIgoiY2lhbyIgPiAiemVicmEiCmBgYAoKCiMgU3RydXR0dXJlIGRhdGkKCiMjIFZldHRvcmkKCiMjIyBDcmVhcmUgdmV0dG9yaQoKMS4gQ3JlYSBpbCB2ZXR0b3JlIGB4YCBjb250ZW5lbnRlIGkgbnVtZXJpIDQsIDYsIDEyLCAzNCwgCgoKMi4gQ3JlYSBpbCB2ZXR0b3JlIGB5YCBjb250ZW5lbnRlIHR1dHRpIGkgbnVtZXJpIHBhcmkgY29tcHJlc2kgdHJhIDEgZSAyNSAoYD9zZXEoKWApCgoKMy4gQ3JlYSBpbCB2ZXR0b3JlIGB6YCBjb250ZW5lbnRlIHR1dHRpIGkgcHJpbWkgMTAgbXVsdGlwbGkgZGkgNyBwYXJ0ZW5kbyBkYSAxNCAoYD9zZXEoKWAgaW4gcGFydGljb2xhcmUgbCdhcmdvbWVudG8gYGxlbmd0aC5vdXQgPWApCgoKNC4gQ3JlYSBpbCB2ZXR0b3JlIGBzYCBpbiBjdWkgbGUgbGV0dGVyZSBgIkEiYCxgIkIiYCBlIGAiQyJgIHZlbmdvbm8gcmlwZXR1dGUgbmVsIG1lZGVzaW1vIG9yZGluZSA0IHZvbHRlIChgP3JlcCgpYCkKCgo1LiBDcmVhIGlsIHZldHRvcmUgYHRgIGluIGN1aSBsZSBsZXR0ZXIgYCJBImAsYCJCImAgZSBgIkMiYCB2ZW5nb25vIHJpcGV0dXRlIG9nbnVuYSA0IHZvbHRlIChgP3JlcCgpYCkKCgo2LiBHZW5lcmEgaWwgc2VndWVudGUgb3V0cHV0IGluIG1vZG8gcGlncm8sIG92dmVybyBzY3JpdmVuZG8gbWVubyBjb2RpY2UgcG9zc2liaWxlIDspCgpgYGAKIyMgWzFdICJmb28iICJmb28iICJiYXIiICJiYXIiICJmb28iICJmb28iICJiYXIiICJiYXIiCmBgYAoKCgojIyMgSW5kaWNpenphcmUgdmV0dG9yaQoKMS4gRGVsIHZldHRvcmUgYHhgIHNlbGV6aW9uYSBpbCAywrAsIDPCsCBlIDXCsCBlbGVtZW50bwoKCjIuIERlbCB2ZXR0b3JlIGB4YCBzZWxlemlvbmEgaSB2YWxvcmkgMzQgZSA0CgoKMy4gRGF0byBpbCB2ZXR0b3JlIGBteV92ZWN0b3IgPSBjKDIsNCw2LDgpYCBjb21tZW50YSBpbCByaXN1bHRhdG8gZGVsIGNvbWFuZG8gYG15X3ZlY3RvcltteV92ZWN0b3JdYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbXlfdmVjdG9yID0gYygyLDQsNiw4KQpteV92ZWN0b3JbbXlfdmVjdG9yXQpgYGAKCjQuIERlbCB2ZXR0b3JlIGB5YCBzZWxlemlvbmEgdHV0dGkgaSB2YWxvcmkgbWlub3JpIGRpIDEzIG8gbWFnZ2lvcmkgZGkgMTkKCgo1LiBEZWwgdmV0dG9yZSBgemAgc2VsZXppb25hIHR1dHRpIGkgdmFsb3JpIGNvbXByZXNpIHRyYSAyNCBlIDUwCgo2LiBEZWwgdmV0dG9yZSBgc2Agc2VsZXppb25hIHR1dHRpIGdsaSBlbGVtZW50aSB1Z3VhbGkgYWQgIGAiQSJgCgoKNy4gRGVsIHZldHRvcmUgYHRgIHNlbGV6aW9uYSB0dXR0aSBnbGkgZWxlbWVudGkgZGl2ZXJzaSBkYSAgYCJCImAKCjguIENyZWEgdW4gbnVvdm8gdmV0dG9yZSBgdWAgaWRlbnRpY28gYSBgc2AgbWEgZG92ZSBsZSBgIkEiYCBzb25vIHNvc3RpdHVpdGUgY29uIGxhIGxldHRlcmEgYCJVImAKCgo5LiBFbGltaW5hIGRhbCB2ZXR0b3JlIGB6YCBpIHZhbG9yaSAyOCBlIDQyCgoKMTAuIFNwZXNzbyDDqCBuZWNlc3NhcmlvIGNyZWFyZSBkZWxsZSBzdHJpbmdoZSByYW5kb20gcGVyIGNvZGlmaWNhcmUgdW4gcGFydGVjaXBhbnRlIGluIG1vZG8gdW5pdm9jbyBtYSBzZW56YSB1c2FyZSBpbmZvcm1hemlvbmkgc2Vuc2liaWxpLiBHZW5lcmEgMSBjb2RpY2UgdW5pdm9jbyBmb3JtYXRpIGRhIGBpZF9zdHJpbmdfbnVtYCBkb3ZlOgogICAgLSBgaWRgIMOoIHVuIG51bWVybyBjYXN1YWxlIHRyYSAxIGUgMTAKICAgIC0gYHN0cmluZ2Agw6ggdW5hIHN0cmluZ2EgZm9ybWF0YSBkYSA1IGxldHRlcmUgY2FzdWFsaQogICAgLSBgbnVtc2Agw6ggdW4gbnVtZXJvIGNhc3VhbGUgdHJhIDEwMCBlIDk5OQpBZCBlc2VtcGlvIGAxX2FkcnR2XzEwMGAgw6ggdW4gaWQgdmFsaWRvLgoKVGlwczoKICAgIC0gdmVkaSBsYSBmdW56aW9uZSBgc2FtcGxlYCBvIGBydW5pZmAgZSBgcm91bmRgIHBlciBnZW5lcmFyZSBpIG51bWVyaQogICAgLSB2ZWRpIGwnb2dnZXR0byBgbGV0dGVyc2AgZ2nDoCBkaXNwb25pYmlsZSBpbiBSCiAgICAtIHZlZGkgbGEgZnVuemlvbmUgYHBhc3RlMCgpYCBvIGBzcHJpbnRmKClgCiAgICAKCiMjIEZhdHRvcmkKCjEuIENyZWEgbGEgdmFyaWFiaWxlIGNhdGVnb3JpYWxlIGBnZW5lcmVgIGNvc8OsIGRlZmluaXRhOgoKYGBge3IsIGVjaG89RkFMU0V9CmZhY3RvcihjKHJlcChjKCJNIiwiRiIpLDMpLCJGIiwiRiIsIk0iKSkKYGBgCgoKMi4gUmlub21pbmEgaSBsaXZlbGxpIGRlbGxhIHZhcmlhYmlsZSBgZ2VuZXJlYCByaXNwZXR0aXZhbWVudGUgaW4gYCJkb25uZSJgIGUgYCJ1b21pbmkiYC4KCgozLiBDcmVhIGxhIHZhcmlhYmlsZSBjYXRlZ29yaWFsZSBgaW50ZXJ2ZW50b2AgY29zw6wgZGVmaW5pdGE6CgpgYGB7ciwgZWNobyA9IEZBTFNFfQpmYWN0b3IoYyhyZXAoYygiQ0JUIiwiUHNpY2FuYWxpc2kiKSwzKSwiQ29udHJvbGxvIiwiQ29udHJvbGxvIiwiQ0JUIikpCmBgYAoKCjQuIENvcnJlZ2dpIG5lbGxhIHZhcmlhYmlsZSBgaW50ZXJ2ZW50b2AgbGEgN8KwIGUgOMKwIG9zc2VydmF6aW9uZSBjb24gbGEgdm9jZSBgRmFybWFjaWAuIE5vdGF0ZSBxdWFsY29zYSBkaSBzdHJhbm8/CgoKCjUuIEFnZ2l1bmdpIGFsbGEgdmFyaWFiaWxlIGBpbnRlcnZlbnRvYCBsZSBzZWd1ZW50aSBudW92ZSBvc3NlcnZhemlvbmk6CgpgYGB7cn0KYygiRmFybWFjaSIsIkNvbnRyb2xsbyIsIkZhcm1hY2kiKQpgYGAKCgojIyBNYXRyaWNpCgoxLiBDcmVhIGxhIG1hdHJpY2UgYEFgIGNvc8OsIGRlZmluaXRhOiAKCiQkClxiZWdpbnttYXRyaXh9CjIgJiAzNCAmIDEyICYgN1xcCjQ2ICYgOTMgJiAyNyAmIDk5XFwKMjMgICYgMzggJiA3ICYgMDQKXGVuZHttYXRyaXh9CiQkCgoyLiBDcmVhIGxhIG1hdHJpY2UgYEJgIGNvbnRlbmVudGUgdHV0dGkgaSBwcmltaSAxMiBudW1lcmkgZGlzcGFyaSBkaXNwb3N0aSBzdSA0IHJpZ2hlIGUgMyBjb2xvbm5lLgozLiBDcmVhIGxhIG1hdHJpY2UgYENgIGNvbnRlbmVudGUgaSBwcmltaSAxMiBtdWx0aXBsaSBkaSA5IGRpc3Bvc3RpIHN1IDMgcmlnaGUgZSA0IGNvbG9ubmUuCjQuIENyZWEgbGEgbWF0cmljZSBgRGAgIGZvcm1hdGEgZGEgMyBjb2xvbm5lIGluIGN1aSBsZSBsZXR0ZXJlIGAiQSJgLGAiQiJgIGUgYCJDImAgdmVuZ2FubyByaXBldHV0ZSA0IHZvbHRlIGNpYXNjdW5hIHJpc3BldHRpdmFtZW50ZSBuZWxsYSBwcmltYSwgc2Vjb25kYSBlIHRlcnphIGNvbG9ubmEuCjUuIENyZWEgbGEgbWF0cmljZSBgRWAgIGZvcm1hdGEgZGEgMyByaWdoZSBpbiBjdWkgbGUgbGV0dGVyZSBgIkEiYCxgIkIiYCBlIGAiQyJgIHZlbmdhbm8gcmlwZXR1dGUgNCB2b2x0ZSBjaWFzY3VuYSByaXNwZXR0aXZhbWVudGUgbmVsbGEgcHJpbWEsIHNlY29uZGEgZSB0ZXJ6YSByaWdhLgo2LiBVdGlsaXp6YW5kbyBnbGkgaW5kaWNpIGRpIHJpZ2EgZSBkaSBjb2xvbm5hIHNlbGV6aW9uYSBpbCBudW1lcm8gMjcgZGVsbGEgbWF0cmljZSBgQWAKNy4gU2VsemlvbmEgZ2xpIGVsZW1lbnRpIGNvbXByZXNpIHRyYSBsYSBzZWNvbmRhIGUgcXVhcnRhIHJpZ2EsIHNlY29uZGEgZSB0ZXJ6YSBjb2xvbm5hIGRlbGxhIG1hdHJpY2UgYEJgCjguIFNlbGV6aW9uYSBzb2xvIGdsaSBlbGVtZW50aSBwYXJpIGRlbGxhIG1hdHJpY2UgYEFgIChOb3RhOiB1dGlsaXp6YSBsJ29wZXJhemlvbmUgcmVzdG8gYCUlYCkKOS4gRWxpbWluYSBkYWxsYSBtYXRyaWNlIGBDYCBsYSB0ZXJ6YSByaWdhIGUgbGEgdGVyemEgY29sb25uYQoxMC4gU2VsZXppb25hIHR1dHRpIGdsaSBlbGVtZW50aSBkZWxsYSBzZWNvbmRhIGUgdGVyemEgcmlnYSBkZWxsYSBtYXRyaWNlIGBCYAoxMS4gU2VsZXppb25hIHR1dHRpIGdsaSBlbGVtZW50aSBkaXZlcnNpIGRhICDigJxC4oCdIGFwcGFydGVuZW50aSBhbGxhIG1hdHJpY2UgYERgCjEyLiBDcmVhIGxhIG1hdHJpY2UgYEdgIHVuZW5kbyBhbGxhIG1hdHJpY2UgYEFgIGxlIHByaW1lIGR1ZSBjb2xvbm5lIGRlbGxhIG1hdHJpY2UgYENgCjEzLiBDcmVhIGxhIG1hdHJpY2UgYEhgIHVuZW5kbyBhbGxhIG1hdHJpY2UgYENgIGxlIHByaW1lIGR1ZSByaWdoZSBkZWxsYSBtYXRyaWNlIHRyYXNwb3N0YSBkaSBgQmAKMTQuIFJpZGVmaW5pc2NpIGxhIG1hdHJpY2UgYEFgIGVsaW1pbmFuZG8gbGEgc2Vjb25kYSBjb2xvbm5hLiBSaWRlZmluaXNjaSBsYSBtYXRyaWNlIGBCYCBlbGltaW5hbmRvIGxhIHByaW1hIHJpZ2EuIFZlcmlmaWNhIGNoZSBsZSAgbWF0cmljaSBjb3PDrCBvdHRlbnV0ZSBhYmJpYW5vIGxhIHN0ZXNzYSBkaW1lbnNpb25lLgoxNS4gQ29tbWVudGEgaSBkaWZmZXJlbnRpIHJpc3VsdGF0aSBjaGUgb3R0ZW5pYW1vIG5lbGxlIG9wZXJhemlvbmkgYEEqQmAsIGBCKkFgLCBgQSUqJUJgIGUgYEIlKiVBYC4KMTYuIEFzc2VnbmEgaSBzZWd1ZW50aSBub21pIGFsbGUgY29sb25uZSBlIGFsbGUgcmlnaGUgZGVsbGEgbWF0cmljZSBgQ2A6IGAiY29sXzEiLCAiY29sXzIiLCAiY29sXzMiLCAiY29sXzQiLCAicm93XzEiLCAicm93XzIiLCAicm93XzMiYC4KCiMjIExpc3RlCgoxLiBDcmVhIHVuYSAqKm5hbWVkIGxpc3QqKiBjb24gcXVlc3RpIGVsZW1lbnRpOgoKLSB1biB2ZXR0b3JlIG51bWVyaWNvIChgeGApIGNvbiBpIG51bWVyaSBkYSAxIGEgMTAwCi0gdW4gdmV0dG9yZSBkaSBjYXJhdHRlcmkgKGB5YCkgY29uIGxlIHByaW1lIDE1IGxldHRlcmUgZGVsbCdhbGZhYmV0byAodmVkaSBsJ29nZ2V0dG8gYGxldHRlcnNgKQotIGxhIG1hdHJpY2UgYEJgIGNyZWF0YSBuZWxsJ2VzZXJjaXppbyBwcmVjZWRlbnRlCi0gdW4gZmF0dG9yZSBjcmVhdG8gbmVsbCdlc2VyY2l6aW8gcHJlY2VkZW50ZQoKMi4gQWdnaXVuZ2kgYWxsYSBsaXN0YSB1biB2ZXR0b3JlIGxvZ2ljbyBgVFJVRS9GQUxTRWAgdGVzdGFuZG8gY2hlIGlsIHZldHRvcmUgbnVtZXJpY28gY3JlYXRvIGluIHByZWNlZGVuemEgYWJiaWEgbnVtZXJpIHBhcmkgKGBUUlVFYCkgbyBkaXNwYXJpIChgRkFMU0VgKQozLiBBZ2dpdW5naSB1biBhbHRybyBlbGVtZW50byBhbGxhIGxpc3RhIGNvbWUgdmV0dG9yZSBudW1lcmljbyBjcmVhdG8gZmFjZW5kbyB1bmEgc2VsZXppb25lICBkZWdsaSBlbGVtZW50aSBkaSBgeGAKNC4gU292cmFzY3JpdmkgaWwgdmV0dG9yZSBgeWAgY29uIGxvIHN0ZXNzbyB2ZXR0b3JlIGRpIGNhcmF0dGVyaSBtYSByaXBldGVuZG8gb2duaSBlbGVtZW50byA0IHZvbHRlCjUuIFNlbGV6aW9uYSBpIG51bWVyaSBkYSAxIGEgNTAgZGVsIHZldHRvcmUgYHhgIGFsbCdpbnRlcm5vIGRlbGxhIGxpc3RhCjYuIEFnZ2l1bmdpIGFsbGEgbGlzdGEgdW4ndWx0ZXJpb3JlIGxpc3RhIHNlbGV6aW9uYW5kbyBpIHByaW1pIDMgZWxlbWVudGkgZGVsbGEgbGlzdGEgb3JpZ2luYWxlCjcuIEFnZ2l1bmdpIGFsbGEgbGlzdGEgbmVzdGVkIHVuIHZldHRvcmUgbnVtZXJpY28gc2VsZXppb25hbmRvIHNvbG8gaSBudW1lcmkgZGkgYHhgIGRpdmlzaWJpbGkgcGVyIDMgKHJpY29yZGkgbGEgZnVuemlvbmUgbW9kdWxvIGAlJWA/KQoKIyMgRGF0YWZyYW1lCgoxLiBSaWNyZWEgcXVlc3RvIGRhdGFmcmFtZSBpbiBSOgoKYGBge3J9CmRhdGFfbG9uZzwtZGF0YS5mcmFtZShJZD1yZXAoYygic3Vial8xIiwic3Vial8yIiwic3Vial8zIiksZWFjaD0zKSwKICAgICAgICAgICAgICAgICAgICAgIGFnZT1yZXAoYygyMSwyMywxOSksZWFjaD0zKSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmRlcj1yZXAoYygiRiIsIk0iLCJGIiksZWFjaD0zKSwKICAgICAgICAgICAgICAgICAgICAgIGl0ZW09cmVwKDE6MywzKSwKICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlPWMoMiwxLDEsMCwyLDEsMiwwLDEpKQpgYGAKCjIuIFV0aWxpenphbmRvIGlsIGRhdGFmcmFtZSBgZGF0YWZyYW1lX2V4YW1wbGUxLnJkc2AgZXNlZ3VpIGxlIHNlZ3VlbnRpIG9wZXJhemlvbmk6CgotIHNlbGV6aW9uYXJlIGxlIHBlcnNvbmUgY29uIGV0w6AgbWFnZ2lvcmUgZGkgMzAgYW5uaQotIHNlbGV6aW9uYXJlIGxlIHBlcnNvbmUgY29uIGV0w6AgbWFnZ2lvcmUgZGkgMzAgYW5uaSBlIG1pbm9yZSBkaSAyMQotIHNlbGV6aW9uYXJlIGxlIHBlcnNvbmUgY2hlIHN0dWRpYW5vIGFsICpkYW1zKiBjb24gdm90byBkaSBsYXVyZWEgbWFnZ2lvcmUgZGkgMTAwIGUgYWxtZW5vIDEgZnJhdGVsbG8vc29yZWxsYQotIHNlbGV6aW9uYXJlIGxlIHBlcnNvbmUgY2hlIHN0dWRpYW5vICpwc2ljb2xvZ2lhKiBlIG5vbiBoYW5ubyBmcmF0ZWxsaQotIHNlbGV6aW9uYXJlIGxlIHBlcnNvbmUgY2hlIGhhbm5vIHB1bnRlZ2dpIGRpIGRlcHJlc3Npb25lIG1hZ2dpb3JpIGRpIDUwIGUgcHVudGVnZ2kgZGkgYW5zaWEgbWFnZ2lvcmkgZGkgMjAKCjMuIFV0aWxpenphIGxhIGZ1bnppb25lIGBwdXRfcmFuZG9tX25hKGRhdGEsIG4pYCBkb3ZlIGBkYXRhYCDDqCBpbCBkYXRhZnJhbWUgZSBgbmAgw6ggaWwgbnVtZXJvIGRpIGBOQWAgZGEgZ2VuZXJhcmUuIE9yYToKCi0gc2VsZXppb25hIGxlIHBlcnNvbmUgY29uIGBOQWAgcGVyIGxhIGNvbG9ubmEgYGRlZ3JlZWAgZSBwZXIgbGEgY29sb25uYSBgZXTDoGAKLSBzZWxlemlvbmEgbGUgcGVyc29uZSBjb24gYE5BYCBwZXIgbGEgY29sb25uYSBgZW1haWxgIGUgYWJpdGFudGkgZGVsIHZlbmV0byBvIGNhbXBhbmlhCi0gc2VsZXppb25hIGxlIHBlcnNvbmUgY2hlIG5vbiBoYW5ubyBuZXNzdW4gTkEgKHZlZGkgbGEgZnVuemlvbmUgYGNvbXBsZXRlLmNhc2VzKClgKQoKNC4gVXRpbGl6emFuZG8gaWwgZGF0YXNldCBgZGF0YWZyYW1lX2V4YW1wbGUyLmNzdmAKCi0gaW1wb3J0YSBpbCBkYXRhZnJhbWUgZGFsIGZvcm1hdG8gYGNzdmAKLSBjcmVhIHVuYSBudW92YSBjb2xvbm5hIGBpZGAgY2hlIGlkZW50aWZpY2hpIGluIG1vZG8gdW5pdm9jbyBvZ25pIHNvZ2dldHRvIHByb2dyZXNzaXZhbWVudGUKLSBjb250cm9sbGEgY2hlIHR1dHRlIGxlIGNvbG9ubmUgc2lhbm8gZGVsIHRpcG8gYXBwcm9wcmlhdG8gKHVzYSBsYSBmYW1pZ2xpYSBgaXMuKmApCi0gc2VsZXppb25hIHR1dHRlIGxlIHBlcnNvbmUgYSBjdWkgbm9uIHBpYWNlIGBuZXNzdW5hYCBmZXN0YS4gTm90aSBxdWFsY2hlIHByb2JsZW1hPwotIHNlbGV6aW9uYSB0dXR0ZSBsZSBwZXJzb25lIGEgY3VpIHBpYWNlIGlsIGNvbG9yZSBgYmx1YCBlIHBpYWNlIGlsIGBuYXRhbGVgCi0gcGVyIHJpc29sdmVyZSBpIHByb2JsZW1pIGNvbiBsZSBzdHJpbmdoZSwgdHJvdmEgdW4gbW9kbyBwZXIgcG9ydGFyZSB0dXR0ZSBsZSBzdHJpbmdoZSBhZCB1biBmb3JtYXRvIGNvbXBhcmFiaWxlCi0gZmFpIHVuIHN1YnNldCBkZWwgZGF0YXNldCB0ZW5lbmRvIHNvbG8gbGUgY29sb25uZSBudW1lcmljaGUgKHVzYSBsYSBgaXMuKiBmYW1pbHlgKQoKCiMgUHJvZ3JhbW1hemlvbmUKCiMjIEZ1bnppb25pCgoxLiBkZWZpbmlzY2kgdW5hIGZ1bnppb25lIGNoZSB0cmFzZm9ybWkgbGEgdGVtcGVyYXR1cmEgZGEgQ2Vsc2l1cyBhIEZhaHJlbmhlaXQgCgokJApGYWhyZW5oZWl0ID0gQ2Vsc2l1cyAqIDEuOCArIDMyCiQkCgoyLiBEZWZpbmlzY2kgdW5hIGZ1bnppb25lIGNoZSBwZXJtZXR0YSBkaSBmYXJlIGdsaSBhdWd1cmkgZGkgYnVvbiBuYXRhbGUgZSBidW9uYSBwYXNxdWEgYWQgdW5hIHBlcnNvbmEuIFByb3ZhIGEgdXRpbGl6emFyZSBlIGNhcGlyZSBsZSBmdW56aW9uaSBgcGFzdGUoKWAgZSBgcHJpbnQoKWAuCjMuIERlZmluaXNjaSB1bmEgZnVuemlvbmUgYG5fYW5kX21lZGlhKClgIGNoZSwgZGF0byB1biB2ZXR0b3JlIGRpIHZhbG9yaSBudW1lcmljaSwgY2FsY29saSBpbCBudW1lcm8gZGkgZWxlbWVudGkgZSBsYSBsb3JvIG1lZGlhIGUgcmVzdGl0dWlzY2EgZW50cmFtYmUgaW4gdW5hIGZyYXNlLiBBZCBlc2VtcGlvIGBuX2FuZF9tZWRpYSh4KWAgZGV2ZSByZXN0aXR1aXJlICJsYSBtZWRpYSBkaSB4IMOoIC4uLiBlIGxhIGx1bmdoZXp6YSBkaSB4IMOoIC4uLiIuIEFuY2hlIHF1aSBzb25vIHV0aWxpIGxlIGZ1bnppb25pIGBwYXN0ZSgpYC9gc3ByaW50ZmAgZSBgcHJpbnQoKWAuCgojIyBDb25kaXppb25hbGkKCmBgYHtyLCBlY2hvID0gVFJVRX0Kc2NvcmUgPC0gcnVuaWYoMTAwLCAwLCAxKQpgYGAKCjEuIERlZmluaXNjaSB1bmEgZnVuemlvbmUgcGVyIGFzc2VnbmFyZSB1biB2b3RvIGluIGJhc2UgYWxsYSBwZXJjZW50dWFsZSBkaSByaXNwb3N0ZSBjb3JyZXR0ZSAoKnNjb3JlKikgc2VndWkgbGUgc2VndWVudGkgaW5kaWNhemlvbmk6IAoKLSBzY29yZSA8IC41NSBpbnN1ZmZpY2llbnRlCi0gLjU1IDw9IHNjb3JlIDwgLjY1IC0gc3VmZmljaWVudGUKLSAuNjUgPD0gc2NvcmUgPCAuNzUgLSBidW9ubwotIC43NSA8PSBzY29yZSA8IC44NSAtIGRpc3RpbnRvCi0gLjg1IDw9IHNjb3JlIC0gb3R0aW1vCgpMYSBmdW56aW9uZSBxdWluZGkgZGV2ZSByaWNldmVyZSBpbiBpbnB1dCB1biB2YWxvcmUgZSByZXN0aXR1aXJlIGxhIHN0cmluZ2EgY29ycmlzcG9uZGVudGUgaW4gYmFzZSBhbCB2YWxvcmUgc3Rlc3NvLgoKMi4gVXNhIHVuYSBzZXJpZSBkaSBgaWZlbHNlKClgIG5lc3RlZCBwZXIgY3JlYXJlIHVuYSB2YXJpYWJpbGUgYHNjb3JlX2NocmAgY29uIGxlIGV0aWNoZXR0ZSBjb3JyaXNwb25kZW50aSBhbGwnZXNlcmNpemlvIHByZWNlZGVudGUuCjMuIERlZmluaXNjaSB1bmEgZnVuemlvbmUgY2hlIGRldGVybWluaSBzZSB1biBudW1lcm8gw6ggcGFyaSBvIGRpc3BhcmkuCjQuIERlZmluaXNjaSB1bmEgZnVuemlvbmUgY2hlIGRldGVybWluaSBzZSB1biBudW1lcm8gw6ggdW4gbXVsdGlwbG8gZGkgMywgNCwgbyA1LgoKIyMgTG9vcAoKIyMjIFNjcml2aSB1biBsb29wIGNoZToKClNjcml2aSB1biBsb29wIGNoZSBzY29ycmUgbGUgY29sb25uZSBkZWwgZGF0YXNldCBgaXJpc2AgKGxvIHRyb3ZhdGUgZGlyZXR0YW1lbnRlIGluIFIpIGUgc3RhbXBhIGlsIG5vbWUgZGVsbGEgY29sb25uYSBhc3NpZW1lIGFsIG51bWVybyBkaSBjYXJhdHRlcmkgY2hlIGNvbXBvbmUgbGEgc3RyaW5nYS4gQWQgZXNlbXBpbyBgU2VwYWwuTGVuZ3RoICgxMilgLiBQb3RldGUgdXNhcmUgbGUgZnVuemlvbmkgYHByaW50KClgLCBgcGFzdGUoKWAgYW5kIGBuY2hhcigpYC4KCiMjIyBTY3JpdmkgdW4gbG9vcCBjaGU6CgpTY3JpdmkgdW4gbG9vcCBjaGUgc2NvcnJlIGxlIGNvbG9ubmUgZGVsIGRhdGFzZXQgYGlyaXNgIChsbyB0cm92YXRlIGRpcmV0dGFtZW50ZSBpbiBSKSBlIHJlc2l0dWlzY2UgbGEgYG1lZGlhYCBzZSBsYSBjb2xvbm5hIMOoIG51bWVyaWNhIChyaWNvcmRhdGUgbGUgYGlzLipgIGZhbWlseSkgb3BwdXJlIGxhIHRhYmVsbGEgZGkgZnJlcXVlbnphIChjb21hbmRvIGB0YWJsZSgpYCkgc2UgbGEgY29sb25uYSDDqCBzdHJpbmdhL2ZhdHRvcmUKCiMjIyBgKmFwcGx5KClgCgpVc2FuZG8gbGEgZnVuemlvbmUgcGnDuSBhZGF0dGEgZGVsbGAqYXBwbHkoKWAgZmFtaWx5LCBjYWxjb2xhIGxhIGBkZXZpYXppb25lIHN0YW5kYXJkYCBkaSBvZ25pIGNvbG9ubmEgZGVsIGRhdGFzZXQgYG10Y2Fyc2AgKGxvIHRyb3ZhdGUgZGlyZXR0YW1lbnRlIGluIFIpCgojIyMgUG9wb2xhIGxhIG1hdHJpY2UgY29uIHVuIGBmb3JgCgpQb3BvbGlhbW8gdW5hIG1hdHJpY2UgdXNhbmRvIGlsIGNpY2xvIGBmb3JgLiBMYSBtYXRyaWNlIGhhIHVuIG51bWVybyAqdG90YWxlKiBkaSBlbGVtZW50aS4gVXNhbmRvIGlsIHZldHRvcmUgYHhgIGNoZSBjb250aWVuZSBpbCBudW1lcm8gZGkgZWxlbWVudGkgdG90YWxpIGRlbGxhIG1hdHJpY2UgY29tZSBwb3NzaWFtbyBwb3BvbGFyZSBsYSBtYXRyaWNlIHBhcnRlbmRvIGRhIHVuYSB2dW90YSBgbWF0cml4KE5BLCAxMCwgMTApYC4gTGEgbWF0cmljZSDDqCAqKnF1YWRyYXRhKiogZGkgZGltZW5zaW9uZSAxMHgxMDoKCmBgYHtyfQojIG1hdHJpY2UgMTAgeCAxMCBxdWluZGkgMTAwIGVsZW1lbnRpIHRvdGFsaQpteV9tYXQgPC0gbWF0cml4KE5BLCAxMCwgMTApICMgbWF0cmljZSB2dW90YSAxMHgxMApgYGAKClN1Z2dlcmltZW50bzogcHVvaSBmYXJlIHVuIGRvcHBpbyBjaWNsbyBgZm9yYCBlIGdlbmVyYXJlIHVuIG51bWVybyBjYXN1YWxlIGRhIGluc2VyaXJlIGNvbiBgcm5vcm0oMSlgCgojIyBSaS1jcmVpYW1vIGxlIGZ1bnppb25pIGluIFIKClVuIG90dGltbyBlc2VyY2l6aW8gcGVyIGFmZmluYXJlIGxlIG5vc3RyZSBjb21wZXRlbnplIGRpIGNvZGljZSDDqCBxdWVsbG8gZGkgcmljcmVhcmUgbGUgZnVuemlvbmkgZGkgUiBjaGUgZGlhbW8gcGVyIHNjb250YXRlIGRpY2lhbW8uIExhIGZ1bnppb25lIGBtZWFuKClgIGFkIGVzZW1waW8gc2kgcHXDsiBmYWNpbG1lbnRlIGNyZWF0ZSBjb24gdW4gY2ljbG8gZm9yIGUgcXVhbGNoZSBhbHRyYSBhZ2dpdW50YS4KCiMjIyBgc3VtKClgCgpSaWNyZWEgbGEgZnVuemlvbmUgYHN1bSgpYCBlIHRlc3RhbGEgc3VsIHNlZ3VlbnRlIHZldHRvcmU6CgpgYGB7cn0KeCA8LSBybm9ybSgxMDApCnN1bSh4KQpgYGAKCiMjIyBgbWVhbigpYAoKUmljcmVhIGxhIGZ1bnppb25lIGBtZWFuKClgIHRlc3RhbGEgc3VsIHNlZ3VlbnRlIHZldHRvcmUgY29uZnJvbnRhbmRvIGlsIHJpc3VsdGF0byBjb24gbGEgZnVuemlvbmUgaW50ZXJuYSAoaGludDogcHVvaSB1c2FyZSBsYSBmdW56aW9uZSBgc3VtKClgIGNyZWF0YSBwcmltYSk6CgpgYGB7cn0KeCA8LSBybm9ybSgxMDApCm1lYW4oeCkKYGBgCgojIyMgYG1lZGlhbigpYAoKTGEgW21lZGlhbmFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01lZGlhbikgw6ggZGVmaW5pdGEgY29tZSBpbCB2YWxvcmUgY2hlIGRpdmlkZSBhIG1ldMOgIHVuYSBkaXN0cmlidXppb25lIGRpIHZhbG9yaS4gUmljcmVhbGEgaW4gUiBmYWNlbmRvIGF0dGVuemlvbmUgY2hlIGxhIGZvcm11bGEgY2FtYmlhIGEgc2Vjb25kYSBjaGUgaWwgdmV0dG9yZSBzaWEgZGkgbHVuZ2hlenphIHBhcmkgbyBkaXNwYXJpIChoaW50OiBwdW9pIHVzYXJlIHVuIGBpZmAgcGVyIGNvbnRyb2xsYXJlIHF1ZXN0ZSBjb25kaXppb25pKQoKYGBge3J9Cm1lZGlhbih4KQpgYGAKCiMjIyBNb2RhCgpJbiBSIG5vbiDDqCBwcmV2aXN0YSB1bmEgZnVuemlvbmUgcGVyIGNhbGNvbGFyZSBsYSAqKm1vZGEqKi4gSW4gc3RhdGlzdGljYSBsYSBtb2RhIMOoIGRlZmluaXRhIGNvbWUgaWwgdmFsb3JlL2kgYXNzb2NpYXRvIGEgbWFnZ2lvcmUgZnJlcXVlbnphLiBBZCBlc2VtcGlvIHNlIGFiYmlhbW8gdW4gdmV0dG9yZSAkeCQgY29tZSBgYygxLDEsMSwyLDMsNSlgIGxhIG1vZGEgc2Fyw6AgMS4KCi0gY29tZSBzaSBwb3RyZWJiZSBjYWxjb2xhcmUgbGEgbW9kYSBpbiBSPwotIGNpIHNvbm8gZGVpIHBhY2NoZXR0aSBjaGUgbCdoYW5ubyBpbXBsZW1lbnRhdGE/Ci0gcG9zc29ubyBlc3NlcmNpIHNpdHVhemlvbmkgZG92ZSBhYmJpYW1vIHBpw7kgbW9kZS4gTGEgZnVuemlvbmUgY2hlIGhhaSB0cm92YXRvL3Njcml0dG8gZ2VzdGlzY2UgcXVlc3RvIHNjZW5hcmlvPyBldmVudHVhbG1lbnRlIGNvbWUgcHVvaSBtb2RpZmljYXJsYT8KCiMjIyBgc2QoKWAKCkxhIGRldmlhemlvbmUgc3RhbmRhcmQgw6ggZmFjaWxtZW50ZSBpbXBsZW1lbnRhdGEgaW4gUiBjb24gbGEgZnVuemlvbmUgYHNkKClgLiBQcm92YSBhIHJpY3JlYXJsYSBjb24gdHV0dGkgZ2xpIHN0cnVtZW50aSBjaGUgYWJiaWFtbyBpbXBhcmF0byBmaW5vIGFkIG9yYS4gTGEgZm9ybXVsYSBtYXRlbWF0aWNhIMOoIGxhIHNlZ3VlbnRlOgoKJCQKU0QgPSBcc3FydHtcZnJhY3tcc3VtX3tpID0gMX1ee059ICh4X2kgLSBcbXVfeCleMn17Tn19CiQkCkF0dGVuemlvbmUgY2hlIFIgdXRpbGl6emEgdW4gZGVub21pbmF0b3JlIGRpdmVyc28gcGVyIGxhIGZ1bnppb25lIGBzZCgpYCByaXNwZXR0byBhbGxhIGZvcm11bGEgcHJvcG9zdGEuIENlcmNhIGRpIGNhcGlyZSBjb3NhIHV0aWxpenphIFIgYXByZW5kbyBsYSBkb2N1bWVudGF6aW9uZSBkaSBgc2QoKWAgZSBpbXBsZW1lbnRhIGxhIHZlcnNpb25lIGRpIFIgZSBxdWVsbGEgZGVsbGEgZm9ybXVsYS4gQ3JlYSBwb2kgdW5hIHRlcnphIHZlcnNpb25lIGRlbGxhIGZ1bnppb25lIGBteV9zZCh4LCB2ZXJzaW9uZSlgIGRvdmUgdmllbmUgY2FsY29sYXRhIHVuYSBvIGwnYWx0cmEgdmVyc2lvbmUgaW4gYmFzZSBhIGNvc2EgdmllbmUgbWVzc28gY29tZSBhcmdvbWVudG8uCgojIyMgVHJvdmEgaWwgbnVtZXJvIGFsbCdpbnRlcm5vIGRlbCB2ZXR0b3JlCgpTY3JpdmkgdW5hIGZ1bnppb25lIGNoZSBkYXRvIGluIGlucHV0IHVuIHZldHRvcmUgYHhgIHJlc3RpdHVpc2NhIGlsIG51bWVybyBkaSB2b2x0ZSBjaGUgY29tcGFyZSBpbCBudW1lcm8gJDMkOgoKLSBwZXIgdGVzdGFyZSBsYSBmdW56aW9uZSBnZW5lcmEgdW4gdmV0dG9yZSBkaSBudW1lcmkgY2FzdWFsaSBjb24gYHJvdW5kKHJ1bmlmKDIwLCAxLCAxMCkpYAoKIyMjIE51bWVyaSBwYXJpCgpTY3JpdmkgdW5hIGZ1bnppb25lIGNoZSBwcmVuZGEgaW4gaW5wdXQgdW4gdmV0dG9yZSBgeGAgZSByZXN0aXR1aXNjYSBpbCBudW1lcm8gZGkgbnVtZXJpIHBhcmkgYWxsJ2ludGVybm8gZGVsIHZldHRvcmUuIFNjcml2aSB1bmEgZnVuemlvbmUgY2hlIHV0aWxpenppIHVuIGBmb3IgbG9vcGAgbWVudHJlIHVuJ2FsdHJhIGNoZSB1c2kgdW4nb3BlcmF6aW9uZSB2ZXR0b3JpenphdGEuCgotIHVzYSBsJ29wZXJhdG9yZSBkaXZpc2lvbmUgaW50ZXJhIGAlJWAKCiMjIyBgY29tcGxldGUuY2FzZXMoKWAKCkNlcmNhIGRpIGNhcGlyZSBjb3NhIGZhIGxhIGZ1bnppb25lIGBjb21wbGV0ZS5jYXNlcygpYCBlIGRpIGNyZWFyZSB1bmEgZnVuemlvbmUgcGVyc29uYWxpenphdGEgYG15X2NvbXBsZXRlX2Nhc2VzKClgIGNvbiBjdWkgb3R0ZW5lcmUgbG8gc3Rlc3NvIG91dHB1dC4KCmBgYHtyfQpjb21wbGV0ZS5jYXNlcyhpcmlzWzE6MTAsIF0pCmBgYAoKU3VnZ2VyaW1lbnRpOgoKLSB1c2EgbGEgZnVuemlvbmUgYGFueSgpYAotIHB1b2kgdXNhcmUgdW4gYGNpY2xvIGZvcmAgb3BwdXJlIHVuYSBmdW56aW9uZSBkZWxsJ2AqYXBwbHlgIGZhbWlseQoKIyMjIFRyb3ZpYW1vIGdsaSBvdXRsaWVyCgpVbm8gc3RlcCBpbXBvcnRhbnRlIHF1YW5kbyBzaSBhbmFsaXp6YW5vIGkgZGF0aSDDqCBxdWVsbG8gZGkgY2VyY2FyZSB2YWxvcmkgYW5vbWFsaSAoYWQgZXNlbXBpbyBjaGUgc3VwZXJhbm8gdW5hIGNlcnRhIHNvZ2xpYSBvcHB1cmUgY2hlIHNvbm8gdHJvcHBvIG9sdHJlIGdsaSBpbmRpY2kgZGkgdGVuZGVuemEgY2VudHJhbGUpLiAKCi0gU2NyaXZpIHVuYSBmdW56aW9uZSBgaXNfb3V0bGllcih4KWAgY2hlIGRhdG8gaW4gaW5wdXQgdW4gdmV0dG9yZSBgeGAsIHJlc3RpdHVpc2NhIHVuIHZldHRvcmUgbG9naWNvIGRvdmUgYFRSVUVgIGNvcnJpc3BvbmRlIGFkIHVuIHZhbG9yZSBjaGUgw6ggbWFnZ2lvcmUgZGVsbGEgbWVkaWEgcGnDuSB1bmEgdm9sdGEgbGEgZGV2aWF6aW9uZSBzdGFuZGFyZCBkaSBgeGAuCi0gU2NyaXZpIHVuYSBmdW56aW9uZSBgaGFzX291dGxpZXJgIGNoZSBhcHBsaWNoaSBgaXNfb3V0bGllcigpYCBhZCBvZ25pIGNvbG9ubmEgZGkgdW4gZGF0YWZyYW1lIGUgcmVzdGl0dWlzY2EgaSBub21pIGRlbGxlIGNvbG9ubmUgZG92ZSDDqCBwcmVzZW50ZSAqKmFsbWVubyAxIG91dGxpZXIqKi4KICAgIC0gcHVvaSB1c2FyZSBsYSBmdW56aW9uZSBgYW55KClgCiAgICAtIHB1b2kgdXNhcmUgdW5hIGZ1bnppb25lIGRlbnRybyB1bidhbHRyYSBmdW56aW9uZQogICAgLSB1c2EgaWwgZGF0YWZyYW1lIGBtdGNhcnNgCi0gbW9kaWZpY2EgbGEgZnVuemlvbmUgYGhhc19vdXRsaWVycygpYCBwZXIgZnVuemlvbmFyZSBzb2xvIHN1bGxlIGNvbG9ubmUgZGVsIGRhdGFmcmFtZSBjaGUgc29ubyAqKm51bWVyaWNoZSoqIChubyBmYXR0b3JpIG8gY2FyYXR0ZXJpKQoKVXNhIGlsIGRhdGFmcmFtZSBgbXlfbXRjYXJzYCBkb3ZlIHNvbm8gc3RhdGUgYWdnaXVudGUgZGVsbGUgdmFyaWFiaWxpIG5vbiBudW1lcmljaGU6CgpgYGB7cn0KbXlfbXRjYXJzIDwtIG10Y2FycwpteV9tdGNhcnMkZmFjdG9yMSA8LSBmYWN0b3IocmVwKGMoImEiLCAiYiIsICJjIiksIGMoMTAsIDEwLCAxMikpKQpteV9tdGNhcnMkZmFjdG9yMiA8LSByZXAoYygiZCIsICJlIiwgImYiKSwgYygxMCwgMTAsIDEyKSkKYGBgCgojIyMgQm9vdHN0cmFwcGluZwoKSWwgYm9vdHN0cmFwcGluZyDDqCB1bmEgdGVjbmljYSBzdGF0aXN0aWNhIG1vbHRvIHV0aWxpenphdGEuIExhIGxvZ2ljYSBkZWxsYSBwcm9ncmFtbWF6aW9uZSBwZXLDsiDDqCBtb2x0byBzZW1wbGljZSBtYSBpbnRlcmVzc2FudGUgKGUgcG90cmViYmUgZXNzZXJ2aSBhbmNoZSB1dGlsZSkuIFNjcml2ZXRlIHVuYSBmdW56aW9uZSBgcHJlcGFyZV9ib290c3RyYXAoZGF0YSwgbnJvdywgbilgIGNoZSBwcmVuZGEgaW4gaW5wdXQgdW4gZGF0YWZyYW1lIGBkYXRhYCwgaWwgbnVtZXJvIGRpIHJpZ2hlIGRhIGVzdHJhcnJlIGBucm93YCBlIHF1YW50aSBkYXRhZnJhbWUgY3JlYXJlIGBuYCBlIGNoZSBmb3JuaXNjYSBxdWluZGkgYG5gIGRhdGFmcmFtZSBkb3ZlIGBuYCByaWdoZSBzb25vIGVzdHJhdHRlIGNhc3VhbG1lbnRlLgoKLSB1c2EgaWwgZGF0YXNldCBgbXRjYXJzYAotIHVzYSBsYSBmdW56aW9uZSBgc2FtcGxlKClgCgojIyMgTGVhdmUtb25lLW91dAoKSWwgbGVhdmUtb25lLW91dCDDqCB1bmEgcHJvY2VkdXJhIHN0YXRpc3RpY2EgdXNhdGEgaW4gdmFyaSBjb250ZXN0aS4gQWQgZXNlbXBpbywgcGVyIGNhcGlyZSBzZSBjaSBzb25vIGRlbGxlIG9zc2VydmF6aW9uaSBpbmZsdWVudGkgaW4gdW4gY2VydG8gbW9kZWxsbyBzdGF0aXN0aWNvLCBzaSByaXBldGUgbCdhbmFsaXNpIHJpbXVvdmVuZG8gdW4nb3NzZXJ2YXppb25lIGFsbGEgdm9sdGEuIFNlIGFiYmlhbW8gMTAwIG9zc2VydmF6aW9uaSBvdHRlbmlhbW8gMTAwIGRhdGFzZXQgb2dudW5vIGZvcm1hdG8gZGEgOTkgb3NzZXJ2YXppb25pIHBlcmNow6ggdW5hIMOoIHN0YXRhIHJpbW9zc2EuCgoxLiBTY3JpdmV0ZSB1bmEgZnVuemlvbmUgYGxlYXZlMW91dChkYXRhKWAgY2hlIHJpY2V2ZSBpbiBpbnB1dCB1biBkYXRhc2V0IGUgcmVzdGl0dWlzY2UgdW5hIGxpc3RhIGRpIGRhdGFzZXQsIG9nbnVubyBkb3ZlIMOoIHN0YXRhIHRvbHRhIHVuJ29zc2VydmF6aW9uZS4gVXNhdGUgdW4gY2ljbG8gYGZvcmAuCgoyLiBTY3JpdmV0ZSB1bmEgdmVyc2lvbmUgcGnDuSBjb21wYXR0YSB1c2FuZG8gbCdgKmFwcGx5YCBmYW1pbHkuCgozLiBBbXBsaWF0ZSBsYSBmdW56aW9uZSBjcmVhdGEgYWwgcHVudG8gMSBpbiBtb2RvIGNoZSBsYSBmdW56aW9uYSB0b2xnYSBvZ25pIHZvbHRhICRuJCBvc3NlcnZhemlvbmkgY2FzdWFsaSwgZG92ZSAkbiQgw6ggdW4gcGFyYW1ldHJvIGRlbGxhIGZ1bnppb25lLiBBZCBlc2VtcGlvLCBsYSBmdW56aW9uZSBkZXZlIGNyZWFyZSAxMDAgZGF0YXNldCwgZG92ZSBvZ25pIHZvbHRhIHRvZ2xpZSAzIG9zc2VydmF6aW9uaS4KClBvdGV0ZSB1c2FyZSBpbCBkYXRhc2V0IGBpcmlzYAoKIyMjIFBvcG9sYXJlIHVuYSBtYXRyaWNlCgpTY3JpdmkgdW5hIGZ1bnppb25lIGBwb3B1bGF0ZV9tYXRyaXgobWF0cml4LCBkYXRhKWAgY2hlIHByZXNpIGluIGlucHV0IGkgZGVpIGRhdGkgZSB1bmEgbWF0cmljZSB2dW90YSwgcG9wb2xpIGxhIG1hdHJpY2UgdnVvdGEgdXNhbmRvIHVuIG5lc3RlZCBsb29wLgoKYGBge3J9CnggPC0gcm5vcm0oMTAwKQptYXQgPC0gbWF0cml4KG5yb3cgPSAxMCwgbmNvbCA9IDEwKQpgYGAKCiMgRXNlcmNpemkgYXZhbnphdGkgZGF0YWZyYW1lCgpJbiBxdWVzdGkgZXNlcmNpemkgdmVkcmVtbyBjb21lIGdlc3RpcmUgdW4gZGF0YXNldCBwacO5IG8gbWVubyBjb21wbGVzc28gaW4gbW9kbyBkYSBhcHBsaWNhcmUgbGUgbm96aW9uaSBjaGUgYWJiaWFtbyBpbXBhcmF0by4gR2xpIG9iaWV0dGl2aSBzb25vOgoKLSAqKmFwcGxpY2FyZSoqIGlsIHBpw7kgcG9zc2liaWxlIGkgKipjb25jZXR0aSBkZWwgY29yc28qKgotICoqY2VyY2FyZSBzb2x1emlvbmkgb25saW5lKiogcGVyIGxlIGNvc2Ugbm9uIGNoaWFyZQotIHNjcml2ZXJlIGZ1bnppb25pIGRvdmUgcG9zc2liaWxlCi0gcHJvZHVycmUgdW5vIHNjcmlwdCBkaSBwdWxpemlhIGRhdGkKCkluIHF1ZXN0byBjb3JzbyBub24gYWJiaWFtbyB2aXN0byBhc3BldHRpIGRpIHN0YXRpc3RpY2EgbyBtYW5pcG9sYXppb25lIGRhdGkgaW4gc2Vuc28gc3RyZXR0bywgdHV0dGF2aWEgaSBjb25jZXR0aSBjaGUgYWJiaWFtbyBpbXBhcmF0byBzb25vIGFwcGxpY2FiaWxpIGFuY2hlIHNlbnphIHF1ZXN0ZSBub3ppb25pLiBBZCBlc2VtcGlvIG5vbiBhYmJpYW1vIGFmZnJvbnRhdG8gbmVsIGRldHRhZ2xpbyBsYSBwdWxpemlhIGRlaSBkYXRpLCBpIHBhY2NoZXR0aSBwZXIgZ2VzdGlyZSBkYXRhc2V0IGNvbXBsZXNzaSBtYSBzYXBwaWFtbyBjaGUgYWQgZXNlbXBpbyB1biBgZGF0YWZyYW1lYCDDqCB1bmEgbGlzdGEsIGUgc2Ugdm9nbGlhbW8gYXBwbGljYXJlIHVuYSBmdW56aW9uZSBhZCB1bmEgbGlzdGEgcG9zc2lhbW8gdXNhcmUgbCdgKmFwcGx5YCBmYW1pbHkuCgojIyBRdWVzdGlvbmFyaQoKUHJvdmlhbW8gYSBmYXJlIGxvIHNjb3JpbmcgZGkgdW4gcXVlc3Rpb25hcmlvIHJhcHByZXNlbnRhdG8gaW4gUiBjb21lIGRhdGFmcmFtZS4gUXVlc3RvIMOoIHVuIGNhc28gbW9sdG8gY29tdW5lIGRvdmUgZG9iYmlhbW8gbGF2b3JhcmUgY29uIGluZGljaSBkaSByaWdhL2NvbG9ubmEuCgotIGltcG9ydGFyZSBpbCBkYXRhc2V0IGBpdGVtX2RhdGEucmRzYAotIGVzcGxvcmFyZSBpbCBkYXRhc2V0IChzdHJ1dHR1cmEsIG51bWVybyBkaSBjb2xvbm5lL3JpZ2hlLCB0aXBvIGRpIGNvbG9ubmUpCi0gdmVkZXJlIGkgdmFsb3JpIG1hc3NpbWkgZSBtaW5pbWkgZGVnbGkgaXRlbQogICAgKyBzZSBjaSBzb25vIHZhbG9yaSBhbm9tYWxpIChtaW5vcmkgZGkgMSBvIG1hZ2dpb3JpIGRpIDUpIG1ldHRpYW1vIGkgcmlzcGV0dGl2aSB2YWxvcmkgbWluaW1pIGUgbWFzc2ltaS4gQWQgZXNlbXBpbyBzZSBjJ8OoIDAgbWV0dGlhbW8gMSBtZW50cmUgMTAgbWV0dGlhbW8gNQotIHZlZGVyZSBzZSBjaSBzb25vIGBOQWAuIFNlIHVuIHNvZ2dldHRvIGhhIHBpw7kgZGkgMSBgTkFgLCBlbGltaW5pYW1vIGlsIHNvZ2dldHRvIGFsdHJpbWVudGkgbWV0dGlhbW8gaWwgdmFsb3JlIDMgYWwgcG9zdG8gZGVnbGkgYE5BYAoKUHJpbWEgZGkgZmFyZSBsbyBzY29yaW5nIGRvYmJpYW1vIGludmVydGlyZSAocmljb2RpZmljYXJlKSBhbGN1bmkgaXRlbS4gSW4gcGFydGljb2xhcmUgZG9iYmlhbW8gaW52ZXJ0aXJlIGdsaSBpdGVtIDEsIDEwLCAxMSwgMTMsIDE0LCAxNSwgMjAuIFBlciBpbnZlcnRpcmUgc2VtcGxpY2VtZW50ZSBpIHB1bnRlZ2dpIDEgZGl2ZW50YW5vIDUsIDIgLS0+IDQgZSBjb3PDrCB2aWEuCgpUaXBzOgoKLSBzY3JpdmVyZSB1bmEgZnVuemlvbmUgcGVyIHJpY29kaWZpY2FyZSAodmVkaSBgY2FzZV93aGVuKClgKSBlIHBvaSBhcHBsaWNhcmxhIGEgdHV0dGkgZ2xpIGl0ZW0KClF1ZXN0byBxdWVzdGlvbmFyaW8gw6ggZm9ybWF0byBkYSAzMCBpdGVtLiBDaSBzb25vIDUgc290dG9zY2FsZSBkb3ZlOgoKLSBzdWIxID0gaXRlbXMgMjAgMjUgMTcgMTggMjIKLSBzdWIyID0gaXRlbXMgMzAgIDMgMTEgMTYgMTkKLSBzdWIzID0gaXRlbXMgNCAxNCAgOCAyMSAyMwotIHN1YjQgPSBpdGVtcyAyNiAyOSAgOSAxMyAxMAotIHN1YjUgPSBpdGVtcyA1IDEyICA2IDI4IDI0Ci0gc3ViNiA9IGl0ZW1zIDcgMTUgMjcgIDIgIDEKClBlciBmYXJlIGxvIHNjb3JpbmcgZG9iYmlhbW86CgotIG1lZGlhcmUgZ2xpIGl0ZW0gcGVyIGxlIHNvdHRvc2NhbGUgYHN1YjFgLCBgc3ViMmAgZSBgc3ViM2AKLSBzb21tYXJlIGdsaSBpdGVtIHBlciBsZSBzb3R0b3NjYWxlIGBzdWI0YCwgYHN1YjVgIGUgYHN1YjZgCgpJbiBlbnRyYW1iaSBpIGNhc2kgY3JlaWFtbyBkZWxsZSBudW92ZSBjb2xvbm5lIGNoZSBzaSBjaGlhbWFubyBjb21lIGxlIHNvdHRvc2NhbGUgZGEgYXR0YWNjYXJlIGFsIGRhdGFmcmFtZSBwcmluY2lwYWxlLgoKIyMgTmV0ZmxpeAoKUXVlc3RvIGRhdGFzZXQgw6ggc3RhdG8gcHJlc28gZGFsIHNpdG8gW0thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zaGl2YW1iL25ldGZsaXgtc2hvd3MpKHVuIG90dGltYSByaXNvcnNhIHBlciB0cm92YXJlIGRhdGFzZXRzKS4gRScgdW4gZGF0YXNldCByZWxhdGl2YW1lbnRlIGdyYW5kZSBjb24gYHIgbnJvdyhuZXRmbGl4KWAgcmlnaGUgZSBgciBuY29sKG5ldGZsaXgpYCBjb2xvbm5lLiBJbCBkYXRhc2V0IGNvbnRpZW5lIGluZm9ybWF6aW9uaSBzdSBzZXJpZS10diBlIGZpbG0gcHJlc2VudGkgc3VsbGEgcGlhdHRhZm9ybWEsIGluIHBhcnRpY29sYXJlOgoKLSBgc2hvd19pZGA6IGluIGluZGljZSBudW1lcmljbyBwZXIgb2duaSBmaWxtL3NlcmllLXR2Ci0gYHR5cGVgOiBpZGVudGlmaWNhIHNlIGlsIHByb2RvdHRvIMOoIHVuYSBzZXJpZS10diBvIHVuIGZpbG0KLSBgdGl0bGVgOiBpbCB0aXRvbG8KLSBgY291bnRyeWA6IGlsIHBhZXNlIGRpIHByb2R1emlvbmUKLSBgZHVyYXRpb25gOiBsYSBkdXJhdGEKLSBgdXNlcnNfcmF0aW5nYDogbGUgdmFsdXRhemlvbmkgZGVnbGkgdXRlbnRpIG5ldGZsaXgKLSBgaW1kYl9yYXRpbmdgOiBsZSB2YWx1dGF6aW9uaSBkZWwgcG9ydGFsZSAqaW1kYioKLSBgdG90YWxfY29zdGA6IGlsIGNvc3RvIGRpIHByb2R1emlvbmUgaW4gbWlsaW9uaQotIGByZWxlYXNlX2RhdGVgOiBsYSBkYXRhIGRpIHJpbGFzY2lvIG9yaWdpbmFsZSBkZWxsYSBzZXJpZSBvIGRlbCBmaWxtCi0gYGluc2VydF9kYXRlYDogbGEgZGF0YSBkaSBpbnNlcmltZW50byBuZWwgY2F0YWxvZ28gbmV0ZmxpeAoKYGBge3J9CmhlYWQobmV0ZmxpeCkKYGBgCgojIyMgUGFzc2FnZ2kgZGEgZmFyZToKCi0gSW1wb3J0YXJlIGlsIGZpbGUgYG5ldGZsaXgudHh0YCBjYXBlbmRvIHF1YWxlIGZ1bnppb25lIHV0aWxpenphcmUsIGNvbWUgYXNzZWduYXJlIGxhIHByaW1hIHJpZ2EgY29tZSBub21pIGUgdXNhbmRvIGlsIHNlcGFyYXRvcmUgZ2l1c3RvCi0gU2NyaXZlcmUgdW5hIGZ1bnppb25lIGNoZSBwcmVuZGEgaW4gKippbnB1dCoqIGlsIGRhdGFzZXQgZSByZXN0aXR1aXNjYSBsYSB0aXBvbG9naWEgZGkgZGF0byBwcmVzZW50ZSBpbiBvZ25pIGNvbG9ubmEKLSBSYWdpb25hcmUgc3VsIHRpcG8gZGkgZGF0byBpbiBiYXNlIGFsbGEgZGVzY3JpemlvbmUgZWQgZXZlbnR1YWxtZW50ZSBjYW1iaWFyZSBsYSB0aXBvbG9naWEgZG92ZSBhcHByb3ByaWF0bwotIFNjcml2ZXJlIHVuYSBmdW56aW9uZSBjaGUgcHJlbmRhIGluIGlucHV0IGlsIGRhdGFzZXQgZSBmb3JuaXNjYSBwZXIgb2duaSBjb2xvbm5hIGlsIG51bWVybyBkaSB2YWxvcmkgYE5BYAotIE1vZGlmaWNhcmUgbGEgZnVuemlvbmUgcHJlY2VkZW50ZSBjb24gdW4gb3B6aW9uZSBjaGlhbWF0YSBgcGVyY2AgY2hlIHNlIGluc2VyaXRhIGNvbWUgYFRSVUVgIHJlc3RpdHVpc2NlIGxhIHBlcmNlbnR1YWxlIGRpIGBOQWAKLSBDcmVhcmUgdW5hIG51b3ZhIGNvbG9ubmEgY29uIGxhIGRpZmZlcmVuemEgdHJhIGByZWxlYXNlX2RhdGVgIGUgYGluc2VydF9kYXRlYC4gUGVyIHF1ZXN0byBwdcOyIGVzc2VyZSB1dGlsZSBjZXJjYXJlIG9ubGluZSBjb21lIFIgZ2VzdGlzY2UgbGUgZGF0ZS4KCiMjIyBQYXNzYWdnaSBhdmFuemF0aQoKU29saXRhbWVudGUgbGUgY29sb25uZSBkaSB1biBkYXRhc2V0IGNvbnRlbmdvbm8gaW5mb3JtYXppb25pIHJpbmRvbmRhbnRpIG8gbm9uIGFkZWd1YXRhbWVudGUgb3JnYW5penphdGUuIEFkIGVzZW1waW8gbGEgY29sb25uYSBgZHVyYXRpb25gIGhhIHVuYSBkdXBsaWNlIGluZm9ybWF6aW9uZTogbGEgZHVyYXRhIGluIG1pbnV0aSAocGVyIGkgZmlsbSkgZSBsYSBkdXJhdGEgaW4gc3RhZ2lvbmkgcGVyIGxlIHNlcmllLiBJbiB1bidhbHRyYSBjb2xvbm5hIGFiYmlhbW8gaW52ZWNlIGxhIGR1cmF0YSBkZWdsaSBlcGlzb2RpIGNoaWFyYW1lbnRlIG5vbiByaWxldmFudGUgcGVyIGkgZmlsbS4gSW4gcXVlc3RvIGNhc28gcG90cmViYmUgZXNzZXJlIHV0aWxlOgoKLSBjcmVhcmUgdW5hIGNvbG9ubmEgYGR1cmF0YWAgY2hlIGNvbnRpZW5lIGxhIGR1cmF0YSBpbiBtaW51dGkgcGVyIGkgZmlsbSBlIGxhIGR1cmF0YSBkZWxsJ2VwaXNvZGlvIHBlciBsZSBsZSBzZXJpZS10dgotIGNyZWFyZSB1bmEgY29sb25uYSBgZHVyYXRhX3N0YWdpb25pYCBjaGUgY29udGVuZ2EgaWwgbnVtZXJvIGRpIHN0YWdpb25pIHBlciBvZ25pIHNlcmllLXR2IG1lbnRyZSBpbCB2YWxvcmUgYDBgIHBlciBpIGZpbG0uCgojIyBBZHZhbmNlZCBDbGVhbmluZwoKTm9uc3RhbnRlIHBvc3Nhbm8gc2VtYnJhcmUgYXNwZXR0aSBzdXBlcmZpY2lhbGksIGxhIHB1bGl6aWEgZGVpIGRhdGFzZXQgw6ggZm9uZGFtZW50YWxlLiBOb21pIGRpIGNvbG9ubmUgYXBwcm9wcmlhdGksIHJpbXVvdmVyZSBjYXJhdHRlcmkgc3BlY2lhbGksIHZlcmlmaWNhcmUgbGEgc3RydXR0dXJhIGUgbGEgdGlwb2xvZ2lhIGRlbGxlIGNvbG9ubmUgZXRjLiBJbCBkYXRhZnJhbWUgYGluZXF1YWxpdHlfc3ViLmNzdmAgw6ggdW4gZXNlbXBpbyBkaSB1biBvdHRpbW8gZGF0YXNldCBkaSBwYXJ0ZW56YSBtYSBjb24gbGEgcG9zc2liaWxpdMOgIGRpIG1pZ2xpb3JhcmUgaSBub21pIGRlbGxlIGNvbG9ubmUgZSBhbGN1bmkgZGV0dGFnbGkgZ2VuZXJhbGkuCgpWZWRpIHVuIGVzZW1waW8gZGkgcHJlLXByb2Nlc3NpbmcgZGkgdW4gZGF0YXNldCBhIHF1ZXN0byBbbGlua10oaW5lcXVhbGl0eS5SKS4KCiMgQ3JlZGl0cwoKQWxjdW5pIGVzZXJjaXppIHNvbm8gc3RhdGkgcHJlc2kgbyBpbnNwaXJhdGkgZGE6CgotIGh0dHBzOi8vd3d3LnItZXhlcmNpc2VzLmNvbS8K