# Importo le librerie che mi servono
library(lubridate)
library(ggplot2)
library(dplyr)
# Importo il dataset
dati <- read.csv("C:\\Users\\arena\\Downloads\\Netflix Userbase.csv", header = TRUE, sep = ",",  stringsAsFactors = FALSE)
dati
dati

Descrizione delle Variabili del Dataset

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:

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:

# 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:

# 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.

LS0tDQp0aXRsZTogIk5ldGZsaXggVXNlcmJhc2UgRGF0YXNldCAoUHJvYmFiaWxpdMOgIHN0YXRpc3RpY2hlIC0gUEU6IEV0aXZpdHkgMSkiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICBmaWdfd2lkdGg6IDEwDQotLS0NCg0KIVtdKGltYWdlcy9uZXRmbGl4LnBuZyl7d2lkdGg9IjUwNiJ9DQoNCmBgYHtyfQ0KIyBJbXBvcnRvIGxlIGxpYnJlcmllIGNoZSBtaSBzZXJ2b25vDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KYGBge3J9DQojIEltcG9ydG8gaWwgZGF0YXNldA0KZGF0aSA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxhcmVuYVxcRG93bmxvYWRzXFxOZXRmbGl4IFVzZXJiYXNlLmNzdiIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmBgYA0KDQpgYGB7cn0NCmRhdGkNCmBgYA0KDQpgYGB7cn0NCiMgTW9zdHJvIHVuYSBwYW5vcmFtaWNhIGdlbmVyYWxlIGRlbGxlIHZhcmlhYmlsaQ0Kc3RyKGRhdGkpDQpgYGANCg0KIyBEZXNjcml6aW9uZSBkZWxsZSBWYXJpYWJpbGkgZGVsIERhdGFzZXQNCg0KLSAgIGBVc2VyLklEYDogVW4gaWRlbnRpZmljYXRvcmUgdW5pY28gcGVyIGNpYXNjdW4gdXRlbnRlLg0KLSAgIGBTdWJzY3JpcHRpb24uVHlwZWA6IElsIHRpcG8gZGkgYWJib25hbWVudG8gc290dG9zY3JpdHRvIGRhbGwndXRlbnRlLCBjb24gY2F0ZWdvcmllIGNvbWUgIkJhc2ljIiwgIlByZW1pdW0iIG8gIlN0YW5kYXJkIi4NCi0gICBgTW9udGhseS5SZXZlbnVlYDogSWwgcmVkZGl0byBtZW5zaWxlIGdlbmVyYXRvIGRhIG9nbmkgdXRlbnRlIHBlciBOZXRmbGl4LCBwcm9iYWJpbG1lbnRlIGluIGRvbGxhcmkuDQotICAgYEpvaW4uRGF0ZWA6IExhIGRhdGEgaW4gY3VpIGwndXRlbnRlIHNpIMOoIGlzY3JpdHRvIGEgTmV0ZmxpeC4NCi0gICBgTGFzdC5QYXltZW50LkRhdGVgOiBMJ3VsdGltYSBkYXRhIGRpIHBhZ2FtZW50byByZWdpc3RyYXRhIHBlciBsJ3V0ZW50ZS4NCi0gICBgQ291bnRyeWA6IElsIHBhZXNlIGRpIHJlc2lkZW56YSBkZWxsJ3V0ZW50ZS4NCi0gICBgQWdlYDogTCdldMOgIGRlbGwndXRlbnRlLg0KLSAgIGBHZW5kZXJgOiBJbCBnZW5lcmUgZGVsbCd1dGVudGUuDQotICAgYERldmljZWA6IElsIGRpc3Bvc2l0aXZvIHV0aWxpenphdG8gZGFsbCd1dGVudGUgcGVyIGFjY2VkZXJlIGEgTmV0ZmxpeC4NCi0gICBgUGxhbi5EdXJhdGlvbmA6IExhIGR1cmF0YSBkZWxsJ2FiYm9uYW1lbnRvIHNvdHRvc2NyaXR0by4NCg0KYGBge3J9DQpzdW1tYXJ5KGRhdGkpDQpgYGANCg0KRGFsIFN1bW1hcnkgdmVkaWFtbyBjaGUgbGUgdmFyaWFiaWxpIG51bWVyaWNoZSBzb25vIDM6DQoNCi0gICBcLSBgVXNlci5JRGA6IE9nbmkgdXRlbnRlIMOoIHByZXNlbnRlIHVuYSB2b2x0YSBzb2xhIG5lbCBkYXRhc2V0LCBhdmVuZG8gaWwgTWF4IFZhbHVlIHVndWFsZSBhbCBudW1lcm8gZGkgcmlnaGUgdG90YWxpLg0KDQotICAgXC0gYE1vbnRobHkuUmV2ZW51ZWA6IETDoCBsJ2ltcHJlc3Npb25lIGRpIGVzc2VyZSB1bmEgInRhcmlmZmEgZmlzc2EiIChkYSAxMCBhIDE1KSwgcGVyIGN1aSBwb3RyZWJiZSBlc3NlcmUgdXRpbGUgdHJhdHRhcmxhIGNvbWUgdW5hIHZhcmlhYmlsZSBjYXRlZ29yaWNhLCBkYXRvIGNoZSBvZ25pIHRhcmlmZmEgcmFwcHJlc2VudGVyw6AgdW4gY2VydG8gdGlwbyBkaSBhYmJvbmFtZW50by4NCg0KLSAgIFwtIGBBZ2VgOiDDiCB1bmEgbm9ybWFsZSB2YXJpYWJpbGUgbnVtZXJpY2EgY2hlIHJhcHByZXNlbnRhIGwnZXTDoCBkZWdsaSB1dGVudGkuDQoNCkRpIHBhcnRpY29sYXJlIGludGVyZXNzZSDDqCBub3RhcmUgY2hlIGxlIHZhcmlhYmlsaSBkaSBkYXRhIHNvbm8gYXR0dWFsbWVudGUgZGkgdGlwbyBTdHJpbmdhLg0KDQpgYGB7cn0NCnN1bW1hcnkoZGF0aSRNb250aGx5LlJldmVudWUpDQpgYGANCg0KYGBge3J9DQp2YXIoZGF0aSRNb250aGx5LlJldmVudWUpDQpzZChkYXRpJE1vbnRobHkuUmV2ZW51ZSkNCmBgYA0KDQpgYGB7cn0NCmhpc3QoZGF0aSRNb250aGx5LlJldmVudWUsIA0KICAgICBtYWluPSJJc3RvZ3JhbW1hIGRlbCBNb250aGx5IFJldmVudWUgY29uIEN1cnZhIGRpIERlbnNpdMOgIiwgDQogICAgIHhsYWI9Ik1vbnRobHkgUmV2ZW51ZSIsIA0KICAgICB5bGFiPSJEZW5zaXTDoCIsDQogICAgIGNvbD0iZ3JlZW4iLCANCiAgICAgYm9yZGVyPSJibGFjayIsDQogICAgIGZyZXE9RkFMU0UpIA0KDQpkZW5zaXTDoF9yZXZlbnVlIDwtIGRlbnNpdHkoZGF0aSRNb250aGx5LlJldmVudWUsIG5hLnJtID0gVFJVRSkNCmxpbmVzKGRlbnNpdMOgX3JldmVudWUsIGNvbD0icmVkIiwgbHdkPTIpIA0KYGBgDQoNCkNvbWUgYXZldm8gZmF0dG8gbm90YXJlIHByaW1hLCBpbCBNb250aGx5IFJldmVudWUgdmEgZGEgMTAgYSAxNSwgcmVuZGVuZG8gcXVpbmRpIHBvY28gdXRpbGUgcXVpIHVuIGlzdG9ncmFtbWEsIGRlY2lkbyBkaSB0cmFzZm9ybWFybGEgdGVtcG9yYW5lYW1lbnRlIGluIHVuYSB2YXJpYWJpbGUgY2F0ZWdvcmljYS4NCg0KYGBge3J9DQpkYXRpJE1vbnRobHkuUmV2ZW51ZSA8LSBhcy5mYWN0b3IoZGF0aSRNb250aGx5LlJldmVudWUpDQpgYGANCg0KYGBge3J9DQojIENhbGNvbG8gZGVsbGUgZnJlcXVlbnplDQpmcmVxdWVuemVfbW9udGhseV9yZXZlbnVlIDwtIHRhYmxlKGRhdGkkTW9udGhseS5SZXZlbnVlKQ0KDQojIENhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkNCnBlcmNlbnR1YWxpX21vbnRobHlfcmV2ZW51ZSA8LSBwcm9wLnRhYmxlKGZyZXF1ZW56ZV9tb250aGx5X3JldmVudWUpICogMTAwDQoNCiMgVmlzdWFsaXp6YXppb25lIGRlbGxlIGZyZXF1ZW56ZSBlIGRlbGxlIHBlcmNlbnR1YWxpDQpmcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlIDwtIGRhdGEuZnJhbWUoRnJlcXVlbnphPWZyZXF1ZW56ZV9tb250aGx5X3JldmVudWUsIFBlcmNlbnR1YWxlPXBlcmNlbnR1YWxpX21vbnRobHlfcmV2ZW51ZSkNCnByaW50KGZyZXFfcGVyY19tb250aGx5X3JldmVudWUpDQpgYGANCg0KYGBge3J9DQojIENyZWF6aW9uZSBkZWwgYmFycGxvdCBwZXIgbGEgZnJlcXVlbnphDQpiYXJwbG90KGZyZXFfcGVyY19tb250aGx5X3JldmVudWUkRnJlcXVlbnphLkZyZXEsDQogICAgICAgIG5hbWVzLmFyZz1mcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlJEZyZXF1ZW56YS5WYXIxLA0KICAgICAgICBtYWluPSJEaXN0cmlidXppb25lIGRlbGxhIE1vbnRobHkgUmV2ZW51ZSIsDQogICAgICAgIHhsYWI9Ik1vbnRobHkgUmV2ZW51ZSIsDQogICAgICAgIHlsYWI9IkZyZXF1ZW56YSIsDQogICAgICAgIGNvbD0iYmx1ZSIsDQogICAgICAgIGxhcz0xKSANCg0KdGV4dCh4ID0gYmFycGxvdChmcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlJEZyZXF1ZW56YS5GcmVxLCBwbG90ID0gRkFMU0UpLA0KICAgICANCiAgICAgeSA9IGZyZXFfcGVyY19tb250aGx5X3JldmVudWUkRnJlcXVlbnphLkZyZXEgKyAxLCANCiAgICAgbGFiZWwgPSBwYXN0ZShyb3VuZChmcmVxX3BlcmNfbW9udGhseV9yZXZlbnVlJFBlcmNlbnR1YWxlLkZyZXEsIDIpLCAiJSIpLCANCiAgICAgcG9zID0gMSwgY2V4PTEsZm9udD0yKSANCmBgYA0KDQpHbGkgYWJib25hbWVudGkgcGnDuSBjb211bmkgc29ubyBxdWVsbGkgYSAxMiBldXJvLCBtZW50cmUgaSBtZW5vIGNvbXVuaSBxdWVsbGkgYSAxMSBldXJvLiBBdmVuZG8gc3RhYmlsaXRvIGNoZSBzb25vIHR1dHRpIHBhZ2FtZW50aSBtZW5zaWxpIMOoIHByb2JhYmlsZSBjaGUgZGlwZW5kYSBkYWwgdGlwbyBkaSBhYmJvbmFtZW50byBlIGRhbCBwYWVzZS4NCg0KYGBge3J9DQpzdW1tYXJ5KGRhdGkkQWdlKQ0KYGBgDQoNCmBgYHtyfQ0KdmFyKGRhdGkkQWdlKQ0Kc2QoZGF0aSRBZ2UpDQpgYGANCg0KYGBge3J9DQpoaXN0KGRhdGkkQWdlLCANCiAgICAgbWFpbj0iSXN0b2dyYW1tYSBkZWxsJ0V0w6AgY29uIEN1cnZhIGRpIERlbnNpdMOgIiwgDQogICAgIHhsYWI9IkV0w6AiLCANCiAgICAgeWxhYj0iRGVuc2l0w6AiLA0KICAgICBjb2w9ImJsdWUiLCANCiAgICAgYm9yZGVyPSJibGFjayIsDQogICAgIGZyZXE9RkFMU0UsDQogICAgIGJyZWFrcz0xMCkNCg0KZGVuc2l0w6AgPC0gZGVuc2l0eShkYXRpJEFnZSkgDQpsaW5lcyhkZW5zaXTDoCwgY29sPSJyZWQiLCBsd2Q9MikgDQoNCmBgYA0KDQpQZXIgcXVhbnRvIHJpZ3VhcmRhIGwnZXTDoCwgbCdpc3RvZ3JhbW1hIGUgbGEgY3VydmEgZGkgZGVuc2l0w6AgbW9zdHJhbm8gdW5hIHJhcHByZXNlbnRhemlvbmUgYWJiYXN0YW56YSByZWdvbGFyZSBkYWkgMjUgYWkgNTAgYW5uaSwgY29uIHVuIGNhbG8gZGFpIDUwIGluIHN1Lg0KDQpgYGB7cn0NCiMgQ2FsY29sbyBkZWxsZSBmcmVxdWVuemUNCmZyZXF1ZW56ZV9zdWJfdHlwZSA8LSB0YWJsZShkYXRpJFN1YnNjcmlwdGlvbi5UeXBlKQ0KDQojIENhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkNCnBlcmNlbnR1YWxpX3N1Yl90eXBlIDwtIHByb3AudGFibGUoZnJlcXVlbnplX3N1Yl90eXBlKSAqIDEwMA0KDQojIFZpc3VhbGl6emF6aW9uZSBkZWxsZSBmcmVxdWVuemUgZSBkZWxsZSBwZXJjZW50dWFsaQ0KZnJlcV9wZXJjX3N1Yl90eXBlIDwtIGRhdGEuZnJhbWUoRnJlcXVlbnphPWZyZXF1ZW56ZV9zdWJfdHlwZSwgUGVyY2VudHVhbGU9cGVyY2VudHVhbGlfc3ViX3R5cGUpDQpwcmludChmcmVxX3BlcmNfc3ViX3R5cGUpDQpgYGANCg0KYGBge3J9DQpiYXJwbG90KGZyZXFfcGVyY19zdWJfdHlwZSRGcmVxdWVuemEuRnJlcSwNCiAgICAgICAgbmFtZXMuYXJnPWZyZXFfcGVyY19zdWJfdHlwZSRGcmVxdWVuemEuVmFyMSwNCiAgICAgICAgbWFpbj0iRGlzdHJpYnV6aW9uZSBkZWxsYSBTdWJzY3JpcHRpb24gVHlwZSIsDQogICAgICAgIHhsYWI9IlN1YnNjcmlwdGlvbiBUeXBlIiwNCiAgICAgICAgeWxhYj0iRnJlcXVlbnphIiwNCiAgICAgICAgY29sPSJibHVlIiwNCiAgICAgICAgbGFzPTEpDQoNCnRleHQoeCA9IGJhcnBsb3QoZnJlcV9wZXJjX3N1Yl90eXBlJEZyZXF1ZW56YS5GcmVxLCBwbG90ID0gRkFMU0UpLCANCiAgICAgeSA9IGZyZXFfcGVyY19zdWJfdHlwZSRGcmVxdWVuemEuRnJlcSArIDEsIA0KICAgICBsYWJlbCA9IHBhc3RlKHJvdW5kKGZyZXFfcGVyY19zdWJfdHlwZSRQZXJjZW50dWFsZS5GcmVxLCAyKSwgIiUiKSwgDQogICAgIHBvcyA9IDEsIGNleD0xLGZvbnQ9MikNCmBgYA0KDQpMYSBjYXRlZ29yaWEgZGkgYWJib25hbWVudG8gcGnDuSBkaWZmdXNhIMOoIHF1ZWxsYSBCYXNpYyBjb24gdW4gNDAlLCBzZWd1aXRhIGRhIFN0YW5kYXJkIGUgcG9jbyBzb3R0byBQcmVtaXVtLg0KDQpgYGB7cn0NCiMgQ2FsY29sbyBkZWxsZSBmcmVxdWVuemUNCmZyZXF1ZW56ZV9jb3VudHJ5IDwtIHRhYmxlKGRhdGkkQ291bnRyeSkNCg0KIyBDYWxjb2xvIGRlbGxlIHBlcmNlbnR1YWxpDQpwZXJjZW50dWFsaV9jb3VudHJ5IDwtIHByb3AudGFibGUoZnJlcXVlbnplX2NvdW50cnkpICogMTAwDQoNCiMgVmlzdWFsaXp6YXppb25lIGRlbGxlIGZyZXF1ZW56ZSBlIGRlbGxlIHBlcmNlbnR1YWxpDQpmcmVxX3BlcmNfY291bnRyeSA8LSBkYXRhLmZyYW1lKEZyZXF1ZW56YT1mcmVxdWVuemVfY291bnRyeSwgUGVyY2VudHVhbGU9cGVyY2VudHVhbGlfY291bnRyeSkNCnByaW50KGZyZXFfcGVyY19jb3VudHJ5KQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCmJhcnBsb3RfaGVpZ2h0cyA8LSBiYXJwbG90KGZyZXFfcGVyY19jb3VudHJ5JEZyZXF1ZW56YS5GcmVxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbj0iRGlzdHJpYnV6aW9uZSBkZWkgcGFlc2kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxhYj0iUGFlc2kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeWxhYj0iRnJlcXVlbnphIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD0iYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYXM9MSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheHQ9J24nKQ0KDQpheGlzKDEsIGF0PWJhcnBsb3RfaGVpZ2h0cywgbGFiZWxzPWZyZXFfcGVyY19jb3VudHJ5JEZyZXF1ZW56YS5WYXIxLCBjZXguYXhpcz0wLjYsIHNydD00NSkNCg0KdGV4dCh4ID0gYmFycGxvdF9oZWlnaHRzLCANCiAgICAgeSA9IGZyZXFfcGVyY19jb3VudHJ5JEZyZXF1ZW56YS5GcmVxICsgMSwgDQogICAgIGxhYmVsID0gcGFzdGUocm91bmQoZnJlcV9wZXJjX2NvdW50cnkkUGVyY2VudHVhbGUuRnJlcSwgMiksICIlIiksIA0KICAgICBwb3MgPSAxLCBjZXg9MC44LGZvbnQ9MikgDQpgYGANCg0KSSBwYWVzaSBjb24gcGnDuSBhYmJvbmF0aSBzb25vIGEgcGFyaSBtZXJpdG8gU3BhZ25hIGUgU3RhdGkgVW5pdGksIHNlZ3VpdGkgcG9pIGRvcG8gZGFsIENhbmFkYSBhbCB0ZXJ6byBwb3N0by4NCg0KYGBge3J9DQojIENhbGNvbG8gZGVsbGUgZnJlcXVlbnplDQpmcmVxdWVuemVfZ2VuZGVyIDwtIHRhYmxlKGRhdGkkR2VuZGVyKQ0KDQojIENhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkNCnBlcmNlbnR1YWxpX2dlbmRlciA8LSBwcm9wLnRhYmxlKGZyZXF1ZW56ZV9nZW5kZXIpICogMTAwDQoNCiMgVmlzdWFsaXp6YXppb25lIGRlbGxlIGZyZXF1ZW56ZSBlIGRlbGxlIHBlcmNlbnR1YWxpDQpmcmVxX3BlcmNfZ2VuZGVyIDwtIGRhdGEuZnJhbWUoRnJlcXVlbnphPWZyZXF1ZW56ZV9nZW5kZXIsIFBlcmNlbnR1YWxlPXBlcmNlbnR1YWxpX2dlbmRlcikNCnByaW50KGZyZXFfcGVyY19nZW5kZXIpDQpgYGANCg0KYGBge3J9DQpiYXJwbG90KGZyZXFfcGVyY19nZW5kZXIkRnJlcXVlbnphLkZyZXEsDQogICAgICAgIG5hbWVzLmFyZz1mcmVxX3BlcmNfZ2VuZGVyJEZyZXF1ZW56YS5WYXIxLA0KICAgICAgICBtYWluPSJEaXN0cmlidXppb25lIGRlbCBnZW5lcmUiLA0KICAgICAgICB4bGFiPSJHZW5lcmkiLA0KICAgICAgICB5bGFiPSJGcmVxdWVuemEiLA0KICAgICAgICBjb2w9ImJsdWUiLA0KICAgICAgICBsYXM9MSkNCg0KdGV4dCh4ID0gYmFycGxvdChmcmVxX3BlcmNfZ2VuZGVyJEZyZXF1ZW56YS5GcmVxLCBwbG90ID0gRkFMU0UpLCANCiAgICAgeSA9IGZyZXFfcGVyY19nZW5kZXIkRnJlcXVlbnphLkZyZXEgKyAxLA0KICAgICBsYWJlbCA9IHBhc3RlKHJvdW5kKGZyZXFfcGVyY19nZW5kZXIkUGVyY2VudHVhbGUuRnJlcSwgMiksICIlIiksIA0KICAgICBwb3MgPSAxLCBjZXg9MSxmb250PTIpIA0KYGBgDQoNCklsIGdlbmVyZSBkZWdsaSBhYmJvbmF0aSDDqCBwcmF0aWNhbWVudGUgZGl2aXNvIGVxdWFtZW50ZSB0cmEgbWFzY2hpIGUgZmVtbWluZS4NCg0KYGBge3J9DQpmcmVxdWVuemVfZGV2aWNlIDwtIHRhYmxlKGRhdGkkRGV2aWNlKQ0KDQpwZXJjZW50dWFsaV9kZXZpY2UgPC0gcHJvcC50YWJsZShmcmVxdWVuemVfZGV2aWNlKSAqIDEwMA0KDQpmcmVxX3BlcmNfZGV2aWNlIDwtIGRhdGEuZnJhbWUoRnJlcXVlbnphPWZyZXF1ZW56ZV9kZXZpY2UsIFBlcmNlbnR1YWxlPXBlcmNlbnR1YWxpX2RldmljZSkNCnByaW50KGZyZXFfcGVyY19kZXZpY2UpDQpgYGANCg0KYGBge3J9DQpiYXJwbG90KGZyZXFfcGVyY19kZXZpY2UkRnJlcXVlbnphLkZyZXEsDQogICAgICAgIG5hbWVzLmFyZz1mcmVxX3BlcmNfZGV2aWNlJEZyZXF1ZW56YS5WYXIxLA0KICAgICAgICBtYWluPSJEaXN0cmlidXppb25lIGRlaSBkaXNwb3NpdGl2aSIsDQogICAgICAgIHhsYWI9IkRpc3Bvc2l0aXZpIiwNCiAgICAgICAgeWxhYj0iRnJlcXVlbnphIiwNCiAgICAgICAgY29sPSJibHVlIiwNCiAgICAgICAgbGFzPTEpDQoNCnRleHQoeCA9IGJhcnBsb3QoZnJlcV9wZXJjX2RldmljZSRGcmVxdWVuemEuRnJlcSwgcGxvdCA9IEZBTFNFKSwgDQogICAgIHkgPSBmcmVxX3BlcmNfZGV2aWNlJEZyZXF1ZW56YS5GcmVxICsgMSwNCiAgICAgbGFiZWwgPSBwYXN0ZShyb3VuZChmcmVxX3BlcmNfZGV2aWNlJFBlcmNlbnR1YWxlLkZyZXEsIDIpLCAiJSIpLCANCiAgICAgcG9zID0gMSwgY2V4PTEsZm9udD0yKSANCmBgYA0KDQpBbmNoZSBwZXIgcXVhbnRvIHJpZ3VhcmRhIGkgZGlzcG9zaXRpdmkgdXRpbGl6emF0aSBwZXIgYWNjZWRlcmUgYSBOZXRmbGl4IGxlIG9zc2VydmF6aW9uaSBzb25vIGVndWFsbWVudGUgZGlzdHJpYnVpdGUuDQoNCmBgYHtyfQ0KIyBDYWxjb2xvIGRlbGxlIGZyZXF1ZW56ZQ0KZnJlcXVlbnplX3BsYW5fZHVyYXRpb24gPC0gdGFibGUoZGF0aSRQbGFuLkR1cmF0aW9uKQ0KDQojIENhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkNCnBlcmNlbnR1YWxpX3BsYW5fZHVyYXRpb24gPC0gcHJvcC50YWJsZShmcmVxdWVuemVfcGxhbl9kdXJhdGlvbikgKiAxMDANCg0KIyBWaXN1YWxpenphemlvbmUgZGVsbGUgZnJlcXVlbnplIGUgZGVsbGUgcGVyY2VudHVhbGkNCmZyZXFfcGVyY19wbGFuX2R1cmF0aW9uIDwtIGRhdGEuZnJhbWUoRnJlcXVlbnphPWZyZXF1ZW56ZV9wbGFuX2R1cmF0aW9uLCBQZXJjZW50dWFsZT1wZXJjZW50dWFsaV9wbGFuX2R1cmF0aW9uKQ0KcHJpbnQoZnJlcV9wZXJjX3BsYW5fZHVyYXRpb24pDQpgYGANCg0KYGBge3J9DQpib3hwbG90KGRhdGkkQWdlIH4gZGF0aSRTdWJzY3JpcHRpb24uVHlwZSwNCiAgICAgICAgbWFpbj0iQm94cGxvdCBkaSBBZ2UgcGVyIFN1YnNjcmlwdGlvbiBUeXBlIiwNCiAgICAgICAgeGxhYj0iVGlwbyBkaSBBYmJvbmFtZW50byIsDQogICAgICAgIHlsYWI9IkV0w6AiLA0KICAgICAgICBjb2w9ImxpZ2h0Ymx1ZSIpDQoNCmBgYA0KDQpJIGJveHBsb3Qgbm9uIG1vc3RyYW5vIHVuYSBncmFuZGUgZGlmZmVyZW56YSBhIGxpdmVsbG8gZGkgZGlzdHJpYnV6aW9uZSBkZWxsJ2V0w6AgbmVsbGUgMyBjYXRlZ29yaWUgZGkgYWJib25hbWVudG8uDQoNCmBgYHtyfQ0KYm94cGxvdChkYXRpJEFnZSB+IGRhdGkkR2VuZGVyLA0KICAgICAgICBtYWluPSJCb3hwbG90IGRpIEFnZSBwZXIgR2VuZGVyIiwNCiAgICAgICAgeGxhYj0iR2VuZXJlIiwNCiAgICAgICAgeWxhYj0iRXTDoCIsDQogICAgICAgIGNvbD0ibGlnaHRncmVlbiIpDQpgYGANCg0KQW5jaGUgcGVyIHF1YW50byByaWd1YXJkYSBpbCBnZW5lcmUgcmFwcG9ydGF0byBhbGwnZXTDoCBub24gYyfDqCB0cm9wcGEgZGlmZmVyZW56YSwgc2kgcHXDsiBkaXJlIGNoZSBmb3JzZSBsZSBmZW1taW5lIHNvbm8gbGVnZ2VybWVudGUgKGRpIG1vbHRvIHBvY28pIHBpw7kgYW56aWFuZS4NCg0KYGBge3J9DQpib3hwbG90KGRhdGkkQWdlIH4gZGF0aSREZXZpY2UsDQogICAgICAgIG1haW49IkJveHBsb3QgZGkgQWdlIHBlciBEZXZpY2UiLA0KICAgICAgICB4bGFiPSJEaXNwb3NpdGl2byIsDQogICAgICAgIHlsYWI9IkV0w6AiLA0KICAgICAgICBjb2w9ImxpZ2h0Y29yYWwiKQ0KYGBgDQoNClBlciBxdWFudG8gcmlndWFyZGEgaWwgRGV2aWNlIHJhcHBvcnRhdG8gYWxsJ2V0w6AsIGxlIGRpZmZlcmVuemUgc29ubyBtb2x0byBzb3R0aWxpLCBzaSBwdcOyIG5vdGFyZSBjaGUgbmVsbGEgZmFzY2lhIHNvdHRvIGkgMzUgw6ggcGnDuSBwcm9iYWJpbGUgdHJvdmFyZSB1dGVudGkgY2hlIHVzYW5vIFNtYXJ0IFRWIGUgVGFibGV0Lg0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQpib3hwbG90KGRhdGkkQWdlIH4gZGF0aSRDb3VudHJ5LA0KICAgICAgICBtYWluPSJCb3hwbG90IGRpIEFnZSBwZXIgUGFlc2UiLA0KICAgICAgICB4bGFiPSJQYWVzZSIsDQogICAgICAgIHlsYWI9IkV0w6AiLA0KICAgICAgICBjb2w9ImxpZ2h0eWVsbG93IiwNCiAgICAgICAgY2V4LmF4aXM9MC44KQ0KYGBgDQoNCkl0YWxpYSBlIEJyYXNpbGUgaGFubm8gaWwgcmFuZ2UgcGnDuSBhbXBpbyBkaSBldMOgIGRpIHV0ZW50aSBwcmVzZW50aSBwcmVuZGVuZG8gcXVhc2kgdHV0dGEgbGEgZmFzY2lhIGRpIGV0w6AgZGlzcG9uaWJpbGUsIG1lbnRyZSBhZCBlc2VtcGlvIGluIEdlcm1hbmlhIGxlIG9zc2VydmF6aW9uaSBzb25vIHBpw7kgY29uY2VudHJhdGUgdHJhIGkgMzUgZSBpIDQ1Lg0KDQpgYGB7cn0NCg0KIyBSYWdncnVwcG8gbGUgb3NzZXJ2YXppb25pIHBlciBnZW5lcmUgZSBzdWJzY3JpcHRpb24gdHlwZSBlIGNhbGNvbG8gbGUgcGVyY2VudHVhbGkNCmRhdGlfY291bnQgPC0gZGF0aSAlPiUNCiAgZ3JvdXBfYnkoR2VuZGVyLCBTdWJzY3JpcHRpb24uVHlwZSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGdyb3VwX2J5KEdlbmRlcikgJT4lDQogIG11dGF0ZShUb3RhbCA9IHN1bShDb3VudCkpICU+JQ0KICBtdXRhdGUoUGVyY2VudGFnZSA9IENvdW50IC8gVG90YWwgKiAxMDApDQoNCmdncGxvdChkYXRpX2NvdW50LCBhZXMoeCA9IEdlbmRlciwgeSA9IENvdW50LCBmaWxsID0gU3Vic2NyaXB0aW9uLlR5cGUpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMWYlJSIsIFBlcmNlbnRhZ2UpLCB5ID0gQ291bnQpLCANCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCB2anVzdCA9IC0wLjUsIHNpemUgPSAzLjUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXppb25lIGRpIFN1YnNjcmlwdGlvbiBUeXBlIHBlciBHZW5kZXIiLA0KICAgICAgIHggPSAiR2VuZGVyIiwNCiAgICAgICB5ID0gIkNvbnRlZ2dpbyIsDQogICAgICAgZmlsbCA9ICJTdWJzY3JpcHRpb24gVHlwZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KTGEgZGlzdHJpYnV6aW9uZSBkZWwgdGlwbyBkaSBhYmJvbmFtZW50byBwZXIgZ2VuZXJlIG5vbiBwcmVzZW50YSBwcmF0aWNvbGFyaSBkaWZmZXJlbnplLCBlbnRyYW1iaSBwcmVmZXJpc2Nvbm8gbCdhYmJvbmFtZW50byBCYXNpYywgYSBzZWd1aXJlIGxvIFN0YW5kYXJkIGUgcG9pIGlsIFByZW1pdW0uDQoNCmBgYHtyfQ0KIyBSYWdncnVwcG8gbGUgb3NzZXJ2YXppb25pIHBlciBnZW5lcmUgZSBkZXZpY2UgZSBjYWxjb2xvIGxlIHBlcmNlbnR1YWxpDQpkYXRpX2NvdW50IDwtIGRhdGkgJT4lDQogIGdyb3VwX2J5KEdlbmRlciwgRGV2aWNlKSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgZ3JvdXBfYnkoR2VuZGVyKSAlPiUNCiAgbXV0YXRlKFRvdGFsID0gc3VtKENvdW50KSkgJT4lDQogIG11dGF0ZShQZXJjZW50YWdlID0gQ291bnQgLyBUb3RhbCAqIDEwMCkNCg0KZ2dwbG90KGRhdGlfY291bnQsIGFlcyh4ID0gR2VuZGVyLCB5ID0gQ291bnQsIGZpbGwgPSBEZXZpY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMWYlJSIsIFBlcmNlbnRhZ2UpLCB5ID0gQ291bnQpLCANCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCB2anVzdCA9IC0wLjUsIHNpemUgPSAzLjUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXppb25lIGRpIERldmljZSBwZXIgR2VuZGVyIiwNCiAgICAgICB4ID0gIkdlbmRlciIsDQogICAgICAgeSA9ICJDb250ZWdnaW8iLA0KICAgICAgIGZpbGwgPSAiRGV2aWNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpTaSBwdcOyIG9zc2VydmFyZSB1bmEgbGVnZ2VyYSBkaWZmZXJlbnphIGEgbGl2ZWxsbyBkaSBEZXZpY2UgcGVyIEdlbmVyZSwgaSBtYXNjaGkgdXNhbm8gcGnDuSBsbyBTbWFydHBob25lLCBtZW50cmUgbGUgZmVtbWluZSBwacO5IExhcHRvcCBlIFRhYmxldCwgdGVuZW5kbyBsbyBTbWFydHBob25lIGNvbWUgdWx0aW1hIHNjZWx0YS4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KIyBQcmVwYXJhemlvbmUgZGVpIGRhdGkgY29uIGNhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkgcGVyIGNvbG9ubmENCmRhdGlfY291bnQgPC0gZGF0aSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSwgR2VuZGVyKSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIG11dGF0ZShUb3RhbCA9IHN1bShDb3VudCkpICU+JQ0KICBtdXRhdGUoUGVyY2VudGFnZSA9IENvdW50IC8gVG90YWwgKiAxMDApDQoNCmdncGxvdChkYXRpX2NvdW50LCBhZXMoeCA9IENvdW50cnksIHkgPSBDb3VudCwgZmlsbCA9IEdlbmRlcikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjFmJSUiLCBQZXJjZW50YWdlKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwodmp1c3QgPSAwLjUpLA0KICAgICAgICAgICAgc2l6ZSA9IDMuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1emlvbmUgZGkgR2VuZGVyIHBlciBDb3VudHJ5IiwNCiAgICAgICB4ID0gIkNvdW50cnkiLA0KICAgICAgIHkgPSAiUGVyY2VudHVhbGUiLA0KICAgICAgIGZpbGwgPSAiR2VuZGVyIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCkxhIGRpc3RyaWJ1emlvbmUgZGVsIGdlbmVyZSBhbGwnaW50ZXJubyBkaSBvZ25pIHBhZXNlIMOoIHByYXRpY2FtZW50ZSBzZW1wcmUgZGl2aXNhIGFsIDUwJS4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KIyBQcmVwYXJhemlvbmUgZGVpIGRhdGkgY29uIGNhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkgcGVyIGNvbG9ubmENCmRhdGlfY291bnQgPC0gZGF0aSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSwgRGV2aWNlKSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIG11dGF0ZShUb3RhbCA9IHN1bShDb3VudCkpICU+JQ0KICBtdXRhdGUoUGVyY2VudGFnZSA9IENvdW50IC8gVG90YWwgKiAxMDApDQoNCmdncGxvdChkYXRpX2NvdW50LCBhZXMoeCA9IENvdW50cnksIHkgPSBDb3VudCwgZmlsbCA9IERldmljZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjFmJSUiLCBQZXJjZW50YWdlKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwodmp1c3QgPSAwLjUpLA0KICAgICAgICAgICAgc2l6ZSA9IDMuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1emlvbmUgZGkgRGV2aWNlIHBlciBDb3VudHJ5IiwNCiAgICAgICB4ID0gIkNvdW50cnkiLA0KICAgICAgIHkgPSAiUGVyY2VudHVhbGUiLA0KICAgICAgIGZpbGwgPSAiRGV2aWNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpEYSBxdWVzdG8gcGxvdCBwb3NzaWFtbyBub3RhcmUgYWxjdW5lIGRpZmZlcmVuemUgcmlndWFyZG8gaWwgRGV2aWNlIGNoZSBvZ25pIHV0ZW50ZSB1dGlsaXp6YSBpbiBiYXNlIGFsIHBhZXNlLiBBbGN1bmUgY29zZSBwacO5IGV2aWRlbnRpIHNvbm86DQoNCi0gICBJbiBHZXJtYW5pYSBpbCBkaXNwb3NpdGl2byBwacO5IGNvbXVuZSDDqCBpbCBMYXB0b3AsIG1lbnRyZSBtZW5vIGNvbXVuZSBsbyBTbWFydHBob25lLg0KDQotICAgSW4gQ2FuYWRhIGUgaW4gTWVzc2ljbyBpbCBkaXNwb3NpdGl2byBwacO5IHV0aWxpenphdG8gw6ggaWwgVGFibGV0Lg0KDQotICAgQXVzdHJhbGlhLCBCcmFzaWxlIGUgUmVnbm8gVW5pdG8gdXRpbGl6emFubyBpbiBtYW5pZXJhIHByaW5jaXBhbGUgbG8gU21hcnRwaG9uZS4NCg0KLSAgIENoaSBmYSBwacO5IHV0aWxpenpvIGRpIFNtYXJ0IFRWIMOoIGxhIFNwYWduYS4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KIyBQcmVwYXJhemlvbmUgZGVpIGRhdGkgY29uIGNhbGNvbG8gZGVsbGUgcGVyY2VudHVhbGkgcGVyIGNvbG9ubmENCmRhdGlfY291bnQgPC0gZGF0aSAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSwgU3Vic2NyaXB0aW9uLlR5cGUpICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQ0KICBncm91cF9ieShDb3VudHJ5KSAlPiUNCiAgbXV0YXRlKFRvdGFsID0gc3VtKENvdW50KSkgJT4lDQogIG11dGF0ZShQZXJjZW50YWdlID0gQ291bnQgLyBUb3RhbCAqIDEwMCkNCg0KZ2dwbG90KGRhdGlfY291bnQsIGFlcyh4ID0gQ291bnRyeSwgeSA9IENvdW50LCBmaWxsID0gU3Vic2NyaXB0aW9uLlR5cGUpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgUGVyY2VudGFnZSkpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgIHNpemUgPSAzLjUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXppb25lIGRpIFN1YnNjcmlwdGlvbi5UeXBlIHBlciBDb3VudHJ5IiwNCiAgICAgICB4ID0gIkNvdW50cnkiLA0KICAgICAgIHkgPSAiUGVyY2VudHVhbGUiLA0KICAgICAgIGZpbGwgPSAiU3Vic2NyaXB0aW9uIFR5cGUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkFuYWxpenphbmRvIGNvbWUgbGEgU3Vic2NyaXB0aW9uIFR5cGUgw6ggZGlzdHJpYnVpdGEgYWxsJ2ludGVybm8gZGVpIHBhZXNpLCBzYWx0YW5vIGFsbCdvY2NoaW8gYWxjdW5pIGluc2lnaHRzOg0KDQotICAgSW4gQnJhc2lsZSwgaW4gR2VybWFuaWEgZSBpbiBJdGFsaWEgbGEgcXVhc2kgdG90YWxpdMOgIGRlZ2xpIHV0ZW50aSBoYW5ubyBhYmJvbmFtZW50aSBCYXNpYyAocmlzcGV0dGl2YW1lbnRlIDgwJSwgODElIGUgOTYlKS4NCg0KLSAgIEluIEZyYW5jaWEgbCc4MCUgZGVnbGkgdXRlbnRpIGhhbm5vIGFiYm9uYW1lbnRvIFByZW1pdW0sIGUgbmVzc3VubyBTdGFuZGFyZC4NCg0KLSAgIEluIE1lc3NpY28gZSBpbiBSZWdubyBVbml0byBpbCA5OCUgZGVnbGkgYWJib25hbWVudGkgc29ubyBTdGFuZGFyZC4NCg0KLSAgIEFuY2hlIGluIEF1c3RyYWxpYSBlIGluIFNwYWduYSBzaSBwcmVmZXJpc2Nvbm8gYWJib25hbWVudGkgUHJlbWl1bS4NCg0KYGBge3J9DQojIFByZXBhcmF6aW9uZSBkZWkgZGF0aSBjb24gY2FsY29sbyBkZWxsZSBwZXJjZW50dWFsaSBwZXIgY29sb25uYQ0KZGF0aV9jb3VudCA8LSBkYXRpICU+JQ0KICBncm91cF9ieShTdWJzY3JpcHRpb24uVHlwZSwgTW9udGhseS5SZXZlbnVlKSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUNCiAgZ3JvdXBfYnkoU3Vic2NyaXB0aW9uLlR5cGUpICU+JQ0KICBtdXRhdGUoVG90YWwgPSBzdW0oQ291bnQpKSAlPiUNCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBDb3VudCAvIFRvdGFsICogMTAwKQ0KDQpnZ3Bsb3QoZGF0aV9jb3VudCwgYWVzKHggPSBTdWJzY3JpcHRpb24uVHlwZSwgeSA9IENvdW50LCBmaWxsID0gTW9udGhseS5SZXZlbnVlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZmlsbCIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMWYlJSIsIFBlcmNlbnRhZ2UpKSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSksDQogICAgICAgICAgICBzaXplID0gMy41KSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV6aW9uZSBkaSBNb250aGx5IFJldmVudWUgcGVyIFN1YnNjcmlwdGlvbiBUeXBlIiwNCiAgICAgICB4ID0gIlN1c2NyaXB0aW9uIFR5cGUiLA0KICAgICAgIHkgPSAiUGVyY2VudHVhbGUiLA0KICAgICAgIGZpbGwgPSAiTW9udGhseS5SZXZlbnVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQoNCiMgVHJhc2Zvcm1vIEpvaW4uRGF0ZSBpbiBmb3JtYXRvIERhdGENCmRhdGkkSm9pbi5EYXRlIDwtIGFzLkRhdGUoZGF0aSRKb2luLkRhdGUsIGZvcm1hdCA9ICIlZC0lbS0leSIpICANCg0KIyBSYWdncnVwcG8gcGVyIG1lc2UNCmRhdGkkSm9pbi5Nb250aCA8LSBmbG9vcl9kYXRlKGRhdGkkSm9pbi5EYXRlLCAibW9udGgiKQ0KdXNlcl9jb3VudHNfbW9udGhseSA8LSBhZ2dyZWdhdGUoeCA9IGxpc3QoQ291bnQgPSByZXAoMSwgbnJvdyhkYXRpKSkpLCBieSA9IGxpc3QoRGF0ZSA9IGRhdGkkSm9pbi5Nb250aCksIEZVTiA9IHN1bSkNCg0KZ2dwbG90KHVzZXJfY291bnRzX21vbnRobHksIGFlcyh4ID0gRGF0ZSwgeSA9IENvdW50KSkgKw0KICBnZW9tX2xpbmUoKSArICANCiAgZ2VvbV9wb2ludChzaXplID0gMywgY29sb3IgPSAiYmx1ZSIpICsgIA0KICBsYWJzKHRpdGxlID0gIk51bWVybyBkaSBVdGVudGkgQ2hlIFNpIFVuaXNjb25vIG5lbCBUZW1wbyAoTWVuc2lsZSkiLCB4ID0gIkRhdGEiLCB5ID0gIk51bWVybyBkaSBVdGVudGkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlbS0leSIpDQpgYGANCg0KRGFsIExpbmVwbG90IHNpIHB1w7Igbm90YXJlIGNoZSBsYSBtYWdnaW9yIHBhcnRlIGRlZ2xpIGFiYm9uYW1lbnRpIHNvbm8gYXZ2ZW51dGkgZGFsIG1lc2UgZGkgR2l1Z25vIDIwMjIgYWQgT3R0b2JyZSAyMDIyIChtZXNlIG1pZ2xpb3JlIGluIGdlbmVyYWxlKSwgY29uIGFsY3VuaSBhbHRpIGUgYmFzc2kgaW4gbWV6em8uDQoNCmBgYHtyfQ0KIyBUcmFzZm9ybW8gTGFzdC5QYXltZW50LkRhdGUgaW4gZm9ybWF0byBEYXRhDQpkYXRpJExhc3QuUGF5bWVudC5EYXRlIDwtIGFzLkRhdGUoZGF0aSRMYXN0LlBheW1lbnQuRGF0ZSwgZm9ybWF0ID0gIiVkLSVtLSV5IikgIA0KDQojIFJhZ2dydXBwbyBwZXIgbWVzZQ0KZGF0aSRMYXN0LlBheW1lbnQuTW9udGggPC0gZmxvb3JfZGF0ZShkYXRpJExhc3QuUGF5bWVudC5EYXRlLCAibW9udGgiKQ0KcGF5bWVudF9jb3VudHNfbW9udGhseSA8LSBhZ2dyZWdhdGUoeCA9IGxpc3QoQ291bnQgPSByZXAoMSwgbnJvdyhkYXRpKSkpLCBieSA9IGxpc3QoRGF0ZSA9IGRhdGkkTGFzdC5QYXltZW50Lk1vbnRoKSwgRlVOID0gc3VtKQ0KDQpnZ3Bsb3QocGF5bWVudF9jb3VudHNfbW9udGhseSwgYWVzKHggPSBEYXRlLCB5ID0gQ291bnQpKSArDQogIGdlb21fbGluZSgpICsgIA0KICBnZW9tX3BvaW50KHNpemUgPSAzLCBjb2xvciA9ICJibHVlIikgKyAgDQogIGxhYnModGl0bGUgPSAiTnVtZXJvIGRpIFBhZ2FtZW50aSBFZmZldHR1YXRpIG5lbCBUZW1wbyAoTWVuc2lsZSkiLCB4ID0gIkRhdGEiLCB5ID0gIk51bWVybyBkaSBQYWdhbWVudGkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlbS0leSIpDQpgYGANCg0KR2xpIHVsdGltaSBwYWdhbWVudGkgc29ubyB0dXR0aSBlZmZldHR1YXRpIGEgZ2l1Z25vIDIwMjMgZSBsdWdsaW8gMjAyMywgZXNzZW5kbyBwcm9iYWJpbG1lbnRlIGlsIERhdGFzZXQgYWdnaW9ybmF0byBhIHF1ZXN0ZSBkYXRlLCBzaSBwdcOyIGRlZHVycmUgY2hlIGFiYmlhbW8gcHJvYmFiaWxtZW50ZSBzb2xvIHV0ZW50aSBhdHRpdmkuDQo=