Descrizione delle Variabili del Dataset
User.ID: Un identificatore unico per ciascun
utente.
Subscription.Type: Il tipo di abbonamento sottoscritto
dall’utente, con categorie come “Basic”, “Premium” o “Standard”.
Monthly.Revenue: Il reddito mensile generato da ogni
utente per Netflix, probabilmente in dollari.
Join.Date: La data in cui l’utente si è iscritto a
Netflix.
Last.Payment.Date: L’ultima data di pagamento
registrata per l’utente.
Country: Il paese di residenza dell’utente.
Age: L’età dell’utente.
Gender: Il genere dell’utente.
Device: Il dispositivo utilizzato dall’utente per
accedere a Netflix.
Plan.Duration: La durata dell’abbonamento
sottoscritto.
summary(dati)
User.ID Subscription.Type Monthly.Revenue Join.Date Last.Payment.Date Country Age
Min. : 1.0 Length:2500 Min. :10.00 Length:2500 Length:2500 Length:2500 Min. :26.0
1st Qu.: 625.8 Class :character 1st Qu.:11.00 Class :character Class :character Class :character 1st Qu.:32.0
Median :1250.5 Mode :character Median :12.00 Mode :character Mode :character Mode :character Median :39.0
Mean :1250.5 Mean :12.51 Mean :38.8
3rd Qu.:1875.2 3rd Qu.:14.00 3rd Qu.:45.0
Max. :2500.0 Max. :15.00 Max. :51.0
Gender Device Plan.Duration
Length:2500 Length:2500 Length:2500
Class :character Class :character Class :character
Mode :character Mode :character Mode :character
Dal Summary vediamo che le variabili numeriche sono 3:
- User.ID: Ogni utente è presente una volta sola nel
dataset, avendo il Max Value uguale al numero di righe totali.
- Monthly.Revenue: Dà l’impressione di essere una
“tariffa fissa” (da 10 a 15), per cui potrebbe essere utile trattarla
come una variabile categorica, dato che ogni tariffa rappresenterà un
certo tipo di abbonamento.
- Age: È una normale variabile numerica che
rappresenta l’età degli utenti.
Di particolare interesse è notare che le variabili di data sono
attualmente di tipo Stringa.
summary(dati$Monthly.Revenue)
Min. 1st Qu. Median Mean 3rd Qu. Max.
10.00 11.00 12.00 12.51 14.00 15.00
var(dati$Monthly.Revenue)
[1] 2.845468
sd(dati$Monthly.Revenue)
[1] 1.686851
hist(dati$Monthly.Revenue,
main="Istogramma del Monthly Revenue con Curva di Densità",
xlab="Monthly Revenue",
ylab="Densità",
col="green",
border="black",
freq=FALSE)
densità_revenue <- density(dati$Monthly.Revenue, na.rm = TRUE)
lines(densità_revenue, col="red", lwd=2)

Come avevo fatto notare prima, il Monthly Revenue va da 10 a 15,
rendendo quindi poco utile qui un istogramma, decido di trasformarla
temporaneamente in una variabile categorica.
dati$Monthly.Revenue <- as.factor(dati$Monthly.Revenue)
# Calcolo delle frequenze
frequenze_monthly_revenue <- table(dati$Monthly.Revenue)
# Calcolo delle percentuali
percentuali_monthly_revenue <- prop.table(frequenze_monthly_revenue) * 100
# Visualizzazione delle frequenze e delle percentuali
freq_perc_monthly_revenue <- data.frame(Frequenza=frequenze_monthly_revenue, Percentuale=percentuali_monthly_revenue)
print(freq_perc_monthly_revenue)
# Creazione del barplot per la frequenza
barplot(freq_perc_monthly_revenue$Frequenza.Freq,
names.arg=freq_perc_monthly_revenue$Frequenza.Var1,
main="Distribuzione della Monthly Revenue",
xlab="Monthly Revenue",
ylab="Frequenza",
col="blue",
las=1)
text(x = barplot(freq_perc_monthly_revenue$Frequenza.Freq, plot = FALSE),
y = freq_perc_monthly_revenue$Frequenza.Freq + 1,
label = paste(round(freq_perc_monthly_revenue$Percentuale.Freq, 2), "%"),
pos = 1, cex=1,font=2)

Gli abbonamenti più comuni sono quelli a 12 euro, mentre i meno
comuni quelli a 11 euro. Avendo stabilito che sono tutti pagamenti
mensili è probabile che dipenda dal tipo di abbonamento e dal paese.
summary(dati$Age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
26.0 32.0 39.0 38.8 45.0 51.0
var(dati$Age)
[1] 51.43439
sd(dati$Age)
[1] 7.171778
hist(dati$Age,
main="Istogramma dell'Età con Curva di Densità",
xlab="Età",
ylab="Densità",
col="blue",
border="black",
freq=FALSE,
breaks=10)
densità <- density(dati$Age)
lines(densità, col="red", lwd=2)

Per quanto riguarda l’età, l’istogramma e la curva di densità
mostrano una rappresentazione abbastanza regolare dai 25 ai 50 anni, con
un calo dai 50 in su.
# Calcolo delle frequenze
frequenze_sub_type <- table(dati$Subscription.Type)
# Calcolo delle percentuali
percentuali_sub_type <- prop.table(frequenze_sub_type) * 100
# Visualizzazione delle frequenze e delle percentuali
freq_perc_sub_type <- data.frame(Frequenza=frequenze_sub_type, Percentuale=percentuali_sub_type)
print(freq_perc_sub_type)
barplot(freq_perc_sub_type$Frequenza.Freq,
names.arg=freq_perc_sub_type$Frequenza.Var1,
main="Distribuzione della Subscription Type",
xlab="Subscription Type",
ylab="Frequenza",
col="blue",
las=1)
text(x = barplot(freq_perc_sub_type$Frequenza.Freq, plot = FALSE),
y = freq_perc_sub_type$Frequenza.Freq + 1,
label = paste(round(freq_perc_sub_type$Percentuale.Freq, 2), "%"),
pos = 1, cex=1,font=2)

La categoria di abbonamento più diffusa è quella Basic con un 40%,
seguita da Standard e poco sotto Premium.
# Calcolo delle frequenze
frequenze_country <- table(dati$Country)
# Calcolo delle percentuali
percentuali_country <- prop.table(frequenze_country) * 100
# Visualizzazione delle frequenze e delle percentuali
freq_perc_country <- data.frame(Frequenza=frequenze_country, Percentuale=percentuali_country)
print(freq_perc_country)
barplot_heights <- barplot(freq_perc_country$Frequenza.Freq,
main="Distribuzione dei paesi",
xlab="Paesi",
ylab="Frequenza",
col="blue",
las=1,
xaxt='n')
axis(1, at=barplot_heights, labels=freq_perc_country$Frequenza.Var1, cex.axis=0.6, srt=45)
text(x = barplot_heights,
y = freq_perc_country$Frequenza.Freq + 1,
label = paste(round(freq_perc_country$Percentuale.Freq, 2), "%"),
pos = 1, cex=0.8,font=2)

I paesi con più abbonati sono a pari merito Spagna e Stati Uniti,
seguiti poi dopo dal Canada al terzo posto.
# Calcolo delle frequenze
frequenze_gender <- table(dati$Gender)
# Calcolo delle percentuali
percentuali_gender <- prop.table(frequenze_gender) * 100
# Visualizzazione delle frequenze e delle percentuali
freq_perc_gender <- data.frame(Frequenza=frequenze_gender, Percentuale=percentuali_gender)
print(freq_perc_gender)
barplot(freq_perc_gender$Frequenza.Freq,
names.arg=freq_perc_gender$Frequenza.Var1,
main="Distribuzione del genere",
xlab="Generi",
ylab="Frequenza",
col="blue",
las=1)
text(x = barplot(freq_perc_gender$Frequenza.Freq, plot = FALSE),
y = freq_perc_gender$Frequenza.Freq + 1,
label = paste(round(freq_perc_gender$Percentuale.Freq, 2), "%"),
pos = 1, cex=1,font=2)

Il genere degli abbonati è praticamente diviso equamente tra maschi e
femmine.
frequenze_device <- table(dati$Device)
percentuali_device <- prop.table(frequenze_device) * 100
freq_perc_device <- data.frame(Frequenza=frequenze_device, Percentuale=percentuali_device)
print(freq_perc_device)
barplot(freq_perc_device$Frequenza.Freq,
names.arg=freq_perc_device$Frequenza.Var1,
main="Distribuzione dei dispositivi",
xlab="Dispositivi",
ylab="Frequenza",
col="blue",
las=1)
text(x = barplot(freq_perc_device$Frequenza.Freq, plot = FALSE),
y = freq_perc_device$Frequenza.Freq + 1,
label = paste(round(freq_perc_device$Percentuale.Freq, 2), "%"),
pos = 1, cex=1,font=2)

Anche per quanto riguarda i dispositivi utilizzati per accedere a
Netflix le osservazioni sono egualmente distribuite.
# Calcolo delle frequenze
frequenze_plan_duration <- table(dati$Plan.Duration)
# Calcolo delle percentuali
percentuali_plan_duration <- prop.table(frequenze_plan_duration) * 100
# Visualizzazione delle frequenze e delle percentuali
freq_perc_plan_duration <- data.frame(Frequenza=frequenze_plan_duration, Percentuale=percentuali_plan_duration)
print(freq_perc_plan_duration)
boxplot(dati$Age ~ dati$Subscription.Type,
main="Boxplot di Age per Subscription Type",
xlab="Tipo di Abbonamento",
ylab="Età",
col="lightblue")

I boxplot non mostrano una grande differenza a livello di
distribuzione dell’età nelle 3 categorie di abbonamento.
boxplot(dati$Age ~ dati$Gender,
main="Boxplot di Age per Gender",
xlab="Genere",
ylab="Età",
col="lightgreen")

Anche per quanto riguarda il genere rapportato all’età non c’è troppa
differenza, si può dire che forse le femmine sono leggermente (di molto
poco) più anziane.
boxplot(dati$Age ~ dati$Device,
main="Boxplot di Age per Device",
xlab="Dispositivo",
ylab="Età",
col="lightcoral")

Per quanto riguarda il Device rapportato all’età, le differenze sono
molto sottili, si può notare che nella fascia sotto i 35 è più probabile
trovare utenti che usano Smart TV e Tablet.
boxplot(dati$Age ~ dati$Country,
main="Boxplot di Age per Paese",
xlab="Paese",
ylab="Età",
col="lightyellow",
cex.axis=0.8)

Italia e Brasile hanno il range più ampio di età di utenti presenti
prendendo quasi tutta la fascia di età disponibile, mentre ad esempio in
Germania le osservazioni sono più concentrate tra i 35 e i 45.
# Raggruppo le osservazioni per genere e subscription type e calcolo le percentuali
dati_count <- dati %>%
group_by(Gender, Subscription.Type) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(Gender) %>%
mutate(Total = sum(Count)) %>%
mutate(Percentage = Count / Total * 100)
ggplot(dati_count, aes(x = Gender, y = Count, fill = Subscription.Type)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = sprintf("%.1f%%", Percentage), y = Count),
position = position_dodge(width = 0.9), vjust = -0.5, size = 3.5) +
labs(title = "Distribuzione di Subscription Type per Gender",
x = "Gender",
y = "Conteggio",
fill = "Subscription Type") +
theme_minimal()

La distribuzione del tipo di abbonamento per genere non presenta
praticolari differenze, entrambi preferiscono l’abbonamento Basic, a
seguire lo Standard e poi il Premium.
# Raggruppo le osservazioni per genere e device e calcolo le percentuali
dati_count <- dati %>%
group_by(Gender, Device) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(Gender) %>%
mutate(Total = sum(Count)) %>%
mutate(Percentage = Count / Total * 100)
ggplot(dati_count, aes(x = Gender, y = Count, fill = Device)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = sprintf("%.1f%%", Percentage), y = Count),
position = position_dodge(width = 0.9), vjust = -0.5, size = 3.5) +
labs(title = "Distribuzione di Device per Gender",
x = "Gender",
y = "Conteggio",
fill = "Device") +
theme_minimal()

Si può osservare una leggera differenza a livello di Device per
Genere, i maschi usano più lo Smartphone, mentre le femmine più Laptop e
Tablet, tenendo lo Smartphone come ultima scelta.
# Preparazione dei dati con calcolo delle percentuali per colonna
dati_count <- dati %>%
group_by(Country, Gender) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(Country) %>%
mutate(Total = sum(Count)) %>%
mutate(Percentage = Count / Total * 100)
ggplot(dati_count, aes(x = Country, y = Count, fill = Gender)) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = sprintf("%.1f%%", Percentage)),
position = position_fill(vjust = 0.5),
size = 3.5) +
labs(title = "Distribuzione di Gender per Country",
x = "Country",
y = "Percentuale",
fill = "Gender") +
theme_minimal()

La distribuzione del genere all’interno di ogni paese è praticamente
sempre divisa al 50%.
# Preparazione dei dati con calcolo delle percentuali per colonna
dati_count <- dati %>%
group_by(Country, Device) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(Country) %>%
mutate(Total = sum(Count)) %>%
mutate(Percentage = Count / Total * 100)
ggplot(dati_count, aes(x = Country, y = Count, fill = Device)) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = sprintf("%.1f%%", Percentage)),
position = position_fill(vjust = 0.5),
size = 3.5) +
labs(title = "Distribuzione di Device per Country",
x = "Country",
y = "Percentuale",
fill = "Device") +
theme_minimal()

Da questo plot possiamo notare alcune differenze riguardo il Device
che ogni utente utilizza in base al paese. Alcune cose più evidenti
sono:
In Germania il dispositivo più comune è il Laptop, mentre meno
comune lo Smartphone.
In Canada e in Messico il dispositivo più utilizzato è il
Tablet.
Australia, Brasile e Regno Unito utilizzano in maniera principale
lo Smartphone.
Chi fa più utilizzo di Smart TV è la Spagna.
# Preparazione dei dati con calcolo delle percentuali per colonna
dati_count <- dati %>%
group_by(Country, Subscription.Type) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(Country) %>%
mutate(Total = sum(Count)) %>%
mutate(Percentage = Count / Total * 100)
ggplot(dati_count, aes(x = Country, y = Count, fill = Subscription.Type)) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = sprintf("%.1f%%", Percentage)),
position = position_fill(vjust = 0.5),
size = 3.5) +
labs(title = "Distribuzione di Subscription.Type per Country",
x = "Country",
y = "Percentuale",
fill = "Subscription Type") +
theme_minimal()

Analizzando come la Subscription Type è distribuita all’interno dei
paesi, saltano all’occhio alcuni insights:
In Brasile, in Germania e in Italia la quasi totalità degli
utenti hanno abbonamenti Basic (rispettivamente 80%, 81% e
96%).
In Francia l’80% degli utenti hanno abbonamento Premium, e
nessuno Standard.
In Messico e in Regno Unito il 98% degli abbonamenti sono
Standard.
Anche in Australia e in Spagna si preferiscono abbonamenti
Premium.
# Preparazione dei dati con calcolo delle percentuali per colonna
dati_count <- dati %>%
group_by(Subscription.Type, Monthly.Revenue) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(Subscription.Type) %>%
mutate(Total = sum(Count)) %>%
mutate(Percentage = Count / Total * 100)
ggplot(dati_count, aes(x = Subscription.Type, y = Count, fill = Monthly.Revenue)) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = sprintf("%.1f%%", Percentage)),
position = position_fill(vjust = 0.5),
size = 3.5) +
labs(title = "Distribuzione di Monthly Revenue per Subscription Type",
x = "Suscription Type",
y = "Percentuale",
fill = "Monthly.Revenue") +
theme_minimal()

# Trasformo Join.Date in formato Data
dati$Join.Date <- as.Date(dati$Join.Date, format = "%d-%m-%y")
# Raggruppo per mese
dati$Join.Month <- floor_date(dati$Join.Date, "month")
user_counts_monthly <- aggregate(x = list(Count = rep(1, nrow(dati))), by = list(Date = dati$Join.Month), FUN = sum)
ggplot(user_counts_monthly, aes(x = Date, y = Count)) +
geom_line() +
geom_point(size = 3, color = "blue") +
labs(title = "Numero di Utenti Che Si Uniscono nel Tempo (Mensile)", x = "Data", y = "Numero di Utenti") +
theme_minimal() +
theme(axis.text.x = element_text(size = rel(0.8))) +
scale_x_date(date_breaks = "1 month", date_labels = "%m-%y")

Dal Lineplot si può notare che la maggior parte degli abbonamenti
sono avvenuti dal mese di Giugno 2022 ad Ottobre 2022 (mese migliore in
generale), con alcuni alti e bassi in mezzo.
# Trasformo Last.Payment.Date in formato Data
dati$Last.Payment.Date <- as.Date(dati$Last.Payment.Date, format = "%d-%m-%y")
# Raggruppo per mese
dati$Last.Payment.Month <- floor_date(dati$Last.Payment.Date, "month")
payment_counts_monthly <- aggregate(x = list(Count = rep(1, nrow(dati))), by = list(Date = dati$Last.Payment.Month), FUN = sum)
ggplot(payment_counts_monthly, aes(x = Date, y = Count)) +
geom_line() +
geom_point(size = 3, color = "blue") +
labs(title = "Numero di Pagamenti Effettuati nel Tempo (Mensile)", x = "Data", y = "Numero di Pagamenti") +
theme_minimal() +
theme(axis.text.x = element_text(size = rel(0.8))) +
scale_x_date(date_breaks = "1 month", date_labels = "%m-%y")

Gli ultimi pagamenti sono tutti effettuati a giugno 2023 e luglio
2023, essendo probabilmente il Dataset aggiornato a queste date, si può
dedurre che abbiamo probabilmente solo utenti attivi.
# Riconverto il Monthly.Revenue in un tipo numerico così da poter vedere la correlazione con l'età
dati$Monthly.Revenue <- as.numeric(as.character(dati$Monthly.Revenue))
# Calcolo la matrice di correlazione
cor_matrix <- cor(dati[, c("Age", "Monthly.Revenue")])
# Correlation Matrix
corrplot(cor_matrix, method = "color", type = "upper", addCoef.col = "black",
tl.col = "black", tl.srt = 45,
cl.pos = "n")

Dalla Correlation Matrix è possibile osservare che non c’è alcuna
correlazione tra le variabili (valori vicino allo 0 indicano che non c’è
correlazione, vicino a 1 invece massima).
#verifico l'intervallo di confidenza per le due variabili numeriche
t.test(dati$Age)$conf.int
[1] 38.51434 39.07686
attr(,"conf.level")
[1] 0.95
t.test(dati$Monthly.Revenue)$conf.int
[1] 12.44224 12.57456
attr(,"conf.level")
[1] 0.95
Possiamo dire con il 95% di confidenza che la media dell’età degli
utenti di Netflix nel dataset è compresa tra 38,51 e 39,08 anni. Mentre
Il reddito mensile generato dagli stessi utenti è compreso tra 12,44 e
12,57 dollari, sempre col 95% di confidenza.
chisq.test(table(dati$Subscription.Type, dati$Country))
Pearson's Chi-squared test
data: table(dati$Subscription.Type, dati$Country)
X-squared = 1683.9, df = 18, p-value < 2.2e-16
Valutando il test del chi-quadro tra i tipi di abbonamento e i paesi
è evidente che il tipo di abbonamento è fortemente associato al paese
degli utenti. Un p-value bassissimo e un X-squared molto alto indicano
chiaramente che va rifiutata l’ipotesi nulla e ci confermano ciò che già
poteva osservarsi abbastanza nei grafici precedenti.
chisq.test(table(dati$Device, dati$Country))
Pearson's Chi-squared test
data: table(dati$Device, dati$Country)
X-squared = 32.423, df = 27, p-value = 0.2168
Per quanto riguarda Device e Country invece questi non sembrano avere
particolare rilevanza l’uno sull’altro.
chisq.test(table(dati$Subscription.Type, dati$Gender))
Pearson's Chi-squared test
data: table(dati$Subscription.Type, dati$Gender)
X-squared = 0.62823, df = 2, p-value = 0.7304
Genere e tipo di iscrizione dell’utente non sono in alcun modo
legati.
modello_lineare <- lm(Monthly.Revenue ~ Age, data = dati)
summary(modello_lineare)
Call:
lm(formula = Monthly.Revenue ~ Age, data = dati)
Residuals:
Min 1Q Median 3Q Max
-2.5671 -1.5024 -0.4477 1.4926 2.5523
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 12.701332 0.185624 68.425 <2e-16 ***
Age -0.004973 0.004705 -1.057 0.291
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.687 on 2498 degrees of freedom
Multiple R-squared: 0.000447, Adjusted R-squared: 4.69e-05
F-statistic: 1.117 on 1 and 2498 DF, p-value: 0.2906
Dai valori che ci restituisce questo modello lineare possiamo trarre
che l’età non influenza in modo importante il costo dell’abbonamento. Un
RSE abbastanza alto, un R2 molto basso e la combinazione di p-value
(>0,05) e F-statistic non danno risultati positivi. Prevale quindi
l’ipotesi nulla.
LS0tDQp0aXRsZTogIk5ldGZsaXggVXNlcmJhc2UgRGF0YXNldCAoUHJvYmFiaWxpdMOgIHN0YXRpc3RpY2hlIC0gUEU6IEV0aXZpdHkgNCkiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICBmaWdfd2lkdGg6IDEwDQotLS0NCg0KIVtdKGltYWdlcy9uZXRmbGl4LnBuZyl7d2lkdGg9IjUwNiJ9DQoNCmBgYHtyfQ0KIyBJbXBvcnRvIGxlIGxpYnJlcmllIGNoZSBtaSBzZXJ2b25vDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KYGBgDQoNCmBgYHtyfQ0KIyBJbXBvcnRvIGlsIGRhdGFzZXQNCmRhdGkgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcYXJlbmFcXERvd25sb2Fkc1xcTmV0ZmxpeCBVc2VyYmFzZS5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpkYXRpDQpgYGANCg0KYGBge3J9DQojIE1vc3RybyB1bmEgcGFub3JhbWljYSBnZW5lcmFsZSBkZWxsZSB2YXJpYWJpbGkNCnN0cihkYXRpKQ0KYGBgDQoNCiMgRGVzY3JpemlvbmUgZGVsbGUgVmFyaWFiaWxpIGRlbCBEYXRhc2V0DQoNCi0gICBgVXNlci5JRGA6IFVuIGlkZW50aWZpY2F0b3JlIHVuaWNvIHBlciBjaWFzY3VuIHV0ZW50ZS4NCi0gICBgU3Vic2NyaXB0aW9uLlR5cGVgOiBJbCB0aXBvIGRpIGFiYm9uYW1lbnRvIHNvdHRvc2NyaXR0byBkYWxsJ3V0ZW50ZSwgY29uIGNhdGVnb3JpZSBjb21lICJCYXNpYyIsICJQcmVtaXVtIiBvICJTdGFuZGFyZCIuDQotICAgYE1vbnRobHkuUmV2ZW51ZWA6IElsIHJlZGRpdG8gbWVuc2lsZSBnZW5lcmF0byBkYSBvZ25pIHV0ZW50ZSBwZXIgTmV0ZmxpeCwgcHJvYmFiaWxtZW50ZSBpbiBkb2xsYXJpLg0KLSAgIGBKb2luLkRhdGVgOiBMYSBkYXRhIGluIGN1aSBsJ3V0ZW50ZSBzaSDDqCBpc2NyaXR0byBhIE5ldGZsaXguDQotICAgYExhc3QuUGF5bWVudC5EYXRlYDogTCd1bHRpbWEgZGF0YSBkaSBwYWdhbWVudG8gcmVnaXN0cmF0YSBwZXIgbCd1dGVudGUuDQotICAgYENvdW50cnlgOiBJbCBwYWVzZSBkaSByZXNpZGVuemEgZGVsbCd1dGVudGUuDQotICAgYEFnZWA6IEwnZXTDoCBkZWxsJ3V0ZW50ZS4NCi0gICBgR2VuZGVyYDogSWwgZ2VuZXJlIGRlbGwndXRlbnRlLg0KLSAgIGBEZXZpY2VgOiBJbCBkaXNwb3NpdGl2byB1dGlsaXp6YXRvIGRhbGwndXRlbnRlIHBlciBhY2NlZGVyZSBhIE5ldGZsaXguDQotICAgYFBsYW4uRHVyYXRpb25gOiBMYSBkdXJhdGEgZGVsbCdhYmJvbmFtZW50byBzb3R0b3Njcml0dG8uDQoNCmBgYHtyfQ0Kc3VtbWFyeShkYXRpKQ0KYGBgDQoNCkRhbCBTdW1tYXJ5IHZlZGlhbW8gY2hlIGxlIHZhcmlhYmlsaSBudW1lcmljaGUgc29ubyAzOg0KDQotICAgXC0gYFVzZXIuSURgOiBPZ25pIHV0ZW50ZSDDqCBwcmVzZW50ZSB1bmEgdm9sdGEgc29sYSBuZWwgZGF0YXNldCwgYXZlbmRvIGlsIE1heCBWYWx1ZSB1Z3VhbGUgYWwgbnVtZXJvIGRpIHJpZ2hlIHRvdGFsaS4NCg0KLSAgIFwtIGBNb250aGx5LlJldmVudWVgOiBEw6AgbCdpbXByZXNzaW9uZSBkaSBlc3NlcmUgdW5hICJ0YXJpZmZhIGZpc3NhIiAoZGEgMTAgYSAxNSksIHBlciBjdWkgcG90cmViYmUgZXNzZXJlIHV0aWxlIHRyYXR0YXJsYSBjb21lIHVuYSB2YXJpYWJpbGUgY2F0ZWdvcmljYSwgZGF0byBjaGUgb2duaSB0YXJpZmZhIHJhcHByZXNlbnRlcsOgIHVuIGNlcnRvIHRpcG8gZGkgYWJib25hbWVudG8uDQoNCi0gICBcLSBgQWdlYDogw4ggdW5hIG5vcm1hbGUgdmFyaWFiaWxlIG51bWVyaWNhIGNoZSByYXBwcmVzZW50YSBsJ2V0w6AgZGVnbGkgdXRlbnRpLg0KDQpEaSBwYXJ0aWNvbGFyZSBpbnRlcmVzc2Ugw6ggbm90YXJlIGNoZSBsZSB2YXJpYWJpbGkgZGkgZGF0YSBzb25vIGF0dHVhbG1lbnRlIGRpIHRpcG8gU3RyaW5nYS4NCg0KYGBge3J9DQpzdW1tYXJ5KGRhdGkkTW9udGhseS5SZXZlbnVlKQ0KYGBgDQoNCmBgYHtyfQ0KdmFyKGRhdGkkTW9udGhseS5SZXZlbnVlKQ0Kc2QoZGF0aSRNb250aGx5LlJldmVudWUpDQpgYGANCg0KYGBge3J9DQpoaXN0KGRhdGkkTW9udGhseS5SZXZlbnVlLCANCiAgICAgbWFpbj0iSXN0b2dyYW1tYSBkZWwgTW9udGhseSBSZXZlbnVlIGNvbiBDdXJ2YSBkaSBEZW5zaXTDoCIsIA0KICAgICB4bGFiPSJNb250aGx5IFJldmVudWUiLCANCiAgICAgeWxhYj0iRGVuc2l0w6AiLA0KICAgICBjb2w9ImdyZWVuIiwgDQogICAgIGJvcmRlcj0iYmxhY2siLA0KICAgICBmcmVxPUZBTFNFKSANCg0KZGVuc2l0w6BfcmV2ZW51ZSA8LSBkZW5zaXR5KGRhdGkkTW9udGhseS5SZXZlbnVlLCBuYS5ybSA9IFRSVUUpDQpsaW5lcyhkZW5zaXTDoF9yZXZlbnVlLCBjb2w9InJlZCIsIGx3ZD0yKSANCmBgYA0KDQpDb21lIGF2ZXZvIGZhdHRvIG5vdGFyZSBwcmltYSwgaWwgTW9udGhseSBSZXZlbnVlIHZhIGRhIDEwIGEgMTUsIHJlbmRlbmRvIHF1aW5kaSBwb2NvIHV0aWxlIHF1aSB1biBpc3RvZ3JhbW1hLCBkZWNpZG8gZGkgdHJhc2Zvcm1hcmxhIHRlbXBvcmFuZWFtZW50ZSBpbiB1bmEgdmFyaWFiaWxlIGNhdGVnb3JpY2EuDQoNCmBgYHtyfQ0KZGF0aSRNb250aGx5LlJldmVudWUgPC0gYXMuZmFjdG9yKGRhdGkkTW9udGhseS5SZXZlbnVlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjb2xvIGRlbGxlIGZyZXF1ZW56ZQ0KZnJlcXVlbnplX21vbnRobHlfcmV2ZW51ZSA8LSB0YWJsZShkYXRpJE1vbnRobHkuUmV2ZW51ZSkNCg0KIyBDYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpDQpwZXJjZW50dWFsaV9tb250aGx5X3JldmVudWUgPC0gcHJvcC50YWJsZShmcmVxdWVuemVfbW9udGhseV9yZXZlbnVlKSAqIDEwMA0KDQojIFZpc3VhbGl6emF6aW9uZSBkZWxsZSBmcmVxdWVuemUgZSBkZWxsZSBwZXJjZW50dWFsaQ0KZnJlcV9wZXJjX21vbnRobHlfcmV2ZW51ZSA8LSBkYXRhLmZyYW1lKEZyZXF1ZW56YT1mcmVxdWVuemVfbW9udGhseV9yZXZlbnVlLCBQZXJjZW50dWFsZT1wZXJjZW50dWFsaV9tb250aGx5X3JldmVudWUpDQpwcmludChmcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhemlvbmUgZGVsIGJhcnBsb3QgcGVyIGxhIGZyZXF1ZW56YQ0KYmFycGxvdChmcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlJEZyZXF1ZW56YS5GcmVxLA0KICAgICAgICBuYW1lcy5hcmc9ZnJlcV9wZXJjX21vbnRobHlfcmV2ZW51ZSRGcmVxdWVuemEuVmFyMSwNCiAgICAgICAgbWFpbj0iRGlzdHJpYnV6aW9uZSBkZWxsYSBNb250aGx5IFJldmVudWUiLA0KICAgICAgICB4bGFiPSJNb250aGx5IFJldmVudWUiLA0KICAgICAgICB5bGFiPSJGcmVxdWVuemEiLA0KICAgICAgICBjb2w9ImJsdWUiLA0KICAgICAgICBsYXM9MSkgDQoNCnRleHQoeCA9IGJhcnBsb3QoZnJlcV9wZXJjX21vbnRobHlfcmV2ZW51ZSRGcmVxdWVuemEuRnJlcSwgcGxvdCA9IEZBTFNFKSwNCiAgICAgDQogICAgIHkgPSBmcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlJEZyZXF1ZW56YS5GcmVxICsgMSwgDQogICAgIGxhYmVsID0gcGFzdGUocm91bmQoZnJlcV9wZXJjX21vbnRobHlfcmV2ZW51ZSRQZXJjZW50dWFsZS5GcmVxLCAyKSwgIiUiKSwgDQogICAgIHBvcyA9IDEsIGNleD0xLGZvbnQ9MikgDQpgYGANCg0KR2xpIGFiYm9uYW1lbnRpIHBpw7kgY29tdW5pIHNvbm8gcXVlbGxpIGEgMTIgZXVybywgbWVudHJlIGkgbWVubyBjb211bmkgcXVlbGxpIGEgMTEgZXVyby4gQXZlbmRvIHN0YWJpbGl0byBjaGUgc29ubyB0dXR0aSBwYWdhbWVudGkgbWVuc2lsaSDDqCBwcm9iYWJpbGUgY2hlIGRpcGVuZGEgZGFsIHRpcG8gZGkgYWJib25hbWVudG8gZSBkYWwgcGFlc2UuDQoNCmBgYHtyfQ0Kc3VtbWFyeShkYXRpJEFnZSkNCmBgYA0KDQpgYGB7cn0NCnZhcihkYXRpJEFnZSkNCnNkKGRhdGkkQWdlKQ0KYGBgDQoNCmBgYHtyfQ0KaGlzdChkYXRpJEFnZSwgDQogICAgIG1haW49IklzdG9ncmFtbWEgZGVsbCdFdMOgIGNvbiBDdXJ2YSBkaSBEZW5zaXTDoCIsIA0KICAgICB4bGFiPSJFdMOgIiwgDQogICAgIHlsYWI9IkRlbnNpdMOgIiwNCiAgICAgY29sPSJibHVlIiwgDQogICAgIGJvcmRlcj0iYmxhY2siLA0KICAgICBmcmVxPUZBTFNFLA0KICAgICBicmVha3M9MTApDQoNCmRlbnNpdMOgIDwtIGRlbnNpdHkoZGF0aSRBZ2UpIA0KbGluZXMoZGVuc2l0w6AsIGNvbD0icmVkIiwgbHdkPTIpIA0KDQpgYGANCg0KUGVyIHF1YW50byByaWd1YXJkYSBsJ2V0w6AsIGwnaXN0b2dyYW1tYSBlIGxhIGN1cnZhIGRpIGRlbnNpdMOgIG1vc3RyYW5vIHVuYSByYXBwcmVzZW50YXppb25lIGFiYmFzdGFuemEgcmVnb2xhcmUgZGFpIDI1IGFpIDUwIGFubmksIGNvbiB1biBjYWxvIGRhaSA1MCBpbiBzdS4NCg0KYGBge3J9DQojIENhbGNvbG8gZGVsbGUgZnJlcXVlbnplDQpmcmVxdWVuemVfc3ViX3R5cGUgPC0gdGFibGUoZGF0aSRTdWJzY3JpcHRpb24uVHlwZSkNCg0KIyBDYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpDQpwZXJjZW50dWFsaV9zdWJfdHlwZSA8LSBwcm9wLnRhYmxlKGZyZXF1ZW56ZV9zdWJfdHlwZSkgKiAxMDANCg0KIyBWaXN1YWxpenphemlvbmUgZGVsbGUgZnJlcXVlbnplIGUgZGVsbGUgcGVyY2VudHVhbGkNCmZyZXFfcGVyY19zdWJfdHlwZSA8LSBkYXRhLmZyYW1lKEZyZXF1ZW56YT1mcmVxdWVuemVfc3ViX3R5cGUsIFBlcmNlbnR1YWxlPXBlcmNlbnR1YWxpX3N1Yl90eXBlKQ0KcHJpbnQoZnJlcV9wZXJjX3N1Yl90eXBlKQ0KYGBgDQoNCmBgYHtyfQ0KYmFycGxvdChmcmVxX3BlcmNfc3ViX3R5cGUkRnJlcXVlbnphLkZyZXEsDQogICAgICAgIG5hbWVzLmFyZz1mcmVxX3BlcmNfc3ViX3R5cGUkRnJlcXVlbnphLlZhcjEsDQogICAgICAgIG1haW49IkRpc3RyaWJ1emlvbmUgZGVsbGEgU3Vic2NyaXB0aW9uIFR5cGUiLA0KICAgICAgICB4bGFiPSJTdWJzY3JpcHRpb24gVHlwZSIsDQogICAgICAgIHlsYWI9IkZyZXF1ZW56YSIsDQogICAgICAgIGNvbD0iYmx1ZSIsDQogICAgICAgIGxhcz0xKQ0KDQp0ZXh0KHggPSBiYXJwbG90KGZyZXFfcGVyY19zdWJfdHlwZSRGcmVxdWVuemEuRnJlcSwgcGxvdCA9IEZBTFNFKSwgDQogICAgIHkgPSBmcmVxX3BlcmNfc3ViX3R5cGUkRnJlcXVlbnphLkZyZXEgKyAxLCANCiAgICAgbGFiZWwgPSBwYXN0ZShyb3VuZChmcmVxX3BlcmNfc3ViX3R5cGUkUGVyY2VudHVhbGUuRnJlcSwgMiksICIlIiksIA0KICAgICBwb3MgPSAxLCBjZXg9MSxmb250PTIpDQpgYGANCg0KTGEgY2F0ZWdvcmlhIGRpIGFiYm9uYW1lbnRvIHBpw7kgZGlmZnVzYSDDqCBxdWVsbGEgQmFzaWMgY29uIHVuIDQwJSwgc2VndWl0YSBkYSBTdGFuZGFyZCBlIHBvY28gc290dG8gUHJlbWl1bS4NCg0KYGBge3J9DQojIENhbGNvbG8gZGVsbGUgZnJlcXVlbnplDQpmcmVxdWVuemVfY291bnRyeSA8LSB0YWJsZShkYXRpJENvdW50cnkpDQoNCiMgQ2FsY29sbyBkZWxsZSBwZXJjZW50dWFsaQ0KcGVyY2VudHVhbGlfY291bnRyeSA8LSBwcm9wLnRhYmxlKGZyZXF1ZW56ZV9jb3VudHJ5KSAqIDEwMA0KDQojIFZpc3VhbGl6emF6aW9uZSBkZWxsZSBmcmVxdWVuemUgZSBkZWxsZSBwZXJjZW50dWFsaQ0KZnJlcV9wZXJjX2NvdW50cnkgPC0gZGF0YS5mcmFtZShGcmVxdWVuemE9ZnJlcXVlbnplX2NvdW50cnksIFBlcmNlbnR1YWxlPXBlcmNlbnR1YWxpX2NvdW50cnkpDQpwcmludChmcmVxX3BlcmNfY291bnRyeSkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQpiYXJwbG90X2hlaWdodHMgPC0gYmFycGxvdChmcmVxX3BlcmNfY291bnRyeSRGcmVxdWVuemEuRnJlcSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW49IkRpc3RyaWJ1emlvbmUgZGVpIHBhZXNpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWI9IlBhZXNpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsYWI9IkZyZXF1ZW56YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9ImJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzPTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB4YXh0PSduJykNCg0KYXhpcygxLCBhdD1iYXJwbG90X2hlaWdodHMsIGxhYmVscz1mcmVxX3BlcmNfY291bnRyeSRGcmVxdWVuemEuVmFyMSwgY2V4LmF4aXM9MC42LCBzcnQ9NDUpDQoNCnRleHQoeCA9IGJhcnBsb3RfaGVpZ2h0cywgDQogICAgIHkgPSBmcmVxX3BlcmNfY291bnRyeSRGcmVxdWVuemEuRnJlcSArIDEsIA0KICAgICBsYWJlbCA9IHBhc3RlKHJvdW5kKGZyZXFfcGVyY19jb3VudHJ5JFBlcmNlbnR1YWxlLkZyZXEsIDIpLCAiJSIpLCANCiAgICAgcG9zID0gMSwgY2V4PTAuOCxmb250PTIpIA0KYGBgDQoNCkkgcGFlc2kgY29uIHBpw7kgYWJib25hdGkgc29ubyBhIHBhcmkgbWVyaXRvIFNwYWduYSBlIFN0YXRpIFVuaXRpLCBzZWd1aXRpIHBvaSBkb3BvIGRhbCBDYW5hZGEgYWwgdGVyem8gcG9zdG8uDQoNCmBgYHtyfQ0KIyBDYWxjb2xvIGRlbGxlIGZyZXF1ZW56ZQ0KZnJlcXVlbnplX2dlbmRlciA8LSB0YWJsZShkYXRpJEdlbmRlcikNCg0KIyBDYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpDQpwZXJjZW50dWFsaV9nZW5kZXIgPC0gcHJvcC50YWJsZShmcmVxdWVuemVfZ2VuZGVyKSAqIDEwMA0KDQojIFZpc3VhbGl6emF6aW9uZSBkZWxsZSBmcmVxdWVuemUgZSBkZWxsZSBwZXJjZW50dWFsaQ0KZnJlcV9wZXJjX2dlbmRlciA8LSBkYXRhLmZyYW1lKEZyZXF1ZW56YT1mcmVxdWVuemVfZ2VuZGVyLCBQZXJjZW50dWFsZT1wZXJjZW50dWFsaV9nZW5kZXIpDQpwcmludChmcmVxX3BlcmNfZ2VuZGVyKQ0KYGBgDQoNCmBgYHtyfQ0KYmFycGxvdChmcmVxX3BlcmNfZ2VuZGVyJEZyZXF1ZW56YS5GcmVxLA0KICAgICAgICBuYW1lcy5hcmc9ZnJlcV9wZXJjX2dlbmRlciRGcmVxdWVuemEuVmFyMSwNCiAgICAgICAgbWFpbj0iRGlzdHJpYnV6aW9uZSBkZWwgZ2VuZXJlIiwNCiAgICAgICAgeGxhYj0iR2VuZXJpIiwNCiAgICAgICAgeWxhYj0iRnJlcXVlbnphIiwNCiAgICAgICAgY29sPSJibHVlIiwNCiAgICAgICAgbGFzPTEpDQoNCnRleHQoeCA9IGJhcnBsb3QoZnJlcV9wZXJjX2dlbmRlciRGcmVxdWVuemEuRnJlcSwgcGxvdCA9IEZBTFNFKSwgDQogICAgIHkgPSBmcmVxX3BlcmNfZ2VuZGVyJEZyZXF1ZW56YS5GcmVxICsgMSwNCiAgICAgbGFiZWwgPSBwYXN0ZShyb3VuZChmcmVxX3BlcmNfZ2VuZGVyJFBlcmNlbnR1YWxlLkZyZXEsIDIpLCAiJSIpLCANCiAgICAgcG9zID0gMSwgY2V4PTEsZm9udD0yKSANCmBgYA0KDQpJbCBnZW5lcmUgZGVnbGkgYWJib25hdGkgw6ggcHJhdGljYW1lbnRlIGRpdmlzbyBlcXVhbWVudGUgdHJhIG1hc2NoaSBlIGZlbW1pbmUuDQoNCmBgYHtyfQ0KZnJlcXVlbnplX2RldmljZSA8LSB0YWJsZShkYXRpJERldmljZSkNCg0KcGVyY2VudHVhbGlfZGV2aWNlIDwtIHByb3AudGFibGUoZnJlcXVlbnplX2RldmljZSkgKiAxMDANCg0KZnJlcV9wZXJjX2RldmljZSA8LSBkYXRhLmZyYW1lKEZyZXF1ZW56YT1mcmVxdWVuemVfZGV2aWNlLCBQZXJjZW50dWFsZT1wZXJjZW50dWFsaV9kZXZpY2UpDQpwcmludChmcmVxX3BlcmNfZGV2aWNlKQ0KYGBgDQoNCmBgYHtyfQ0KYmFycGxvdChmcmVxX3BlcmNfZGV2aWNlJEZyZXF1ZW56YS5GcmVxLA0KICAgICAgICBuYW1lcy5hcmc9ZnJlcV9wZXJjX2RldmljZSRGcmVxdWVuemEuVmFyMSwNCiAgICAgICAgbWFpbj0iRGlzdHJpYnV6aW9uZSBkZWkgZGlzcG9zaXRpdmkiLA0KICAgICAgICB4bGFiPSJEaXNwb3NpdGl2aSIsDQogICAgICAgIHlsYWI9IkZyZXF1ZW56YSIsDQogICAgICAgIGNvbD0iYmx1ZSIsDQogICAgICAgIGxhcz0xKQ0KDQp0ZXh0KHggPSBiYXJwbG90KGZyZXFfcGVyY19kZXZpY2UkRnJlcXVlbnphLkZyZXEsIHBsb3QgPSBGQUxTRSksIA0KICAgICB5ID0gZnJlcV9wZXJjX2RldmljZSRGcmVxdWVuemEuRnJlcSArIDEsDQogICAgIGxhYmVsID0gcGFzdGUocm91bmQoZnJlcV9wZXJjX2RldmljZSRQZXJjZW50dWFsZS5GcmVxLCAyKSwgIiUiKSwgDQogICAgIHBvcyA9IDEsIGNleD0xLGZvbnQ9MikgDQpgYGANCg0KQW5jaGUgcGVyIHF1YW50byByaWd1YXJkYSBpIGRpc3Bvc2l0aXZpIHV0aWxpenphdGkgcGVyIGFjY2VkZXJlIGEgTmV0ZmxpeCBsZSBvc3NlcnZhemlvbmkgc29ubyBlZ3VhbG1lbnRlIGRpc3RyaWJ1aXRlLg0KDQpgYGB7cn0NCiMgQ2FsY29sbyBkZWxsZSBmcmVxdWVuemUNCmZyZXF1ZW56ZV9wbGFuX2R1cmF0aW9uIDwtIHRhYmxlKGRhdGkkUGxhbi5EdXJhdGlvbikNCg0KIyBDYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpDQpwZXJjZW50dWFsaV9wbGFuX2R1cmF0aW9uIDwtIHByb3AudGFibGUoZnJlcXVlbnplX3BsYW5fZHVyYXRpb24pICogMTAwDQoNCiMgVmlzdWFsaXp6YXppb25lIGRlbGxlIGZyZXF1ZW56ZSBlIGRlbGxlIHBlcmNlbnR1YWxpDQpmcmVxX3BlcmNfcGxhbl9kdXJhdGlvbiA8LSBkYXRhLmZyYW1lKEZyZXF1ZW56YT1mcmVxdWVuemVfcGxhbl9kdXJhdGlvbiwgUGVyY2VudHVhbGU9cGVyY2VudHVhbGlfcGxhbl9kdXJhdGlvbikNCnByaW50KGZyZXFfcGVyY19wbGFuX2R1cmF0aW9uKQ0KYGBgDQoNCmBgYHtyfQ0KYm94cGxvdChkYXRpJEFnZSB+IGRhdGkkU3Vic2NyaXB0aW9uLlR5cGUsDQogICAgICAgIG1haW49IkJveHBsb3QgZGkgQWdlIHBlciBTdWJzY3JpcHRpb24gVHlwZSIsDQogICAgICAgIHhsYWI9IlRpcG8gZGkgQWJib25hbWVudG8iLA0KICAgICAgICB5bGFiPSJFdMOgIiwNCiAgICAgICAgY29sPSJsaWdodGJsdWUiKQ0KDQpgYGANCg0KSSBib3hwbG90IG5vbiBtb3N0cmFubyB1bmEgZ3JhbmRlIGRpZmZlcmVuemEgYSBsaXZlbGxvIGRpIGRpc3RyaWJ1emlvbmUgZGVsbCdldMOgIG5lbGxlIDMgY2F0ZWdvcmllIGRpIGFiYm9uYW1lbnRvLg0KDQpgYGB7cn0NCmJveHBsb3QoZGF0aSRBZ2UgfiBkYXRpJEdlbmRlciwNCiAgICAgICAgbWFpbj0iQm94cGxvdCBkaSBBZ2UgcGVyIEdlbmRlciIsDQogICAgICAgIHhsYWI9IkdlbmVyZSIsDQogICAgICAgIHlsYWI9IkV0w6AiLA0KICAgICAgICBjb2w9ImxpZ2h0Z3JlZW4iKQ0KYGBgDQoNCkFuY2hlIHBlciBxdWFudG8gcmlndWFyZGEgaWwgZ2VuZXJlIHJhcHBvcnRhdG8gYWxsJ2V0w6Agbm9uIGMnw6ggdHJvcHBhIGRpZmZlcmVuemEsIHNpIHB1w7IgZGlyZSBjaGUgZm9yc2UgbGUgZmVtbWluZSBzb25vIGxlZ2dlcm1lbnRlIChkaSBtb2x0byBwb2NvKSBwacO5IGFuemlhbmUuDQoNCmBgYHtyfQ0KYm94cGxvdChkYXRpJEFnZSB+IGRhdGkkRGV2aWNlLA0KICAgICAgICBtYWluPSJCb3hwbG90IGRpIEFnZSBwZXIgRGV2aWNlIiwNCiAgICAgICAgeGxhYj0iRGlzcG9zaXRpdm8iLA0KICAgICAgICB5bGFiPSJFdMOgIiwNCiAgICAgICAgY29sPSJsaWdodGNvcmFsIikNCmBgYA0KDQpQZXIgcXVhbnRvIHJpZ3VhcmRhIGlsIERldmljZSByYXBwb3J0YXRvIGFsbCdldMOgLCBsZSBkaWZmZXJlbnplIHNvbm8gbW9sdG8gc290dGlsaSwgc2kgcHXDsiBub3RhcmUgY2hlIG5lbGxhIGZhc2NpYSBzb3R0byBpIDM1IMOoIHBpw7kgcHJvYmFiaWxlIHRyb3ZhcmUgdXRlbnRpIGNoZSB1c2FubyBTbWFydCBUViBlIFRhYmxldC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KYm94cGxvdChkYXRpJEFnZSB+IGRhdGkkQ291bnRyeSwNCiAgICAgICAgbWFpbj0iQm94cGxvdCBkaSBBZ2UgcGVyIFBhZXNlIiwNCiAgICAgICAgeGxhYj0iUGFlc2UiLA0KICAgICAgICB5bGFiPSJFdMOgIiwNCiAgICAgICAgY29sPSJsaWdodHllbGxvdyIsDQogICAgICAgIGNleC5heGlzPTAuOCkNCmBgYA0KDQpJdGFsaWEgZSBCcmFzaWxlIGhhbm5vIGlsIHJhbmdlIHBpw7kgYW1waW8gZGkgZXTDoCBkaSB1dGVudGkgcHJlc2VudGkgcHJlbmRlbmRvIHF1YXNpIHR1dHRhIGxhIGZhc2NpYSBkaSBldMOgIGRpc3BvbmliaWxlLCBtZW50cmUgYWQgZXNlbXBpbyBpbiBHZXJtYW5pYSBsZSBvc3NlcnZhemlvbmkgc29ubyBwacO5IGNvbmNlbnRyYXRlIHRyYSBpIDM1IGUgaSA0NS4NCg0KYGBge3J9DQoNCiMgUmFnZ3J1cHBvIGxlIG9zc2VydmF6aW9uaSBwZXIgZ2VuZXJlIGUgc3Vic2NyaXB0aW9uIHR5cGUgZSBjYWxjb2xvIGxlIHBlcmNlbnR1YWxpDQpkYXRpX2NvdW50IDwtIGRhdGkgJT4lDQogIGdyb3VwX2J5KEdlbmRlciwgU3Vic2NyaXB0aW9uLlR5cGUpICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQ0KICBncm91cF9ieShHZW5kZXIpICU+JQ0KICBtdXRhdGUoVG90YWwgPSBzdW0oQ291bnQpKSAlPiUNCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBDb3VudCAvIFRvdGFsICogMTAwKQ0KDQpnZ3Bsb3QoZGF0aV9jb3VudCwgYWVzKHggPSBHZW5kZXIsIHkgPSBDb3VudCwgZmlsbCA9IFN1YnNjcmlwdGlvbi5UeXBlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjFmJSUiLCBQZXJjZW50YWdlKSwgeSA9IENvdW50KSwgDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgdmp1c3QgPSAtMC41LCBzaXplID0gMy41KSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV6aW9uZSBkaSBTdWJzY3JpcHRpb24gVHlwZSBwZXIgR2VuZGVyIiwNCiAgICAgICB4ID0gIkdlbmRlciIsDQogICAgICAgeSA9ICJDb250ZWdnaW8iLA0KICAgICAgIGZpbGwgPSAiU3Vic2NyaXB0aW9uIFR5cGUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkxhIGRpc3RyaWJ1emlvbmUgZGVsIHRpcG8gZGkgYWJib25hbWVudG8gcGVyIGdlbmVyZSBub24gcHJlc2VudGEgcHJhdGljb2xhcmkgZGlmZmVyZW56ZSwgZW50cmFtYmkgcHJlZmVyaXNjb25vIGwnYWJib25hbWVudG8gQmFzaWMsIGEgc2VndWlyZSBsbyBTdGFuZGFyZCBlIHBvaSBpbCBQcmVtaXVtLg0KDQpgYGB7cn0NCiMgUmFnZ3J1cHBvIGxlIG9zc2VydmF6aW9uaSBwZXIgZ2VuZXJlIGUgZGV2aWNlIGUgY2FsY29sbyBsZSBwZXJjZW50dWFsaQ0KZGF0aV9jb3VudCA8LSBkYXRpICU+JQ0KICBncm91cF9ieShHZW5kZXIsIERldmljZSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGdyb3VwX2J5KEdlbmRlcikgJT4lDQogIG11dGF0ZShUb3RhbCA9IHN1bShDb3VudCkpICU+JQ0KICBtdXRhdGUoUGVyY2VudGFnZSA9IENvdW50IC8gVG90YWwgKiAxMDApDQoNCmdncGxvdChkYXRpX2NvdW50LCBhZXMoeCA9IEdlbmRlciwgeSA9IENvdW50LCBmaWxsID0gRGV2aWNlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjFmJSUiLCBQZXJjZW50YWdlKSwgeSA9IENvdW50KSwgDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgdmp1c3QgPSAtMC41LCBzaXplID0gMy41KSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV6aW9uZSBkaSBEZXZpY2UgcGVyIEdlbmRlciIsDQogICAgICAgeCA9ICJHZW5kZXIiLA0KICAgICAgIHkgPSAiQ29udGVnZ2lvIiwNCiAgICAgICBmaWxsID0gIkRldmljZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KU2kgcHXDsiBvc3NlcnZhcmUgdW5hIGxlZ2dlcmEgZGlmZmVyZW56YSBhIGxpdmVsbG8gZGkgRGV2aWNlIHBlciBHZW5lcmUsIGkgbWFzY2hpIHVzYW5vIHBpw7kgbG8gU21hcnRwaG9uZSwgbWVudHJlIGxlIGZlbW1pbmUgcGnDuSBMYXB0b3AgZSBUYWJsZXQsIHRlbmVuZG8gbG8gU21hcnRwaG9uZSBjb21lIHVsdGltYSBzY2VsdGEuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCiMgUHJlcGFyYXppb25lIGRlaSBkYXRpIGNvbiBjYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpIHBlciBjb2xvbm5hDQpkYXRpX2NvdW50IDwtIGRhdGkgJT4lDQogIGdyb3VwX2J5KENvdW50cnksIEdlbmRlcikgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGdyb3VwX2J5KENvdW50cnkpICU+JQ0KICBtdXRhdGUoVG90YWwgPSBzdW0oQ291bnQpKSAlPiUNCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBDb3VudCAvIFRvdGFsICogMTAwKQ0KDQpnZ3Bsb3QoZGF0aV9jb3VudCwgYWVzKHggPSBDb3VudHJ5LCB5ID0gQ291bnQsIGZpbGwgPSBHZW5kZXIpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgUGVyY2VudGFnZSkpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgIHNpemUgPSAzLjUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXppb25lIGRpIEdlbmRlciBwZXIgQ291bnRyeSIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIlBlcmNlbnR1YWxlIiwNCiAgICAgICBmaWxsID0gIkdlbmRlciIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpMYSBkaXN0cmlidXppb25lIGRlbCBnZW5lcmUgYWxsJ2ludGVybm8gZGkgb2duaSBwYWVzZSDDqCBwcmF0aWNhbWVudGUgc2VtcHJlIGRpdmlzYSBhbCA1MCUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCiMgUHJlcGFyYXppb25lIGRlaSBkYXRpIGNvbiBjYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpIHBlciBjb2xvbm5hDQpkYXRpX2NvdW50IDwtIGRhdGkgJT4lDQogIGdyb3VwX2J5KENvdW50cnksIERldmljZSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGdyb3VwX2J5KENvdW50cnkpICU+JQ0KICBtdXRhdGUoVG90YWwgPSBzdW0oQ291bnQpKSAlPiUNCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBDb3VudCAvIFRvdGFsICogMTAwKQ0KDQpnZ3Bsb3QoZGF0aV9jb3VudCwgYWVzKHggPSBDb3VudHJ5LCB5ID0gQ291bnQsIGZpbGwgPSBEZXZpY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgUGVyY2VudGFnZSkpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgIHNpemUgPSAzLjUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXppb25lIGRpIERldmljZSBwZXIgQ291bnRyeSIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIlBlcmNlbnR1YWxlIiwNCiAgICAgICBmaWxsID0gIkRldmljZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KRGEgcXVlc3RvIHBsb3QgcG9zc2lhbW8gbm90YXJlIGFsY3VuZSBkaWZmZXJlbnplIHJpZ3VhcmRvIGlsIERldmljZSBjaGUgb2duaSB1dGVudGUgdXRpbGl6emEgaW4gYmFzZSBhbCBwYWVzZS4gQWxjdW5lIGNvc2UgcGnDuSBldmlkZW50aSBzb25vOg0KDQotICAgSW4gR2VybWFuaWEgaWwgZGlzcG9zaXRpdm8gcGnDuSBjb211bmUgw6ggaWwgTGFwdG9wLCBtZW50cmUgbWVubyBjb211bmUgbG8gU21hcnRwaG9uZS4NCg0KLSAgIEluIENhbmFkYSBlIGluIE1lc3NpY28gaWwgZGlzcG9zaXRpdm8gcGnDuSB1dGlsaXp6YXRvIMOoIGlsIFRhYmxldC4NCg0KLSAgIEF1c3RyYWxpYSwgQnJhc2lsZSBlIFJlZ25vIFVuaXRvIHV0aWxpenphbm8gaW4gbWFuaWVyYSBwcmluY2lwYWxlIGxvIFNtYXJ0cGhvbmUuDQoNCi0gICBDaGkgZmEgcGnDuSB1dGlsaXp6byBkaSBTbWFydCBUViDDqCBsYSBTcGFnbmEuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCiMgUHJlcGFyYXppb25lIGRlaSBkYXRpIGNvbiBjYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpIHBlciBjb2xvbm5hDQpkYXRpX2NvdW50IDwtIGRhdGkgJT4lDQogIGdyb3VwX2J5KENvdW50cnksIFN1YnNjcmlwdGlvbi5UeXBlKSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIG11dGF0ZShUb3RhbCA9IHN1bShDb3VudCkpICU+JQ0KICBtdXRhdGUoUGVyY2VudGFnZSA9IENvdW50IC8gVG90YWwgKiAxMDApDQoNCmdncGxvdChkYXRpX2NvdW50LCBhZXMoeCA9IENvdW50cnksIHkgPSBDb3VudCwgZmlsbCA9IFN1YnNjcmlwdGlvbi5UeXBlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZmlsbCIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMWYlJSIsIFBlcmNlbnRhZ2UpKSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSksDQogICAgICAgICAgICBzaXplID0gMy41KSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV6aW9uZSBkaSBTdWJzY3JpcHRpb24uVHlwZSBwZXIgQ291bnRyeSIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIlBlcmNlbnR1YWxlIiwNCiAgICAgICBmaWxsID0gIlN1YnNjcmlwdGlvbiBUeXBlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpBbmFsaXp6YW5kbyBjb21lIGxhIFN1YnNjcmlwdGlvbiBUeXBlIMOoIGRpc3RyaWJ1aXRhIGFsbCdpbnRlcm5vIGRlaSBwYWVzaSwgc2FsdGFubyBhbGwnb2NjaGlvIGFsY3VuaSBpbnNpZ2h0czoNCg0KLSAgIEluIEJyYXNpbGUsIGluIEdlcm1hbmlhIGUgaW4gSXRhbGlhIGxhIHF1YXNpIHRvdGFsaXTDoCBkZWdsaSB1dGVudGkgaGFubm8gYWJib25hbWVudGkgQmFzaWMgKHJpc3BldHRpdmFtZW50ZSA4MCUsIDgxJSBlIDk2JSkuDQoNCi0gICBJbiBGcmFuY2lhIGwnODAlIGRlZ2xpIHV0ZW50aSBoYW5ubyBhYmJvbmFtZW50byBQcmVtaXVtLCBlIG5lc3N1bm8gU3RhbmRhcmQuDQoNCi0gICBJbiBNZXNzaWNvIGUgaW4gUmVnbm8gVW5pdG8gaWwgOTglIGRlZ2xpIGFiYm9uYW1lbnRpIHNvbm8gU3RhbmRhcmQuDQoNCi0gICBBbmNoZSBpbiBBdXN0cmFsaWEgZSBpbiBTcGFnbmEgc2kgcHJlZmVyaXNjb25vIGFiYm9uYW1lbnRpIFByZW1pdW0uDQoNCmBgYHtyfQ0KIyBQcmVwYXJhemlvbmUgZGVpIGRhdGkgY29uIGNhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkgcGVyIGNvbG9ubmENCmRhdGlfY291bnQgPC0gZGF0aSAlPiUNCiAgZ3JvdXBfYnkoU3Vic2NyaXB0aW9uLlR5cGUsIE1vbnRobHkuUmV2ZW51ZSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGdyb3VwX2J5KFN1YnNjcmlwdGlvbi5UeXBlKSAlPiUNCiAgbXV0YXRlKFRvdGFsID0gc3VtKENvdW50KSkgJT4lDQogIG11dGF0ZShQZXJjZW50YWdlID0gQ291bnQgLyBUb3RhbCAqIDEwMCkNCg0KZ2dwbG90KGRhdGlfY291bnQsIGFlcyh4ID0gU3Vic2NyaXB0aW9uLlR5cGUsIHkgPSBDb3VudCwgZmlsbCA9IE1vbnRobHkuUmV2ZW51ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjFmJSUiLCBQZXJjZW50YWdlKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwodmp1c3QgPSAwLjUpLA0KICAgICAgICAgICAgc2l6ZSA9IDMuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1emlvbmUgZGkgTW9udGhseSBSZXZlbnVlIHBlciBTdWJzY3JpcHRpb24gVHlwZSIsDQogICAgICAgeCA9ICJTdXNjcmlwdGlvbiBUeXBlIiwNCiAgICAgICB5ID0gIlBlcmNlbnR1YWxlIiwNCiAgICAgICBmaWxsID0gIk1vbnRobHkuUmV2ZW51ZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KDQojIFRyYXNmb3JtbyBKb2luLkRhdGUgaW4gZm9ybWF0byBEYXRhDQpkYXRpJEpvaW4uRGF0ZSA8LSBhcy5EYXRlKGRhdGkkSm9pbi5EYXRlLCBmb3JtYXQgPSAiJWQtJW0tJXkiKSAgDQoNCiMgUmFnZ3J1cHBvIHBlciBtZXNlDQpkYXRpJEpvaW4uTW9udGggPC0gZmxvb3JfZGF0ZShkYXRpJEpvaW4uRGF0ZSwgIm1vbnRoIikNCnVzZXJfY291bnRzX21vbnRobHkgPC0gYWdncmVnYXRlKHggPSBsaXN0KENvdW50ID0gcmVwKDEsIG5yb3coZGF0aSkpKSwgYnkgPSBsaXN0KERhdGUgPSBkYXRpJEpvaW4uTW9udGgpLCBGVU4gPSBzdW0pDQoNCmdncGxvdCh1c2VyX2NvdW50c19tb250aGx5LCBhZXMoeCA9IERhdGUsIHkgPSBDb3VudCkpICsNCiAgZ2VvbV9saW5lKCkgKyAgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGNvbG9yID0gImJsdWUiKSArICANCiAgbGFicyh0aXRsZSA9ICJOdW1lcm8gZGkgVXRlbnRpIENoZSBTaSBVbmlzY29ubyBuZWwgVGVtcG8gKE1lbnNpbGUpIiwgeCA9ICJEYXRhIiwgeSA9ICJOdW1lcm8gZGkgVXRlbnRpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOCkpKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJW0tJXkiKQ0KYGBgDQoNCkRhbCBMaW5lcGxvdCBzaSBwdcOyIG5vdGFyZSBjaGUgbGEgbWFnZ2lvciBwYXJ0ZSBkZWdsaSBhYmJvbmFtZW50aSBzb25vIGF2dmVudXRpIGRhbCBtZXNlIGRpIEdpdWdubyAyMDIyIGFkIE90dG9icmUgMjAyMiAobWVzZSBtaWdsaW9yZSBpbiBnZW5lcmFsZSksIGNvbiBhbGN1bmkgYWx0aSBlIGJhc3NpIGluIG1lenpvLg0KDQpgYGB7cn0NCiMgVHJhc2Zvcm1vIExhc3QuUGF5bWVudC5EYXRlIGluIGZvcm1hdG8gRGF0YQ0KZGF0aSRMYXN0LlBheW1lbnQuRGF0ZSA8LSBhcy5EYXRlKGRhdGkkTGFzdC5QYXltZW50LkRhdGUsIGZvcm1hdCA9ICIlZC0lbS0leSIpICANCg0KIyBSYWdncnVwcG8gcGVyIG1lc2UNCmRhdGkkTGFzdC5QYXltZW50Lk1vbnRoIDwtIGZsb29yX2RhdGUoZGF0aSRMYXN0LlBheW1lbnQuRGF0ZSwgIm1vbnRoIikNCnBheW1lbnRfY291bnRzX21vbnRobHkgPC0gYWdncmVnYXRlKHggPSBsaXN0KENvdW50ID0gcmVwKDEsIG5yb3coZGF0aSkpKSwgYnkgPSBsaXN0KERhdGUgPSBkYXRpJExhc3QuUGF5bWVudC5Nb250aCksIEZVTiA9IHN1bSkNCg0KZ2dwbG90KHBheW1lbnRfY291bnRzX21vbnRobHksIGFlcyh4ID0gRGF0ZSwgeSA9IENvdW50KSkgKw0KICBnZW9tX2xpbmUoKSArICANCiAgZ2VvbV9wb2ludChzaXplID0gMywgY29sb3IgPSAiYmx1ZSIpICsgIA0KICBsYWJzKHRpdGxlID0gIk51bWVybyBkaSBQYWdhbWVudGkgRWZmZXR0dWF0aSBuZWwgVGVtcG8gKE1lbnNpbGUpIiwgeCA9ICJEYXRhIiwgeSA9ICJOdW1lcm8gZGkgUGFnYW1lbnRpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOCkpKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJW0tJXkiKQ0KYGBgDQoNCkdsaSB1bHRpbWkgcGFnYW1lbnRpIHNvbm8gdHV0dGkgZWZmZXR0dWF0aSBhIGdpdWdubyAyMDIzIGUgbHVnbGlvIDIwMjMsIGVzc2VuZG8gcHJvYmFiaWxtZW50ZSBpbCBEYXRhc2V0IGFnZ2lvcm5hdG8gYSBxdWVzdGUgZGF0ZSwgc2kgcHXDsiBkZWR1cnJlIGNoZSBhYmJpYW1vIHByb2JhYmlsbWVudGUgc29sbyB1dGVudGkgYXR0aXZpLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQojIFJpY29udmVydG8gaWwgTW9udGhseS5SZXZlbnVlIGluIHVuIHRpcG8gbnVtZXJpY28gY29zw6wgZGEgcG90ZXIgdmVkZXJlIGxhIGNvcnJlbGF6aW9uZSBjb24gbCdldMOgDQpkYXRpJE1vbnRobHkuUmV2ZW51ZSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRpJE1vbnRobHkuUmV2ZW51ZSkpDQoNCiMgQ2FsY29sbyBsYSBtYXRyaWNlIGRpIGNvcnJlbGF6aW9uZQ0KY29yX21hdHJpeCA8LSBjb3IoZGF0aVssIGMoIkFnZSIsICJNb250aGx5LlJldmVudWUiKV0pDQoNCiMgQ29ycmVsYXRpb24gTWF0cml4DQpjb3JycGxvdChjb3JfbWF0cml4LCBtZXRob2QgPSAiY29sb3IiLCB0eXBlID0gInVwcGVyIiwgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCANCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDQ1LA0KICAgICAgICAgY2wucG9zID0gIm4iKQ0KYGBgDQoNCkRhbGxhIENvcnJlbGF0aW9uIE1hdHJpeCDDqCBwb3NzaWJpbGUgb3NzZXJ2YXJlIGNoZSBub24gYyfDqCBhbGN1bmEgY29ycmVsYXppb25lIHRyYSBsZSB2YXJpYWJpbGkgKHZhbG9yaSB2aWNpbm8gYWxsbyAwIGluZGljYW5vIGNoZSBub24gYyfDqCBjb3JyZWxhemlvbmUsIHZpY2lubyBhIDEgaW52ZWNlIG1hc3NpbWEpLg0KDQpgYGB7cn0NCiMgVmVyaWZpY28gbCdpbnRlcnZhbGxvIGRpIGNvbmZpZGVuemEgcGVyIGxlIGR1ZSB2YXJpYWJpbGkgbnVtZXJpY2hlDQp0LnRlc3QoZGF0aSRBZ2UpJGNvbmYuaW50DQp0LnRlc3QoZGF0aSRNb250aGx5LlJldmVudWUpJGNvbmYuaW50DQpgYGANCg0KUG9zc2lhbW8gZGlyZSBjb24gaWwgOTUlIGRpIGNvbmZpZGVuemEgY2hlIGxhIG1lZGlhIGRlbGwnZXTDoCBkZWdsaSB1dGVudGkgZGkgTmV0ZmxpeCBuZWwgZGF0YXNldCDDqCBjb21wcmVzYSB0cmEgMzgsNTEgZSAzOSwwOCBhbm5pLiBNZW50cmUgSWwgcmVkZGl0byBtZW5zaWxlIGdlbmVyYXRvIGRhZ2xpIHN0ZXNzaSB1dGVudGkgw6ggY29tcHJlc28gdHJhIDEyLDQ0IGUgMTIsNTcgZG9sbGFyaSwgc2VtcHJlIGNvbCA5NSUgZGkgY29uZmlkZW56YS4NCg0KYGBge3J9DQojIEFwcGxpY28gYWxjdW5pIHRlc3QgY2hpLXF1YWRybw0KY2hpc3EudGVzdCh0YWJsZShkYXRpJFN1YnNjcmlwdGlvbi5UeXBlLCBkYXRpJENvdW50cnkpKQ0KDQpgYGANCg0KVmFsdXRhbmRvIGlsIHRlc3QgZGVsIGNoaS1xdWFkcm8gdHJhIGkgdGlwaSBkaSBhYmJvbmFtZW50byBlIGkgcGFlc2kgw6ggZXZpZGVudGUgY2hlIGlsIHRpcG8gZGkgYWJib25hbWVudG8gw6ggZm9ydGVtZW50ZSBhc3NvY2lhdG8gYWwgcGFlc2UgZGVnbGkgdXRlbnRpLiBVbiBwLXZhbHVlIGJhc3Npc3NpbW8gZSB1biBYLXNxdWFyZWQgbW9sdG8gYWx0byBpbmRpY2FubyBjaGlhcmFtZW50ZSBjaGUgdmEgcmlmaXV0YXRhIGwnaXBvdGVzaSBudWxsYSBlIGNpIGNvbmZlcm1hbm8gY2nDsiBjaGUgZ2nDoCBwb3RldmEgb3NzZXJ2YXJzaSBhYmJhc3RhbnphIG5laSBncmFmaWNpIHByZWNlZGVudGkuDQoNCmBgYHtyfQ0KY2hpc3EudGVzdCh0YWJsZShkYXRpJERldmljZSwgZGF0aSRDb3VudHJ5KSkNCg0KYGBgDQoNClBlciBxdWFudG8gcmlndWFyZGEgRGV2aWNlIGUgQ291bnRyeSBpbnZlY2UgcXVlc3RpIG5vbiBzZW1icmFubyBhdmVyZSBwYXJ0aWNvbGFyZSByaWxldmFuemEgbCd1bm8gc3VsbCdhbHRyby4NCg0KYGBge3J9DQpjaGlzcS50ZXN0KHRhYmxlKGRhdGkkU3Vic2NyaXB0aW9uLlR5cGUsIGRhdGkkR2VuZGVyKSkNCmBgYA0KDQpHZW5lcmUgZSB0aXBvIGRpIGlzY3JpemlvbmUgZGVsbCd1dGVudGUgbm9uIHNvbm8gaW4gYWxjdW4gbW9kbyBsZWdhdGkuDQoNCmBgYHtyfQ0KIyBBcHBsaWNvIHVuYSByZWdyZXNzaW9uZSBsaW5lYXJlIHRyYSBtb250aGx5IHJldmVudWUgZSBldMOgDQptb2RlbGxvX2xpbmVhcmUgPC0gbG0oTW9udGhseS5SZXZlbnVlIH4gQWdlLCBkYXRhID0gZGF0aSkNCnN1bW1hcnkobW9kZWxsb19saW5lYXJlKQ0KYGBgDQoNCkRhaSB2YWxvcmkgY2hlIGNpIHJlc3RpdHVpc2NlIHF1ZXN0byBtb2RlbGxvIGxpbmVhcmUgcG9zc2lhbW8gdHJhcnJlIGNoZSBsJ2V0w6Agbm9uIGluZmx1ZW56YSBpbiBtb2RvIGltcG9ydGFudGUgaWwgY29zdG8gZGVsbCdhYmJvbmFtZW50by4gVW4gUlNFIGFiYmFzdGFuemEgYWx0bywgdW4gUjIgbW9sdG8gYmFzc28gZSBsYSBjb21iaW5hemlvbmUgZGkgcC12YWx1ZSAoXD4wLDA1KSBlIEYtc3RhdGlzdGljIG5vbiBkYW5ubyByaXN1bHRhdGkgcG9zaXRpdmkuIFByZXZhbGUgcXVpbmRpIGwnaXBvdGVzaSBudWxsYS4NCg==