Resources

Install packages (for this training)

This only needs to be done once in your system:

install.packages(c("tidyverse", "openxlsx", "knitr", "janitor", "kable", "kableExtra"))
Error in install.packages : Updating loaded packages

Load libraries

We load the libraries we will use in our project.

library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.0     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ───────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ purrr::%||%()   masks base::%||%()
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(readxl)
library(openxlsx)
library(janitor)

Attaching package: ‘janitor’

The following objects are masked from ‘package:stats’:

    chisq.test, fisher.test
library(knitr)
library(kableExtra)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio

Attaching package: ‘kableExtra’

The following object is masked from ‘package:dplyr’:

    group_rows

Configuration

Here we define a simple vector that contains the UNICEF palette of colors, for later use in plots:

UNICEF_PALETTE <- c("#00AEEF","#00833D", "#80BD41", "#FFC20E", "#F26A21", "#E2231A", "#961A49", "#6A1E74", "#D8D1C9", "#777779", "#2D2926", "#374EA2")

Import data

Using openxlsx library

dataset1 <- read.xlsx("data/Education - MOZ_Edu Cluster_5Ws_Q12024.xlsx", sheet = "Data_Entry_5W", check.names = TRUE, detectDates = TRUE, startRow = 5)

Let’s check the column names:

names(dataset1)
 [1] "Lead.organization...Organização.líder"                                                                                                                        
 [2] "Organization.type...Tipo.de.organização"                                                                                                                      
 [3] "Implementing.partner...Parceiro.de.implementação"                                                                                                             
 [4] "Organization.type...Tipo.de.organização.1"                                                                                                                    
 [5] "Consortium.Lead..in.case.the.activity.is.implemented.under.a.consortium....Líder.de.Consórcio..caso.a.actividade.seja.implementada.no.âmbito.de.um.consórcio."
 [6] "Donor...Doador"                                                                                                                                               
 [7] "Period...Período"                                                                                                                                             
 [8] "HRP.ou.non.HRP.project...Projeto.HRP.ou.não.HRP"                                                                                                              
 [9] "Type.of.Disaster..Tipo.de.Desastre"                                                                                                                           
[10] "Sector...Setor"                                                                                                                                               
[11] "Cluster.objective...Objectivo.do.cluster"                                                                                                                     
[12] "Cluster.indicator...Indicador.do.cluster"                                                                                                                     
[13] "HRP.activity...Actividade.do.HRP"                                                                                                                             
[14] "Non.HRP.activity...Actividade.não.HRP"                                                                                                                        
[15] "Unit.of.measure...Unidade.de.medida"                                                                                                                          
[16] "Delivery.modality...Modalidade.de.intervenção"                                                                                                                
[17] "Activity.status...Progresso.da.actividade"                                                                                                                    
[18] "X..of.items.distributed.or.infrastructures.improved.....de.ítens.distribuídos.ou.infraestruturas.melhoradas"                                                  
[19] "Province...Província...ADM1.PT"                                                                                                                               
[20] "ADM1...PCODE"                                                                                                                                                 
[21] "District...Distrito...ADM2.PT"                                                                                                                                
[22] "ADM2...PCODE.2"                                                                                                                                               
[23] "Posto...ADM3.PT"                                                                                                                                              
[24] "School.name...Nome.da.escola"                                                                                                                                 
[25] "School.code...Código.de.escola"                                                                                                                               
[26] "Other.specific.location...Outro.local.específico"                                                                                                             
[27] "Type.of.beneficiary.reached...Tipo.de.beneficiário.alcançado"                                                                                                 
[28] "Boys...Meninos...18y."                                                                                                                                        
[29] "Girls...Meninas...18y."                                                                                                                                       
[30] "Men...Homens..18...59y."                                                                                                                                      
[31] "Women...Mulheres..18...59y."                                                                                                                                  
[32] "Elderly.men...Idosos..60.y."                                                                                                                                  
[33] "Elderly.women...Idosas..60.y."                                                                                                                                
[34] "Of.which.are.male.PWD..Número.de.pessoas.masculinas.com.deficiência"                                                                                          
[35] "Of.which.are.female.PWD...Número.de.pessoas.femininas.com.deficiência"                                                                                        
[36] "Total.male...Total.masculino"                                                                                                                                 
[37] "Total.female...Total.feminino"                                                                                                                                
[38] "Total.people.reached...Total.de.pessoas.alcançadas"                                                                                                           
[39] "Child.Participation...Participação.da.criança"                                                                                                                
[40] "Comments...Comentários"                                                                                                                                       

Using readxl library

dataset2 <- read_excel("data/Education - MOZ_Edu Cluster_5Ws_Q12024.xlsx", sheet = "Data_Entry_5W", skip = 4)

names(dataset2)
 [1] "Lead organization / Organização líder"                                                                                                                        
 [2] "Organization type  / Tipo de organização"                                                                                                                     
 [3] "Implementing partner / Parceiro de implementação"                                                                                                             
 [4] "Organization type / Tipo de organização"                                                                                                                      
 [5] "Consortium Lead (in case the activity is implemented under a consortium) / Líder de Consórcio (caso a actividade seja implementada no âmbito de um consórcio)"
 [6] "Donor / Doador"                                                                                                                                               
 [7] "Period / Período"                                                                                                                                             
 [8] "HRP ou non-HRP project / Projeto HRP ou não-HRP"                                                                                                              
 [9] "Type of Disaster/ Tipo de Desastre"                                                                                                                           
[10] "Sector / Setor"                                                                                                                                               
[11] "Cluster objective / Objectivo do cluster"                                                                                                                     
[12] "Cluster indicator / Indicador do cluster"                                                                                                                     
[13] "HRP activity / Actividade do HRP"                                                                                                                             
[14] "Non-HRP activity / Actividade não-HRP"                                                                                                                        
[15] "Unit of measure / Unidade de medida"                                                                                                                          
[16] "Delivery modality / Modalidade de intervenção"                                                                                                                
[17] "Activity status / Progresso da actividade"                                                                                                                    
[18] "# of items distributed or infrastructures improved / # de ítens distribuídos ou infraestruturas melhoradas"                                                   
[19] "Province / Província / ADM1 PT"                                                                                                                               
[20] "ADM1 / PCODE"                                                                                                                                                 
[21] "District / Distrito / ADM2  PT"                                                                                                                               
[22] "ADM2 / PCODE 2"                                                                                                                                               
[23] "Posto / ADM3 PT"                                                                                                                                              
[24] "School name / Nome da escola"                                                                                                                                 
[25] "School code / Código de escola"                                                                                                                               
[26] "Other specific location / Outro local específico"                                                                                                             
[27] "Type of beneficiary reached / Tipo de beneficiário alcançado"                                                                                                 
[28] "Boys / Meninos (<18y)"                                                                                                                                        
[29] "Girls / Meninas (<18y)"                                                                                                                                       
[30] "Men / Homens (18 - 59y)"                                                                                                                                      
[31] "Women / Mulheres (18 - 59y)"                                                                                                                                  
[32] "Elderly men / Idosos (60+y)"                                                                                                                                  
[33] "Elderly women / Idosas (60+y)"                                                                                                                                
[34] "Of which are male PWD /Número de pessoas masculinas com deficiência"                                                                                          
[35] "Of which are female PWD / Número de pessoas femininas com deficiência"                                                                                        
[36] "Total male / Total masculino"                                                                                                                                 
[37] "Total female / Total feminino"                                                                                                                                
[38] "Total people reached / Total de pessoas alcançadas"                                                                                                           
[39] "Child Participation / Participação da criança"                                                                                                                
[40] "Comments / Comentários"                                                                                                                                       

Quick exploration

We will use dataset2 in the example.

Get dataset dimensions

  • nrow(dataset2): Returns the number of rows in the dataset2 dataset.
  • ncol(dataset2): Returns the number of columns in the dataset2 dataset.
  • dim(dataset2): Returns a vector containing the number of rows and columns in the dataset2 dataset.
nrow(dataset2)
[1] 711
ncol(dataset2)
[1] 40
dim(dataset2)
[1] 711  40

Using head

head(dataset2) displays the first six rows of the dataset2 dataset.

head(dataset2)

Using summary

summary(dataset2) provides a statistical summary of each variable in the dataset2 dataset, including measures like mean, median, quartiles, minimum, and maximum values.

summary(dataset2)
 Lead organization / Organização líder Organization type  / Tipo de organização
 Length:711                            Length:711                              
 Class :character                      Class :character                        
 Mode  :character                      Mode  :character                        
                                                                               
                                                                               
                                                                               
                                                                               
 Implementing partner / Parceiro de implementação Organization type / Tipo de organização
 Length:711                                       Length:711                             
 Class :character                                 Class :character                       
 Mode  :character                                 Mode  :character                       
                                                                                         
                                                                                         
                                                                                         
                                                                                         
 Consortium Lead (in case the activity is implemented under a consortium) / Líder de Consórcio (caso a actividade seja implementada no âmbito de um consórcio)
 Length:711                                                                                                                                                   
 Class :character                                                                                                                                             
 Mode  :character                                                                                                                                             
                                                                                                                                                              
                                                                                                                                                              
                                                                                                                                                              
                                                                                                                                                              
 Donor / Doador     Period / Período   HRP ou non-HRP project / Projeto HRP ou não-HRP
 Length:711         Length:711         Length:711                                     
 Class :character   Class :character   Class :character                               
 Mode  :character   Mode  :character   Mode  :character                               
                                                                                      
                                                                                      
                                                                                      
                                                                                      
 Type of Disaster/ Tipo de Desastre Sector / Setor     Cluster objective / Objectivo do cluster
 Length:711                         Length:711         Length:711                              
 Class :character                   Class :character   Class :character                        
 Mode  :character                   Mode  :character   Mode  :character                        
                                                                                               
                                                                                               
                                                                                               
                                                                                               
 Cluster indicator / Indicador do cluster HRP activity / Actividade do HRP
 Length:711                               Length:711                      
 Class :character                         Class :character                
 Mode  :character                         Mode  :character                
                                                                          
                                                                          
                                                                          
                                                                          
 Non-HRP activity / Actividade não-HRP Unit of measure / Unidade de medida
 Length:711                            Length:711                         
 Class :character                      Class :character                   
 Mode  :character                      Mode  :character                   
                                                                          
                                                                          
                                                                          
                                                                          
 Delivery modality / Modalidade de intervenção Activity status / Progresso da actividade
 Length:711                                    Length:711                               
 Class :character                              Class :character                         
 Mode  :character                              Mode  :character                         
                                                                                        
                                                                                        
                                                                                        
                                                                                        
 # of items distributed or infrastructures improved / # de ítens distribuídos ou infraestruturas melhoradas
 Min.   :    0.0                                                                                           
 1st Qu.:    1.0                                                                                           
 Median :   12.0                                                                                           
 Mean   :  402.4                                                                                           
 3rd Qu.:  225.8                                                                                           
 Max.   :16000.0                                                                                           
 NA's   :169                                                                                               
 Province / Província / ADM1 PT ADM1 / PCODE       District / Distrito / ADM2  PT ADM2 / PCODE 2    
 Length:711                     Length:711         Length:711                     Length:711        
 Class :character               Class :character   Class :character               Class :character  
 Mode  :character               Mode  :character   Mode  :character               Mode  :character  
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
 Posto / ADM3 PT    School name / Nome da escola School code / Código de escola
 Length:711         Length:711                   Length:711                    
 Class :character   Class :character             Class :character              
 Mode  :character   Mode  :character             Mode  :character              
                                                                               
                                                                               
                                                                               
                                                                               
 Other specific location / Outro local específico
 Length:711                                      
 Class :character                                
 Mode  :character                                
                                                 
                                                 
                                                 
                                                 
 Type of beneficiary reached / Tipo de beneficiário alcançado Boys / Meninos (<18y)
 Length:711                                                   Min.   :   0.0       
 Class :character                                             1st Qu.:   3.0       
 Mode  :character                                             Median :  45.0       
                                                              Mean   : 255.8       
                                                              3rd Qu.: 157.0       
                                                              Max.   :7453.0       
                                                              NA's   :152          
 Girls / Meninas (<18y) Men / Homens (18 - 59y) Women / Mulheres (18 - 59y)
 Min.   :   0.0         Min.   :  0.000         Min.   :  0.000            
 1st Qu.:   3.0         1st Qu.:  0.000         1st Qu.:  0.000            
 Median :  60.0         Median :  0.000         Median :  0.000            
 Mean   : 240.2         Mean   :  2.106         Mean   :  2.401            
 3rd Qu.: 189.0         3rd Qu.:  2.000         3rd Qu.:  1.000            
 Max.   :8547.0         Max.   :103.000         Max.   :119.000            
 NA's   :152            NA's   :152             NA's   :152                
 Elderly men / Idosos (60+y) Elderly women / Idosas (60+y)
 Min.   :0.0000              Min.   :0.00000              
 1st Qu.:0.0000              1st Qu.:0.00000              
 Median :0.0000              Median :0.00000              
 Mean   :0.0161              Mean   :0.00179              
 3rd Qu.:0.0000              3rd Qu.:0.00000              
 Max.   :3.0000              Max.   :1.00000              
 NA's   :152                 NA's   :152                  
 Of which are male PWD /Número de pessoas masculinas com deficiência
 Min.   :0.0000                                                     
 1st Qu.:0.0000                                                     
 Median :0.0000                                                     
 Mean   :0.1056                                                     
 3rd Qu.:0.0000                                                     
 Max.   :7.0000                                                     
 NA's   :152                                                        
 Of which are female PWD / Número de pessoas femininas com deficiência Total male / Total masculino
 Min.   :0.00000                                                       Min.   :   0.0              
 1st Qu.:0.00000                                                       1st Qu.:   0.0              
 Median :0.00000                                                       Median :  21.0              
 Mean   :0.07168                                                       Mean   : 202.8              
 3rd Qu.:0.00000                                                       3rd Qu.:  94.5              
 Max.   :4.00000                                                       Max.   :7453.0              
 NA's   :153                                                                                       
 Total female / Total feminino Total people reached / Total de pessoas alcançadas
 Min.   :   0.0                Min.   :    0.0                                   
 1st Qu.:   0.0                1st Qu.:    2.0                                   
 Median :  24.0                Median :   50.0                                   
 Mean   : 190.7                Mean   :  393.5                                   
 3rd Qu.: 128.5                3rd Qu.:  230.0                                   
 Max.   :8547.0                Max.   :16000.0                                   
                                                                                 
 Child Participation / Participação da criança Comments / Comentários
 Length:711                                    Length:711            
 Class :character                              Class :character      
 Mode  :character                              Mode  :character      
                                                                     
                                                                     
                                                                     
                                                                     

Using glimpse

glimpse(dataset2) provides a transposed overview of the dataset2 dataset, showing the first few entries of each column and the data types of the columns.

glimpse(dataset)
Rows: 711
Columns: 40
$ Lead.organization...Organização.líder                                                                                                                         <chr> …
$ Organization.type...Tipo.de.organização                                                                                                                       <chr> …
$ Implementing.partner...Parceiro.de.implementação                                                                                                              <chr> …
$ Organization.type...Tipo.de.organização.1                                                                                                                     <chr> …
$ Consortium.Lead..in.case.the.activity.is.implemented.under.a.consortium....Líder.de.Consórcio..caso.a.actividade.seja.implementada.no.âmbito.de.um.consórcio. <chr> …
$ Donor...Doador                                                                                                                                                <chr> …
$ Period...Período                                                                                                                                              <chr> …
$ HRP.ou.non.HRP.project...Projeto.HRP.ou.não.HRP                                                                                                               <chr> …
$ Type.of.Disaster..Tipo.de.Desastre                                                                                                                            <chr> …
$ Sector...Setor                                                                                                                                                <chr> …
$ Cluster.objective...Objectivo.do.cluster                                                                                                                      <chr> …
$ Cluster.indicator...Indicador.do.cluster                                                                                                                      <chr> …
$ HRP.activity...Actividade.do.HRP                                                                                                                              <chr> …
$ Non.HRP.activity...Actividade.não.HRP                                                                                                                         <chr> …
$ Unit.of.measure...Unidade.de.medida                                                                                                                           <chr> …
$ Delivery.modality...Modalidade.de.intervenção                                                                                                                 <chr> …
$ Activity.status...Progresso.da.actividade                                                                                                                     <chr> …
$ X..of.items.distributed.or.infrastructures.improved.....de.ítens.distribuídos.ou.infraestruturas.melhoradas                                                   <dbl> …
$ Province...Província...ADM1.PT                                                                                                                                <chr> …
$ ADM1...PCODE                                                                                                                                                  <chr> …
$ District...Distrito...ADM2.PT                                                                                                                                 <chr> …
$ ADM2...PCODE.2                                                                                                                                                <chr> …
$ Posto...ADM3.PT                                                                                                                                               <chr> …
$ School.name...Nome.da.escola                                                                                                                                  <chr> …
$ School.code...Código.de.escola                                                                                                                                <chr> …
$ Other.specific.location...Outro.local.específico                                                                                                              <chr> …
$ Type.of.beneficiary.reached...Tipo.de.beneficiário.alcançado                                                                                                  <chr> …
$ Boys...Meninos...18y.                                                                                                                                         <dbl> …
$ Girls...Meninas...18y.                                                                                                                                        <dbl> …
$ Men...Homens..18...59y.                                                                                                                                       <dbl> …
$ Women...Mulheres..18...59y.                                                                                                                                   <dbl> …
$ Elderly.men...Idosos..60.y.                                                                                                                                   <dbl> …
$ Elderly.women...Idosas..60.y.                                                                                                                                 <dbl> …
$ Of.which.are.male.PWD..Número.de.pessoas.masculinas.com.deficiência                                                                                           <dbl> …
$ Of.which.are.female.PWD...Número.de.pessoas.femininas.com.deficiência                                                                                         <dbl> …
$ Total.male...Total.masculino                                                                                                                                  <dbl> …
$ Total.female...Total.feminino                                                                                                                                 <dbl> …
$ Total.people.reached...Total.de.pessoas.alcançadas                                                                                                            <dbl> …
$ Child.Participation...Participação.da.criança                                                                                                                 <chr> …
$ Comments...Comentários                                                                                                                                        <chr> …

Dataset preparation

Renaming columns

  1. rename_with(~make.unique(str_squish(str_remove(., "/.*"))), .cols = everything()): Removes everything after a slash in column names, squishes spaces, and ensures names are unique.
  2. rename_with(str_to_lower): Converts all column names to lowercase.
  3. rename_with(~str_replace_all(., " ", "_")): Replaces all spaces in column names with underscores to create valid variable names.

dataset_renamed <- dataset2 %>% 
  rename_with(~make.unique(str_squish(str_remove(., "/.*"))), .cols = everything()) %>%
  rename_with(str_to_lower) %>% 
  rename_with(~str_replace_all(., " ", "_"))

  #rename_with(~str_replace_all(str_to_lower(.), " ", "_"), .cols = everything())

Let’s check the column names now:

names(dataset_renamed)
 [1] "lead_organization"                                                       
 [2] "organization_type"                                                       
 [3] "implementing_partner"                                                    
 [4] "organization_type.1"                                                     
 [5] "consortium_lead_(in_case_the_activity_is_implemented_under_a_consortium)"
 [6] "donor"                                                                   
 [7] "period"                                                                  
 [8] "hrp_ou_non-hrp_project"                                                  
 [9] "type_of_disaster"                                                        
[10] "sector"                                                                  
[11] "cluster_objective"                                                       
[12] "cluster_indicator"                                                       
[13] "hrp_activity"                                                            
[14] "non-hrp_activity"                                                        
[15] "unit_of_measure"                                                         
[16] "delivery_modality"                                                       
[17] "activity_status"                                                         
[18] "#_of_items_distributed_or_infrastructures_improved"                      
[19] "province"                                                                
[20] "adm1"                                                                    
[21] "district"                                                                
[22] "adm2"                                                                    
[23] "posto"                                                                   
[24] "school_name"                                                             
[25] "school_code"                                                             
[26] "other_specific_location"                                                 
[27] "type_of_beneficiary_reached"                                             
[28] "boys"                                                                    
[29] "girls"                                                                   
[30] "men"                                                                     
[31] "women"                                                                   
[32] "elderly_men"                                                             
[33] "elderly_women"                                                           
[34] "of_which_are_male_pwd"                                                   
[35] "of_which_are_female_pwd"                                                 
[36] "total_male"                                                              
[37] "total_female"                                                            
[38] "total_people_reached"                                                    
[39] "child_participation"                                                     
[40] "comments"                                                                

Selecting columns

a) Keeping columns

Typing select(c(column_name1, etc.)), selects the columns. But this can be impractical if we need a lot of columns.

dataset_keep_cols <- dataset_renamed %>% 
  select(c(lead_organization,organization_type,implementing_partner,organization_type.1))

head(dataset_keep_cols)

b) Excluding columns

Removes specified columns by their names and positions from dataset_renamed, creating a new dataset dataset_filter_cols.

dataset_filter_cols <- dataset_renamed %>% 
  select(-c(5, donor, sector, hrp_activity, `non-hrp_activity`, 18, unit_of_measure, delivery_modality, comments))

names(dataset_filter_cols)
 [1] "lead_organization"           "organization_type"           "implementing_partner"       
 [4] "organization_type.1"         "period"                      "hrp_ou_non-hrp_project"     
 [7] "type_of_disaster"            "cluster_objective"           "cluster_indicator"          
[10] "activity_status"             "province"                    "adm1"                       
[13] "district"                    "adm2"                        "posto"                      
[16] "school_name"                 "school_code"                 "other_specific_location"    
[19] "type_of_beneficiary_reached" "boys"                        "girls"                      
[22] "men"                         "women"                       "elderly_men"                
[25] "elderly_women"               "of_which_are_male_pwd"       "of_which_are_female_pwd"    
[28] "total_male"                  "total_female"                "total_people_reached"       
[31] "child_participation"        

We can have another glimpse into the dataset:

glimpse(dataset_filter_cols)
Rows: 711
Columns: 31
$ lead_organization           <chr> "United Nations Children's Fund", "United Nations Children's Fund…
$ organization_type           <chr> "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN",…
$ implementing_partner        <chr> "United Nations Children's Fund", "United Nations Children's Fund…
$ organization_type.1         <chr> "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN", "UN", "GOV"…
$ period                      <chr> "March", "March", "March", "March", "March", "March", "March", "M…
$ `hrp_ou_non-hrp_project`    <chr> "HRP", "HRP", "HRP", "HRP", "HRP", "HRP", "HRP", "HRP", "HRP", "H…
$ type_of_disaster            <chr> "Conflict", "Conflict", "Conflict", "Conflict", "Conflict", "Conf…
$ cluster_objective           <chr> "To improve learning environments by ensuring access to improved …
$ cluster_indicator           <chr> "# of children benefitting from new or improved WASH infrastructu…
$ activity_status             <chr> "Ongoing", "Ongoing", "Ongoing", "Ongoing", "Completed", "Complet…
$ province                    <chr> "Nampula", "Nampula", "Nampula", "Nampula", "Nampula", "Nampula",…
$ adm1                        <chr> "MZ07", "MZ07", "MZ07", "MZ07", "MZ07", "MZ07", "MZ07", "MZ07", "…
$ district                    <chr> "Namapa - Eráti", "Namapa - Eráti", "Namapa - Eráti", "Namapa - E…
$ adm2                        <chr> "MZ0703", "MZ0703", "MZ0703", "MZ0703", "MZ0703", "MZ0703", "MZ07…
$ posto                       <chr> "Namapa - Eráti", "Namapa - Eráti", "Namapa - Eráti", "Namapa - E…
$ school_name                 <chr> "Escola Primária do 1º e 2º Grau de Nacucha", "Escola Primária do…
$ school_code                 <chr> "60076", "62132", "61622", "61733", "60076", "62132", "61622", "6…
$ other_specific_location     <chr> "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", …
$ type_of_beneficiary_reached <chr> "IDPs", "IDPs", "IDPs", "IDPs", "IDPs", "IDPs", "IDPs", "IDPs", "…
$ boys                        <dbl> 248, 16, 192, 2, 1205, 1118, 3309, 1205, 1118, 3309, 55, 25, 25, …
$ girls                       <dbl> 186, 23, 138, 0, 1207, 1140, 1790, 1207, 1140, 1790, 52, 14, 14, …
$ men                         <dbl> 6, 8, 16, 0, 6, 8, 16, 6, 8, 16, 4, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0,…
$ women                       <dbl> 25, 29, 26, 0, 25, 29, 26, 25, 29, 26, 1, 0, 0, 0, 3, 3, 0, 0, 0,…
$ elderly_men                 <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ elderly_women               <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ of_which_are_male_pwd       <dbl> 6, 4, 7, 0, 6, 4, 7, 6, 4, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ of_which_are_female_pwd     <dbl> 4, 2, 3, 0, 4, 2, 3, 4, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ total_male                  <dbl> 254, 24, 208, 2, 1211, 1126, 3325, 1211, 1126, 3325, 59, 27, 27, …
$ total_female                <dbl> 211, 52, 164, 0, 1232, 1169, 1816, 1232, 1169, 1816, 53, 14, 14, …
$ total_people_reached        <dbl> 465, 76, 372, 2, 2443, 2295, 5141, 2443, 2295, 5141, 112, 41, 41,…
$ child_participation         <chr> "Sim/Yes", "Sim/Yes", "Sim/Yes", "Sim/Yes", "Sim/Yes", "Sim/Yes",…

Removing empty rows

We can remove rows with empty values that we will not use, for example rows with no lead_organization value defined.

  • filter(!is.na(lead_organization)): Removes rows from dataset_filter_cols where the lead_organization column contains NA values.
dataset_no_empty_rows <- dataset_filter_cols %>% 
  filter(!is.na(lead_organization))

dim(dataset_no_empty_rows)
[1] 542  31

Converting period to date format

Let’s explore the type of data of the period column.

  • class(dataset_no_empty_rows$period) returns the data type or class of the period column in the dataset_no_empty_rows dataset

dataset_no_empty_rows$period)


class(dataset_no_empty_rows$period)
[1] "character"

It is of type “character”, let’s see its unique values using unique:

unique(dataset_no_empty_rows$period)
[1] "March"    "February" "January" 

We need to convert those month strings into a date format, so it will facilitate later on the ordering of results by chronological order. We will use functions from the lubridate package:

  • Converts period column values to date format by appending “2024” to each month name and using my() from lubridate.
  • class(dataset_period_date$period): Returns the class of the period column in dataset_period_date.
  • unique(dataset_period_date$period): Extracts unique date values from the period column in dataset_period_date.

dataset_period_date <- dataset_no_empty_rows %>% 
  mutate(period = my(paste(period, "2024")))

class(dataset_period_date$period)
[1] "Date"
unique(dataset_period_date$period)
[1] "2024-03-01" "2024-02-01" "2024-01-01"

Removing specific rows

Let’s continue by removing the ‘suspended’ and ‘planned’ activities using filter, so we can focus only on ongoing and completed activities. We take the previous cleaned dataframe dataset_period_date.

What different values does activity_status get?

  • unique(dataset_no_empty_rows$activity_status): Returns all unique values from the activity_status column of the dataset_no_empty_rows dataset, removing duplicates.
unique(dataset_period_date$activity_status)
[1] "Ongoing"   "Completed" "Planned"   "Suspended"

The code filters dataset_no_empty_rows to include only rows where the activity_status column has values “Ongoing” or “Completed”, and stores the result in dataset_no_suspended.

dataset_filter_activity <- dataset_period_date %>% 
  filter(activity_status %in% c("Ongoing", "Completed"))

dim(dataset_filter_activity)
[1] 537  31

Let’s check

unique(dataset_filter_activity$activity_status)
[1] "Ongoing"   "Completed"

“Piping” everything so far

We could have done all the previous steps at once by combining all the ‘pipes’ as follows:

  1. Renames columns by removing substrings after a slash, squishing spaces, making names unique
  2. Converting to lowercase
  3. Replacing spaces with underscores.
  4. Removes specified columns by index and name.
  5. Filters out rows where lead_organization is NA
  6. Converts period to 2024 date format
  7. Keeps only rows with activity_status “Ongoing” or “Completed”

dataset_filter_activity_all <- dataset2 %>% 
  rename_with(~make.unique(str_squish(str_remove(., "/.*"))), .cols = everything()) %>%
  rename_with(str_to_lower) %>% 
  rename_with(~str_replace_all(., " ", "_")) %>% 
  select(-c(5, donor, sector, hrp_activity, `non-hrp_activity`, 18, unit_of_measure, delivery_modality, comments)) %>% 
  filter(!is.na(lead_organization)) %>% 
  mutate(period = my(paste(period, "2024"))) %>% 
  filter(activity_status %in% c("Ongoing", "Completed"))
  
  dim(dataset_filter_activity_all)
[1] 537  31
  
  head(dataset_filter_activity_all)
NA

Exporting data as Excel


wb <- createWorkbook()
addWorksheet(wb, "clean_data", gridLines = FALSE)

writeDataTable(wb, sheet = 1, dataset_filter_activity_all, tableStyle = "TableStyleMedium9",  withFilter = TRUE)

saveWorkbook(wb, file = "output/clean_data.xlsx", overwrite = TRUE)

Aggregating data

Let’s see several examples of different aggregations. We will be using our latest result dataset_filter_activity_all, that contains all “ongoing” and “completed” activites

Reached people by period, province and district


reached_period_province_district_tbl <- dataset_filter_activity_all %>% 
  group_by(period, province, district) %>% 
  summarise(
    total_male = sum(total_male, na.rm = TRUE),
    total_female = sum(total_female, na.rm = TRUE), 
    total_reached = sum(total_people_reached, na.rm = TRUE))
`summarise()` has grouped output by 'period', 'province'. You can override using the `.groups`
argument.
  
print(reached_period_province_district_tbl)

We can also use the command across(), to apply the same operation over a collection of columns in a more compact way:

  • Groups dataset_filter_activity_all by period, province, and district.
  • Summarizes total_male, total_female, and total_people_reached for each group, removing NAs.
  • Stores result in reached_period_province_district_tbl.

reached_period_province_district_tbl <- dataset_filter_activity_all %>% 
  group_by(period, province, district) %>% 
  summarise(across(c(total_male, total_female, total_people_reached), ~ sum(., na.rm = TRUE)))
`summarise()` has grouped output by 'period', 'province'. You can override using the `.groups`
argument.
  
print(reached_period_province_district_tbl)

Reached people by indicator

For simplicity, we will assume that we can sum everything up to national level, and also across months

reached_indicator_national_tbl <- dataset_filter_activity_all %>% 
  group_by(cluster_indicator) %>% 
  summarise(total_reached = sum(total_people_reached, na.rm = TRUE))

reached_indicator_national_tbl

Reached people by indicator (a more complex situation)

Let’s see which indicator we have in the dataset:

unique(dataset_filter_activity_all$cluster_indicator)
[1] "# of children benefitting from new or improved WASH infrastructures (drinking water pumps, latrines, etc.) in schools"                                                                                                                                         
[2] "# of children, affect by emergencies accessing formal or non-formal education disaggregated by gender, age and disability"                                                                                                                                     
[3] "# of teachers and education personnel, officials trained in emergency and related topics (MHPSS, Peace Education, DRR, PEBE, climate change, basic pedagogy and learner-centred methodologies, gender socialization, VAC/GBV prevention and referral pathways)"

Let’s assume now that for the [2] indicator, we have take the maximum across months instead the sum because it was defined like that. We need then to apply different aggregation depending on the indicator. A way to do can be:

  • Groups dataset_filter_activity_all by period and cluster_indicator, calculates the sum of total_people_reached for each group.
  • Ungroups the data.
  • Groups by cluster_indicator again, applies different aggregation based on the content of cluster_indicator (max for those containing “formal”, sum for others).
  • Ungroups and removes duplicates
  • Results stored in reached_indicator_national_sum_max_tbl.

In this case, the summarise function generated several repeated rows, as different aggregation paths (like sum and max) could potentially create rows with identical values across the columns being analyzed. We use distinct() to remove the duplicates.

reached_indicator_national_sum_max_tbl <- dataset_filter_activity_all %>% 
  group_by(period, cluster_indicator) %>% 
  summarise(total_reached = sum(total_people_reached, na.rm = TRUE)) %>% 
  ungroup() %>% 
  group_by(cluster_indicator) %>% 
  summarise(total_reached = if_else(grepl("formal", cluster_indicator),
                                    max(total_reached, na.rm = TRUE),
                                    sum(total_reached, na.rm = TRUE))) %>% 
  ungroup() %>% 
  distinct()
`summarise()` has grouped output by 'period'. You can override using the `.groups` argument.
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
ℹ Please use `reframe()` instead.
ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an
  ungrouped data frame and adjust accordingly.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
`summarise()` has grouped output by 'cluster_indicator'. You can override using the `.groups`
argument.
reached_indicator_national_sum_max_tbl 

Reached people by month

  • Groups dataset_filter_activity_all by period.
  • Summarizes total_people_reached into total_reached, ignoring NAs.
  • Adds a month column with month names extracted from period.
  • Selects and rearranges columns to period, month, total_reached.
  • Results stored in reached_period_national_tbl.

reached_month_national_tbl <- dataset_filter_activity_all %>% 
  group_by(period) %>% 
  summarise(total_reached = sum(total_people_reached, na.rm = TRUE)) %>% 
  mutate(month = month(period, label = TRUE)) %>% 
  select(period, month, total_reached)


print(reached_month_national_tbl)

Reached people by month and indicator


reached_month_indicator_national_tbl <- dataset_filter_activity_all %>% 
  group_by(period, cluster_indicator) %>% 
  summarise(total_reached = sum(total_people_reached, na.rm = TRUE)) %>% 
  mutate(month = month(period, label = TRUE)) %>% 
  select(period, month, indicator = cluster_indicator, total_reached)
`summarise()` has grouped output by 'period'. You can override using the `.groups` argument.
print(reached_month_indicator_national_tbl)

Reached people by province

reached_province_tbl <- dataset_filter_activity_all %>% 
  group_by(province, adm1) %>% 
  summarise(total_reached = sum(total_people_reached, na.rm = TRUE)) %>% 
  select(province, adm1, total_reached)
`summarise()` has grouped output by 'province'. You can override using the `.groups` argument.
print(reached_province_tbl)

Reached people by month and province


reached_month_province_tbl <- dataset_filter_activity_all %>% 
  group_by(period, province, adm1) %>% 
  summarise(total_reached = sum(total_people_reached, na.rm = TRUE)) %>% 
  ungroup() %>% 
  mutate(month = month(period, label = TRUE)) %>% 
  select(month, province, adm1, total_reached)
`summarise()` has grouped output by 'period', 'province'. You can override using the `.groups`
argument.
print(reached_month_province_tbl)

Adding totals using janitor() package

While we can calculate table totals (both across row and by column) using dplyr, there is also some packages that facilitate this task, for example the janitor() package. Let’s see an example with the indicators reached_indicator_national_tbl dataframe we obtained before:

  • Adds a total row to the reached_indicator_national_tbl, summing values across the value column, using adorn_totals(where = "row").
  • Stores the resulting table with totals in reached_indicator_national_total_tbl.

reached_indicator_national_total_tbl <- reached_indicator_national_tbl %>% 
  adorn_totals(where = "row")

print(reached_indicator_national_total_tbl)
                                                                                                                                                                                                                                              cluster_indicator
                                                                                                                                          # of children benefitting from new or improved WASH infrastructures (drinking water pumps, latrines, etc.) in schools
                                                                                                                                      # of children, affect by emergencies accessing formal or non-formal education disaggregated by gender, age and disability
 # of teachers and education personnel, officials trained in emergency and related topics (MHPSS, Peace Education, DRR, PEBE, climate change, basic pedagogy and learner-centred methodologies, gender socialization, VAC/GBV prevention and referral pathways)
                                                                                                                                                                                                                                                          Total
 total_reached
         28647
        238213
         11383
        278243

Formatting tables with kable() and kableExtra() packages

We can also format the resulting table for publishing, using the kable() and kableExtra() packages:

reached_period_province_district_tbl %>% kable() %>% kable_classic()
period province district total_male total_female total_people_reached
2024-01-01 Cabo Delgado Ancuabe 1172.0 869.0 2041
2024-01-01 Cabo Delgado Chiúre 375.0 276.0 651
2024-01-01 Cabo Delgado Cidade de Pemba 110.0 190.0 300
2024-01-01 Cabo Delgado Ibo 130.0 132.0 262
2024-01-01 Cabo Delgado Metuge 1565.0 1713.0 3278
2024-01-01 Cabo Delgado Mocímboa da Praia 8.0 2.0 10
2024-01-01 Cabo Delgado Mueda 8.0 7.0 15
2024-01-01 Cabo Delgado Pemba - Metuge 107.0 141.0 248
2024-01-01 Cabo Delgado Quissanga 72.0 58.0 130
2024-01-01 Nampula Memba 223.0 255.0 478
2024-02-01 Cabo Delgado Ancuabe 6544.0 7385.0 13929
2024-02-01 Cabo Delgado Cidade de Pemba 2938.0 4198.0 7136
2024-02-01 Cabo Delgado Ibo 113.0 116.0 229
2024-02-01 Cabo Delgado Meluco 1101.6 1652.4 2754
2024-02-01 Cabo Delgado Metuge 4705.0 4547.0 9252
2024-02-01 Cabo Delgado Montepuez 1481.0 2052.0 3533
2024-02-01 Cabo Delgado Mueda 388.0 647.0 1035
2024-02-01 Cabo Delgado Muidumbe 1496.4 2244.6 3741
2024-02-01 Cabo Delgado Palma 886.0 1329.0 2215
2024-02-01 Nampula Memba 349.0 377.0 726
2024-03-01 Cabo Delgado Ancuabe 5067.0 3609.0 8676
2024-03-01 Cabo Delgado Chiúre 1588.0 1574.0 3162
2024-03-01 Cabo Delgado Cidade de Pemba 354.0 459.0 813
2024-03-01 Cabo Delgado Macomia 6646.0 5810.0 12456
2024-03-01 Cabo Delgado Metuge 0.0 1143.0 1143
2024-03-01 Cabo Delgado Mocimboa Da Praia 3043.0 2772.0 5815
2024-03-01 Cabo Delgado Mocímboa da Praia 587.0 593.0 1180
2024-03-01 Cabo Delgado Montepuez 20345.0 20613.0 40958
2024-03-01 Cabo Delgado Mueda 5509.0 5037.0 10546
2024-03-01 Cabo Delgado Nangade 258.0 258.0 516
2024-03-01 Cabo Delgado Palma 767.0 697.0 1464
2024-03-01 Cabo Delgado Pemba - Metuge 10431.0 6966.0 17397
2024-03-01 Nampula Memba 864.0 920.0 1784
2024-03-01 Nampula Namapa - Eráti 11812.0 8861.0 20673
2024-03-01 Zambézia Inhassungue 5904.0 4596.0 10500
2024-03-01 Zambézia Maganja da Costa 13955.0 11556.0 25511
2024-03-01 Zambézia Mopeia 5985.0 4515.0 10500
2024-03-01 Zambézia Namacurra 13555.0 13631.0 27186
2024-03-01 Zambézia Nicoadala 7453.0 8547.0 16000
2024-03-01 Zambézia Quelimane 5482.0 4518.0 10000

Plotting with ggplot

ggplot2 is a data visualization package for the R programming language. It’s based on the grammar of graphics, providing a system for declaratively creating graphics based on the idea of building up a plot using layers. The plot layers are connected using the sign +

Simple barplot

This code snippet sets up a ggplot object with the aesthetics defined for x-axis (province), y-axis (total reached), and bar fill color (also by province), and then adds a column geometry to render a bar for each province. The fill aesthetic ensures that each province’s bar is colored differently, aiding in distinguishing between them visually in the plot.


ggplot(reached_province_tbl, aes(x = province, y = total_reached, fill = province)) +
  geom_col()

We can also ‘pipe’ the dataframe and send it to ggplot:


reached_province_tbl %>% ggplot(aes(x = province, y = total_reached, fill = province)) +
  geom_col()

If we don’t want to use the plot at the moment of creation, it can be also store in a variable for later print or saving:

Customizing the chart

This code creates a bar chart using ggplot2:

  • aes(x = province, y = total_reached, fill = province): Maps province to the x-axis and the fill color of the bars, and total_reached to the y-axis.
  • geom_col(): Plots bars for each province with heights determined by total_reached.
  • scale_fill_manual(values = UNICEF_PALETTE): Applies custom colors from UNICEF_PALETTE to the bars, one for each province.
  • theme_minimal(): Uses a minimal theme for a clean presentation.
  • labs(): Sets labels for the x-axis, y-axis, and a title for the chart.

p <- ggplot(reached_province_tbl, aes(x = province, y = total_reached, fill = province)) +
  geom_col() +
  scale_fill_manual(values = UNICEF_PALETTE) +
  theme_minimal() +
  labs(x = "Province", y = "Total Reached", title = "Total Reached by Province")

p

Flipped barplot

Usually, we can flip any chart by swapping the variables in the x and y axis as follows:

Alternatively, we can also use the command coord_flip():

p <- ggplot(reached_province_tbl, aes(x = reorder(province, total_reached), y = total_reached, fill = province)) +
  geom_col() +
  coord_flip() +
  scale_fill_manual(values = UNICEF_PALETTE) +
  theme_minimal() +
  labs(x = "Province", y = "Total Reached", title = "Total Reached by Province")

p

Stacked barplot

We are grouping month results in the same bar:

  • ggplot(reached_month_province_tbl, aes(x = province, y = total_reached, fill = month)): Initializes a ggplot graph using reached_month_province_tbl data, setting province as the x-axis, total_reached as the y-axis, and month to color the bars.
  • geom_col(): Adds columns to the plot, with heights representing total_reached values.
  • scale_fill_manual(values = UNICEF_PALETTE): Customizes the color palette of the fill (month) using predefined colors in UNICEF_PALETTE.
  • theme_minimal(): Applies a minimalistic theme to the plot, reducing visual noise.
  • labs(x = "Province", y = "Total Reached", title = "Total Reached by Province and Month"): Sets labels for the x-axis, y-axis, and the plot title.

p <- ggplot(reached_month_province_tbl, aes(x = province, y = total_reached, fill = month)) +
  geom_col() + 
  scale_fill_manual(values = UNICEF_PALETTE) +
  theme_minimal() +
  labs(x = "Province", y = "Total Reached", title = "Total Reached by Province and Month")

p

Grouped barplot

We are grouping by month as before, but in this case we want separate contiguous bars. We control this with geom_col(position = position_dodge()):

  • ggplot(reached_month_province_tbl, aes(x = province, y = total_reached, fill = month)): Sets up a ggplot with province as the x-axis, total_reached as the y-axis, and different colors for each month.
  • geom_col(position = position_dodge()): Adds columns to the plot, separating them by month to avoid overlap and make comparisons easier.
  • scale_fill_manual(values = UNICEF_PALETTE): Customizes the fill colors using the UNICEF_PALETTE.
  • theme_minimal(): Applies a minimalistic theme, reducing visual clutter.
  • labs(x = "Province", y = "Total Reached", title = "Total Reached by Province and Month"): Sets labels and title for clarity.
p <- ggplot(reached_month_province_tbl, aes(x = province, y = total_reached, fill = month)) +
  geom_col(position = position_dodge()) + 
  scale_fill_manual(values = UNICEF_PALETTE) +
  theme_minimal() +
  labs(x = "Province", y = "Total Reached", title = "Total Reached by Province and Month")

p

Lollipop plot

This is a useful plot that can be constructed by combining different ggplot elements:

  • ggplot(reached_province_tbl, aes(x=province, y=total_reached)): Initializes a ggplot graph using reached_province_tbl, mapping province to the x-axis and total_reached to the y-axis.
  • geom_segment(aes(x=province, xend=province, y=0, yend=total_reached, color=province)): Adds vertical line segments from y=0 to total_reached for each province, colored by province.
  • geom_point(aes(color=province), size=4): Adds points at the end of each segment, colored by province and with a specified size.
  • coord_flip(): Flips the x and y axes, making the bars horizontal.
  • scale_color_manual(values = UNICEF_PALETTE): Sets the colors for the different provinces using UNICEF_PALETTE.
  • theme_minimal(): Applies a minimal theme to reduce visual clutter.

p <- ggplot(reached_province_tbl, aes(x=province, y=total_reached)) +
  geom_segment( aes(x=province, xend=province, y=0, yend=total_reached, color=province)) +
  geom_point(aes(color=province), size=4) +
  coord_flip() +
  scale_color_manual(values = UNICEF_PALETTE) +
  theme_minimal()

p

Time series: Dots

We will now be plotting time series data in different ways. In here it was useful to transform the period column into a date format, so we could derive the month variable in the right chronological order (if not, the month would appear ordered alphabetically)

  • ggplot(reached_month_national_tbl, aes(x = month, y = total_reached)): Initializes a ggplot graph with month on the x-axis and total_reached on the y-axis.
  • geom_point(alpha=1, shape=20, color=UNICEF_PALETTE[1], size=15): Adds points representing each data entry. Each point is fully opaque (alpha=1), uses a filled circle shape (shape=20), colored with the first color of the UNICEF_PALETTE, and is large (size=15).
  • geom_text(aes(label = total_reached), size = 2, color = "white", fontface="bold"): Adds text labels to each point displaying total_reached, with small white bold text (size=2).
  • theme_minimal(): Applies a minimal theme to reduce visual clutter.
p <- ggplot(reached_month_national_tbl, aes(x = month, y = total_reached)) +
  geom_point(alpha=1, shape=20, color=UNICEF_PALETTE[1], size=15) +
  geom_text(aes(label = total_reached), size = 2, color = "white", fontface="bold") +
  theme_minimal()

print(p)

Time series: Line plot with dots

We can joint the dots with a line plot to enhance the visualization:

  • ggplot(reached_month_national_tbl, aes(x = month, y = total_reached, group=1)): Sets up a ggplot for reached_month_national_tbl with month on the x-axis, total_reached on the y-axis, and all data considered part of the same group (enabling line connectivity).
  • geom_line(size = 0.5, color = UNICEF_PALETTE[1]): Adds a line connecting the data points, with a specified color from the UNICEF_PALETTE and thickness.
  • geom_point(shape=20, color=UNICEF_PALETTE[1], size=15): Adds large, solid circle points at each data point, matching the line color.
  • geom_text(aes(label = total_reached), size = 2, color = "white", fontface="bold"): Labels each point with its total_reached value in bold, white text.
  • theme_minimal(): Applies a minimalistic theme for a clean appearance.

p <- ggplot(reached_month_national_tbl, aes(x = month, y = total_reached, group=1)) +
  geom_line(size = 0.5, color = UNICEF_PALETTE[1]) +
  geom_point(shape=20, color=UNICEF_PALETTE[1], size=15) +
  geom_text(aes(label = total_reached), size = 2, color = "white", fontface="bold") +
  theme_minimal()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
print(p)

Time series: Multi-line plots

We can use a grouping variable, in this case province, to plot several lineplots across time:

  • ggplot(reached_month_province_tbl, aes(x = month, y = total_reached, group = province, color = province)): Initializes a ggplot with month on the x-axis, total_reached on the y-axis, and lines grouped and colored by province.
  • geom_line(size = 0.5): Adds lines to connect data points within each province group, with a specified line thickness.
  • geom_point(alpha=0.7, shape=20, size=12): Adds semi-transparent circle points at each data point, with specified size and opacity.
  • geom_text(aes(label = total_reached), size = 1.5, color = "white", fontface="bold"): Labels each point with its total_reached value in bold, white text.
  • scale_color_manual(values = UNICEF_PALETTE): Customizes the line colors using the UNICEF_PALETTE.
  • theme_minimal(): Applies a minimal theme to reduce visual clutter.
  • theme(legend.position = "bottom"): Moves the legend to the bottom of the plot for better visibility and layout.

multiline_plot <- ggplot(reached_month_province_tbl, aes(x = month, y = total_reached, group = province, color = province)) +
  geom_line(size = 0.5) +
  geom_point(alpha=0.7, shape=20, size=12) +
  geom_text(aes(label = total_reached), size = 1.5, color = "white", fontface="bold") +
  scale_color_manual(values = UNICEF_PALETTE) +  # Customize the colors
  theme_minimal() +
  theme(legend.position = "bottom")

print(multiline_plot)

Saving a plot

Let’s save the latest plot and export it as a PDF. We only need to write the path and name to the file, and choose the right extension ‘.pdf’. It will be automatically saved as PDF:


ggsave(plot = multiline_plot, filename = "plots/multiline.pdf", width = 12, height = 8)

Alternativelly, we can also save it as PNG following the same approach

LS0tCnRpdGxlOiAiUiBUcmFpbmlnIC0gUHJhY3RpY2FsIENhc2UgV2FsdGhyb3VnaCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgUmVzb3VyY2VzCgotICAgKipSIGZvciBSZXByb2R1Y2libGUgU2NpZW50aWZpYyBBbmFseXNpcyAoKio8aHR0cHM6Ly9zd2NhcnBlbnRyeS5naXRodWIuaW8vci1ub3ZpY2UtZ2FwbWluZGVyLz4pOiBBIHZlcnkgcmVjb21tZW5kYWJsZSBhbmQgZWFzeSB0byBmb2xsb3cgdHV0b3JpYWwgdGhhdCBjb3ZlcnMgdGhlIGJhc2ljcyB3aXRoIG5pY2UgZXhhbXBsZXMgb2YgdXNlCgotICAgKipJbnRlcmFjdGl2ZSBUdXRvcmlhbCBUb29sKiogaW5jbHVkZWQgaW4gUiBTdHVkaW8gKG9uIHRoZSByaWdodCB0b3Agd2luZG93KS4gSXQgd2lsbCBhc2sgeW91IHBlcm1pc2lvbiB0byBpbnN0YWxsIHNvbWUgbmVlZGVkIHBhY2thZ2VzLgoKLSAgIFRoZSAqKlIgR3JhcGggR2FsbGVyeSoqICg8aHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tPik6IEEgY29tcHJlaGVuc2l2ZSByZXNvdXJjZSB3aXRoIGh1bmRyZWRzIG9mIHR1dG9yaWFscyBhbmQgZXhhbXBsZXMgb24gY3JlYXRpbmcgYW1hemluZyB2aXN1YWxpemF0aW9ucyB1c2luZyBSIGFuZCBmb2N1c2luZyBvbiBnZ3Bsb3QKCiMjIEluc3RhbGwgcGFja2FnZXMgKGZvciB0aGlzIHRyYWluaW5nKQoKVGhpcyBvbmx5IG5lZWRzIHRvIGJlIGRvbmUgb25jZSBpbiB5b3VyIHN5c3RlbToKCmBgYHtyfQoKaW5zdGFsbC5wYWNrYWdlcyhjKCJ0aWR5dmVyc2UiLCAib3Blbnhsc3giLCAia25pdHIiLCAiamFuaXRvciIsICJrYWJsZSIsICJrYWJsZUV4dHJhIikpCmBgYAoKIyMgTG9hZCBsaWJyYXJpZXMKCldlIGxvYWQgdGhlIGxpYnJhcmllcyB3ZSB3aWxsIHVzZSBpbiBvdXIgcHJvamVjdC4KCi0gICBgbGlicmFyeSh0aWR5dmVyc2UpYDogQSBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgZGVzaWduZWQgZm9yIGRhdGEgc2NpZW5jZSB0aGF0IGhlbHBzIGluIGRhdGEgbWFuaXB1bGF0aW9uLCB2aXN1YWxpemF0aW9uLCBhbmQgYW5hbHlzaXMuCi0gICBgbGlicmFyeShyZWFkeGwpYDogUHJvdmlkZXMgc3RyYWlnaHRmb3J3YXJkIHRvb2xzIHRvIHJlYWQgRXhjZWwgZmlsZXMgKC54bHMgYW5kIC54bHN4KSBpbnRvIFIgd2l0aG91dCBkZXBlbmRlbmNpZXMuCi0gICBgbGlicmFyeShvcGVueGxzeClgOiBFbmFibGVzIHRoZSByZWFkaW5nLCB3cml0aW5nLCBhbmQgZWRpdGluZyBvZiAueGxzeCBmaWxlcyBpbiBSLCB3aXRoIG1vcmUgZmxleGliaWxpdHkgYW5kIG9wdGlvbnMgdGhhbiByZWFkeGwuCi0gICBsaWJyYXJ5KGphbml0b3IpYGAgOiBMb2FkcyB0aGUgYGphbml0b3JgIHBhY2thZ2UsIHVzZWZ1bCBmb3IgZGF0YSBjbGVhbmluZyB0YXNrcyBsaWtlIHNpbXBsaWZ5aW5nIGNvbHVtbiBuYW1lcyBhbmQgYWRkaW5nIHRvdGFscyB0byB0YWJsZXMuIGBgCi0gICBgbGlicmFyeShrbml0cilgOiBMb2FkcyB0aGUgYGtuaXRyYCBwYWNrYWdlLCB3aGljaCBpcyB1c2VkIGZvciBkeW5hbWljIHJlcG9ydCBnZW5lcmF0aW9uIGluIFIsIGludGVncmF0aW5nIGNvZGUgYW5kIG91dHB1dCBpbnRvIHJpY2ggZG9jdW1lbnRzLgotICAgYGxpYnJhcnkoa2FibGVFeHRyYSlgOiBMb2FkcyB0aGUgYGthYmxlRXh0cmFgIHBhY2thZ2UsIHdoaWNoIGVuaGFuY2VzIGBrbml0cmAncyBga2FibGUoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIG1vcmUgY29tcGxleCBIVE1MIG9yIExhVGVYIHRhYmxlcyB3aXRoIGFkZGl0aW9uYWwgZm9ybWF0dGluZyBvcHRpb25zLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShvcGVueGxzeCkKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmBgYAoKIyMgQ29uZmlndXJhdGlvbgoKSGVyZSB3ZSBkZWZpbmUgYSBzaW1wbGUgdmVjdG9yIHRoYXQgY29udGFpbnMgdGhlIFVOSUNFRiBwYWxldHRlIG9mIGNvbG9ycywgZm9yIGxhdGVyIHVzZSBpbiBwbG90czoKCmBgYHtyfQpVTklDRUZfUEFMRVRURSA8LSBjKCIjMDBBRUVGIiwiIzAwODMzRCIsICIjODBCRDQxIiwgIiNGRkMyMEUiLCAiI0YyNkEyMSIsICIjRTIyMzFBIiwgIiM5NjFBNDkiLCAiIzZBMUU3NCIsICIjRDhEMUM5IiwgIiM3Nzc3NzkiLCAiIzJEMjkyNiIsICIjMzc0RUEyIikKCmBgYAoKIyMgSW1wb3J0IGRhdGEKCiMjIyBVc2luZyBgb3Blbnhsc3hgIGxpYnJhcnkKCmBgYHtyfQoKZGF0YXNldDEgPC0gcmVhZC54bHN4KCJkYXRhL0VkdWNhdGlvbiAtIE1PWl9FZHUgQ2x1c3Rlcl81V3NfUTEyMDI0Lnhsc3giLCBzaGVldCA9ICJEYXRhX0VudHJ5XzVXIiwgY2hlY2submFtZXMgPSBUUlVFLCBkZXRlY3REYXRlcyA9IFRSVUUsIHN0YXJ0Um93ID0gNSkKYGBgCgpMZXQncyBjaGVjayB0aGUgY29sdW1uIG5hbWVzOgoKYGBge3J9CgpuYW1lcyhkYXRhc2V0MSkKYGBgCgojIyMgVXNpbmcgYHJlYWR4bGAgbGlicmFyeQoKYGBge3J9CmRhdGFzZXQyIDwtIHJlYWRfZXhjZWwoImRhdGEvRWR1Y2F0aW9uIC0gTU9aX0VkdSBDbHVzdGVyXzVXc19RMTIwMjQueGxzeCIsIHNoZWV0ID0gIkRhdGFfRW50cnlfNVciLCBza2lwID0gNCkKCm5hbWVzKGRhdGFzZXQyKQpgYGAKCiMjIFF1aWNrIGV4cGxvcmF0aW9uCgpXZSB3aWxsIHVzZSBgZGF0YXNldDJgIGluIHRoZSBleGFtcGxlLgoKIyMjIEdldCBkYXRhc2V0IGRpbWVuc2lvbnMKCi0gICBgbnJvdyhkYXRhc2V0MilgOiBSZXR1cm5zIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgYGRhdGFzZXQyYCBkYXRhc2V0LgotICAgYG5jb2woZGF0YXNldDIpYDogUmV0dXJucyB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgaW4gdGhlIGBkYXRhc2V0MmAgZGF0YXNldC4KLSAgIGBkaW0oZGF0YXNldDIpYDogUmV0dXJucyBhIHZlY3RvciBjb250YWluaW5nIHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucyBpbiB0aGUgYGRhdGFzZXQyYCBkYXRhc2V0LgoKYGBge3J9Cgpucm93KGRhdGFzZXQyKQpuY29sKGRhdGFzZXQyKQpkaW0oZGF0YXNldDIpCmBgYAoKIyMjIFVzaW5nIGBoZWFkYAoKYGhlYWQoZGF0YXNldDIpYCBkaXNwbGF5cyB0aGUgZmlyc3Qgc2l4IHJvd3Mgb2YgdGhlIGBkYXRhc2V0MmAgZGF0YXNldC4KCmBgYHtyfQoKaGVhZChkYXRhc2V0MikKYGBgCgojIyMgVXNpbmcgYHN1bW1hcnlgCgoqKmBzdW1tYXJ5KGRhdGFzZXQyKWAqKiBwcm92aWRlcyBhIHN0YXRpc3RpY2FsIHN1bW1hcnkgb2YgZWFjaCB2YXJpYWJsZSBpbiB0aGUgKipgZGF0YXNldDJgKiogZGF0YXNldCwgaW5jbHVkaW5nIG1lYXN1cmVzIGxpa2UgbWVhbiwgbWVkaWFuLCBxdWFydGlsZXMsIG1pbmltdW0sIGFuZCBtYXhpbXVtIHZhbHVlcy4KCmBgYHtyfQpzdW1tYXJ5KGRhdGFzZXQyKQpgYGAKCiMjIyBVc2luZyBgZ2xpbXBzZWAKCmBnbGltcHNlKGRhdGFzZXQyKWAgcHJvdmlkZXMgYSB0cmFuc3Bvc2VkIG92ZXJ2aWV3IG9mIHRoZSBgZGF0YXNldDJgIGRhdGFzZXQsIHNob3dpbmcgdGhlIGZpcnN0IGZldyBlbnRyaWVzIG9mIGVhY2ggY29sdW1uIGFuZCB0aGUgZGF0YSB0eXBlcyBvZiB0aGUgY29sdW1ucy4KCmBgYHtyfQpnbGltcHNlKGRhdGFzZXQpCmBgYAoKIyMgRGF0YXNldCBwcmVwYXJhdGlvbgoKIyMjIFJlbmFtaW5nIGNvbHVtbnMKCjEuICBgcmVuYW1lX3dpdGgofm1ha2UudW5pcXVlKHN0cl9zcXVpc2goc3RyX3JlbW92ZSguLCAiLy4qIikpKSwgLmNvbHMgPSBldmVyeXRoaW5nKCkpYDogUmVtb3ZlcyBldmVyeXRoaW5nIGFmdGVyIGEgc2xhc2ggaW4gY29sdW1uIG5hbWVzLCBzcXVpc2hlcyBzcGFjZXMsIGFuZCBlbnN1cmVzIG5hbWVzIGFyZSB1bmlxdWUuCjIuICBgcmVuYW1lX3dpdGgoc3RyX3RvX2xvd2VyKWA6IENvbnZlcnRzIGFsbCBjb2x1bW4gbmFtZXMgdG8gbG93ZXJjYXNlLgozLiAgYHJlbmFtZV93aXRoKH5zdHJfcmVwbGFjZV9hbGwoLiwgIiAiLCAiXyIpKWA6IFJlcGxhY2VzIGFsbCBzcGFjZXMgaW4gY29sdW1uIG5hbWVzIHdpdGggdW5kZXJzY29yZXMgdG8gY3JlYXRlIHZhbGlkIHZhcmlhYmxlIG5hbWVzLgoKYGBge3J9CgpkYXRhc2V0X3JlbmFtZWQgPC0gZGF0YXNldDIgJT4lIAogIHJlbmFtZV93aXRoKH5tYWtlLnVuaXF1ZShzdHJfc3F1aXNoKHN0cl9yZW1vdmUoLiwgIi8uKiIpKSksIC5jb2xzID0gZXZlcnl0aGluZygpKSAlPiUKICByZW5hbWVfd2l0aChzdHJfdG9fbG93ZXIpICU+JSAKICByZW5hbWVfd2l0aCh+c3RyX3JlcGxhY2VfYWxsKC4sICIgIiwgIl8iKSkKCiAgI3JlbmFtZV93aXRoKH5zdHJfcmVwbGFjZV9hbGwoc3RyX3RvX2xvd2VyKC4pLCAiICIsICJfIiksIC5jb2xzID0gZXZlcnl0aGluZygpKQoKYGBgCgpMZXQncyBjaGVjayB0aGUgY29sdW1uIG5hbWVzIG5vdzoKCmBgYHtyfQpuYW1lcyhkYXRhc2V0X3JlbmFtZWQpCmBgYAoKIyMjIFNlbGVjdGluZyBjb2x1bW5zCgojIyMgYSkgS2VlcGluZyBjb2x1bW5zCgpUeXBpbmcgYHNlbGVjdChjKGNvbHVtbl9uYW1lMSwgZXRjLikpYCwgc2VsZWN0cyB0aGUgY29sdW1ucy4gQnV0IHRoaXMgY2FuIGJlIGltcHJhY3RpY2FsIGlmIHdlIG5lZWQgYSBsb3Qgb2YgY29sdW1ucy4KCmBgYHtyfQpkYXRhc2V0X2tlZXBfY29scyA8LSBkYXRhc2V0X3JlbmFtZWQgJT4lIAogIHNlbGVjdChjKGxlYWRfb3JnYW5pemF0aW9uLG9yZ2FuaXphdGlvbl90eXBlLGltcGxlbWVudGluZ19wYXJ0bmVyLG9yZ2FuaXphdGlvbl90eXBlLjEpKQoKaGVhZChkYXRhc2V0X2tlZXBfY29scykKYGBgCgojIyMgYikgRXhjbHVkaW5nIGNvbHVtbnMKClJlbW92ZXMgc3BlY2lmaWVkIGNvbHVtbnMgYnkgdGhlaXIgbmFtZXMgYW5kIHBvc2l0aW9ucyBmcm9tIGBkYXRhc2V0X3JlbmFtZWRgLCBjcmVhdGluZyBhIG5ldyBkYXRhc2V0IGBkYXRhc2V0X2ZpbHRlcl9jb2xzYC4KCmBgYHtyfQpkYXRhc2V0X2ZpbHRlcl9jb2xzIDwtIGRhdGFzZXRfcmVuYW1lZCAlPiUgCiAgc2VsZWN0KC1jKDUsIGRvbm9yLCBzZWN0b3IsIGhycF9hY3Rpdml0eSwgYG5vbi1ocnBfYWN0aXZpdHlgLCAxOCwgdW5pdF9vZl9tZWFzdXJlLCBkZWxpdmVyeV9tb2RhbGl0eSwgY29tbWVudHMpKQoKbmFtZXMoZGF0YXNldF9maWx0ZXJfY29scykKCmBgYAoKV2UgY2FuIGhhdmUgYW5vdGhlciBgZ2xpbXBzZWAgaW50byB0aGUgZGF0YXNldDoKCmBgYHtyfQpnbGltcHNlKGRhdGFzZXRfZmlsdGVyX2NvbHMpCmBgYAoKIyMjIFJlbW92aW5nIGVtcHR5IHJvd3MKCldlIGNhbiByZW1vdmUgcm93cyB3aXRoIGVtcHR5IHZhbHVlcyB0aGF0IHdlIHdpbGwgbm90IHVzZSwgZm9yIGV4YW1wbGUgcm93cyB3aXRoIG5vIGBsZWFkX29yZ2FuaXphdGlvbmAgdmFsdWUgZGVmaW5lZC4KCi0gICBgZmlsdGVyKCFpcy5uYShsZWFkX29yZ2FuaXphdGlvbikpYDogUmVtb3ZlcyByb3dzIGZyb20gYGRhdGFzZXRfZmlsdGVyX2NvbHNgIHdoZXJlIHRoZSBgbGVhZF9vcmdhbml6YXRpb25gIGNvbHVtbiBjb250YWlucyBOQSB2YWx1ZXMuCgpgYGB7cn0KZGF0YXNldF9ub19lbXB0eV9yb3dzIDwtIGRhdGFzZXRfZmlsdGVyX2NvbHMgJT4lIAogIGZpbHRlcighaXMubmEobGVhZF9vcmdhbml6YXRpb24pKQoKZGltKGRhdGFzZXRfbm9fZW1wdHlfcm93cykKYGBgCgojIyMgQ29udmVydGluZyBgcGVyaW9kYCB0byBkYXRlIGZvcm1hdAoKTGV0J3MgZXhwbG9yZSB0aGUgdHlwZSBvZiBkYXRhIG9mIHRoZSBwZXJpb2QgY29sdW1uLgoKLSAgICoqYGNsYXNzKGRhdGFzZXRfbm9fZW1wdHlfcm93cyRwZXJpb2QpYCoqIHJldHVybnMgdGhlIGRhdGEgdHlwZSBvciBjbGFzcyBvZiB0aGUgKipgcGVyaW9kYCoqIGNvbHVtbiBpbiB0aGUgKipgZGF0YXNldF9ub19lbXB0eV9yb3dzYCoqIGRhdGFzZXQKCmRhdGFzZXRfbm9fZW1wdHlfcm93c1wkcGVyaW9kKQoKYGBge3J9CgpjbGFzcyhkYXRhc2V0X25vX2VtcHR5X3Jvd3MkcGVyaW9kKQpgYGAKCkl0IGlzIG9mIHR5cGUgImNoYXJhY3RlciIsIGxldCdzIHNlZSBpdHMgdW5pcXVlIHZhbHVlcyB1c2luZyBgdW5pcXVlYDoKCmBgYHtyfQp1bmlxdWUoZGF0YXNldF9ub19lbXB0eV9yb3dzJHBlcmlvZCkKYGBgCgpXZSBuZWVkIHRvIGNvbnZlcnQgdGhvc2UgbW9udGggc3RyaW5ncyBpbnRvIGEgYGRhdGVgIGZvcm1hdCwgc28gaXQgd2lsbCBmYWNpbGl0YXRlIGxhdGVyIG9uIHRoZSBvcmRlcmluZyBvZiByZXN1bHRzIGJ5IGNocm9ub2xvZ2ljYWwgb3JkZXIuIFdlIHdpbGwgdXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSBgbHVicmlkYXRlYCBwYWNrYWdlOgoKLSAgIENvbnZlcnRzIGBwZXJpb2RgIGNvbHVtbiB2YWx1ZXMgdG8gZGF0ZSBmb3JtYXQgYnkgYXBwZW5kaW5nICIyMDI0IiB0byBlYWNoIG1vbnRoIG5hbWUgYW5kIHVzaW5nIGBteSgpYCBmcm9tIGBsdWJyaWRhdGVgLgotICAgYGNsYXNzKGRhdGFzZXRfcGVyaW9kX2RhdGUkcGVyaW9kKWA6IFJldHVybnMgdGhlIGNsYXNzIG9mIHRoZSBgcGVyaW9kYCBjb2x1bW4gaW4gYGRhdGFzZXRfcGVyaW9kX2RhdGVgLgotICAgYHVuaXF1ZShkYXRhc2V0X3BlcmlvZF9kYXRlJHBlcmlvZClgOiBFeHRyYWN0cyB1bmlxdWUgZGF0ZSB2YWx1ZXMgZnJvbSB0aGUgYHBlcmlvZGAgY29sdW1uIGluIGBkYXRhc2V0X3BlcmlvZF9kYXRlYC4KCmBgYHtyfQoKZGF0YXNldF9wZXJpb2RfZGF0ZSA8LSBkYXRhc2V0X25vX2VtcHR5X3Jvd3MgJT4lIAogIG11dGF0ZShwZXJpb2QgPSBteShwYXN0ZShwZXJpb2QsICIyMDI0IikpKQoKY2xhc3MoZGF0YXNldF9wZXJpb2RfZGF0ZSRwZXJpb2QpCgp1bmlxdWUoZGF0YXNldF9wZXJpb2RfZGF0ZSRwZXJpb2QpCmBgYAoKIyMjIFJlbW92aW5nIHNwZWNpZmljIHJvd3MKCkxldCdzIGNvbnRpbnVlIGJ5IHJlbW92aW5nIHRoZSAnc3VzcGVuZGVkJyBhbmQgJ3BsYW5uZWQnIGFjdGl2aXRpZXMgdXNpbmcgYGZpbHRlcmAsIHNvIHdlIGNhbiBmb2N1cyBvbmx5IG9uIG9uZ29pbmcgYW5kIGNvbXBsZXRlZCBhY3Rpdml0aWVzLiBXZSB0YWtlIHRoZSBwcmV2aW91cyBjbGVhbmVkIGRhdGFmcmFtZSBgZGF0YXNldF9wZXJpb2RfZGF0ZS5gCgpXaGF0IGRpZmZlcmVudCB2YWx1ZXMgZG9lcyBgYWN0aXZpdHlfc3RhdHVzYCBnZXQ/CgotICAgYHVuaXF1ZShkYXRhc2V0X25vX2VtcHR5X3Jvd3MkYWN0aXZpdHlfc3RhdHVzKWA6IFJldHVybnMgYWxsIHVuaXF1ZSB2YWx1ZXMgZnJvbSB0aGUgYGFjdGl2aXR5X3N0YXR1c2AgY29sdW1uIG9mIHRoZSBgZGF0YXNldF9ub19lbXB0eV9yb3dzYCBkYXRhc2V0LCByZW1vdmluZyBkdXBsaWNhdGVzLgoKYGBge3J9CnVuaXF1ZShkYXRhc2V0X3BlcmlvZF9kYXRlJGFjdGl2aXR5X3N0YXR1cykKYGBgCgpUaGUgY29kZSBmaWx0ZXJzIGBkYXRhc2V0X25vX2VtcHR5X3Jvd3NgIHRvIGluY2x1ZGUgb25seSByb3dzIHdoZXJlIHRoZSBgYWN0aXZpdHlfc3RhdHVzYCBjb2x1bW4gaGFzIHZhbHVlcyAiT25nb2luZyIgb3IgIkNvbXBsZXRlZCIsIGFuZCBzdG9yZXMgdGhlIHJlc3VsdCBpbiBgZGF0YXNldF9ub19zdXNwZW5kZWRgLgoKYGBge3J9CmRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5IDwtIGRhdGFzZXRfcGVyaW9kX2RhdGUgJT4lIAogIGZpbHRlcihhY3Rpdml0eV9zdGF0dXMgJWluJSBjKCJPbmdvaW5nIiwgIkNvbXBsZXRlZCIpKQoKZGltKGRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5KQpgYGAKCkxldCdzIGNoZWNrCgpgYGB7cn0KdW5pcXVlKGRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5JGFjdGl2aXR5X3N0YXR1cykKYGBgCgojIyAiUGlwaW5nIiBldmVyeXRoaW5nIHNvIGZhcgoKV2UgY291bGQgaGF2ZSBkb25lIGFsbCB0aGUgcHJldmlvdXMgc3RlcHMgYXQgb25jZSBieSBjb21iaW5pbmcgYWxsIHRoZSAncGlwZXMnIGFzIGZvbGxvd3M6CgoxLiAgUmVuYW1lcyBjb2x1bW5zIGJ5IHJlbW92aW5nIHN1YnN0cmluZ3MgYWZ0ZXIgYSBzbGFzaCwgc3F1aXNoaW5nIHNwYWNlcywgbWFraW5nIG5hbWVzIHVuaXF1ZQoyLiAgQ29udmVydGluZyB0byBsb3dlcmNhc2UKMy4gIFJlcGxhY2luZyBzcGFjZXMgd2l0aCB1bmRlcnNjb3Jlcy4KNC4gIFJlbW92ZXMgc3BlY2lmaWVkIGNvbHVtbnMgYnkgaW5kZXggYW5kIG5hbWUuCjUuICBGaWx0ZXJzIG91dCByb3dzIHdoZXJlIGBsZWFkX29yZ2FuaXphdGlvbmAgaXMgTkEKNi4gIENvbnZlcnRzIGBwZXJpb2RgIHRvIDIwMjQgYGRhdGVgIGZvcm1hdAo3LiAgS2VlcHMgb25seSByb3dzIHdpdGggYGFjdGl2aXR5X3N0YXR1c2AgIk9uZ29pbmciIG9yICJDb21wbGV0ZWQiCgpgYGB7cn0KCmRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5X2FsbCA8LSBkYXRhc2V0MiAlPiUgCiAgcmVuYW1lX3dpdGgofm1ha2UudW5pcXVlKHN0cl9zcXVpc2goc3RyX3JlbW92ZSguLCAiLy4qIikpKSwgLmNvbHMgPSBldmVyeXRoaW5nKCkpICU+JQogIHJlbmFtZV93aXRoKHN0cl90b19sb3dlcikgJT4lIAogIHJlbmFtZV93aXRoKH5zdHJfcmVwbGFjZV9hbGwoLiwgIiAiLCAiXyIpKSAlPiUgCiAgc2VsZWN0KC1jKDUsIGRvbm9yLCBzZWN0b3IsIGhycF9hY3Rpdml0eSwgYG5vbi1ocnBfYWN0aXZpdHlgLCAxOCwgdW5pdF9vZl9tZWFzdXJlLCBkZWxpdmVyeV9tb2RhbGl0eSwgY29tbWVudHMpKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShsZWFkX29yZ2FuaXphdGlvbikpICU+JSAKICBtdXRhdGUocGVyaW9kID0gbXkocGFzdGUocGVyaW9kLCAiMjAyNCIpKSkgJT4lIAogIGZpbHRlcihhY3Rpdml0eV9zdGF0dXMgJWluJSBjKCJPbmdvaW5nIiwgIkNvbXBsZXRlZCIpKQogIAogIGRpbShkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGwpCiAgCiAgaGVhZChkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGwpCiAgCmBgYAoKIyMgRXhwb3J0aW5nIGRhdGEgYXMgRXhjZWwKCmBgYHtyfQoKd2IgPC0gY3JlYXRlV29ya2Jvb2soKQphZGRXb3Jrc2hlZXQod2IsICJjbGVhbl9kYXRhIiwgZ3JpZExpbmVzID0gRkFMU0UpCgp3cml0ZURhdGFUYWJsZSh3Yiwgc2hlZXQgPSAxLCBkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGwsIHRhYmxlU3R5bGUgPSAiVGFibGVTdHlsZU1lZGl1bTkiLCAgd2l0aEZpbHRlciA9IFRSVUUpCgpzYXZlV29ya2Jvb2sod2IsIGZpbGUgPSAib3V0cHV0L2NsZWFuX2RhdGEueGxzeCIsIG92ZXJ3cml0ZSA9IFRSVUUpCgpgYGAKCiMjIEFnZ3JlZ2F0aW5nIGRhdGEKCkxldCdzIHNlZSBzZXZlcmFsIGV4YW1wbGVzIG9mIGRpZmZlcmVudCBhZ2dyZWdhdGlvbnMuIFdlIHdpbGwgYmUgdXNpbmcgb3VyIGxhdGVzdCByZXN1bHQgYGRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5X2FsbGAsIHRoYXQgY29udGFpbnMgYWxsICJvbmdvaW5nIiBhbmQgImNvbXBsZXRlZCIgYWN0aXZpdGVzCgojIyMgUmVhY2hlZCBwZW9wbGUgYnkgcGVyaW9kLCBwcm92aW5jZSBhbmQgZGlzdHJpY3QKCmBgYHtyfQoKcmVhY2hlZF9wZXJpb2RfcHJvdmluY2VfZGlzdHJpY3RfdGJsIDwtIGRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5X2FsbCAlPiUgCiAgZ3JvdXBfYnkocGVyaW9kLCBwcm92aW5jZSwgZGlzdHJpY3QpICU+JSAKICBzdW1tYXJpc2UoCiAgICB0b3RhbF9tYWxlID0gc3VtKHRvdGFsX21hbGUsIG5hLnJtID0gVFJVRSksCiAgICB0b3RhbF9mZW1hbGUgPSBzdW0odG90YWxfZmVtYWxlLCBuYS5ybSA9IFRSVUUpLCAKICAgIHRvdGFsX3JlYWNoZWQgPSBzdW0odG90YWxfcGVvcGxlX3JlYWNoZWQsIG5hLnJtID0gVFJVRSkpCiAgCnByaW50KHJlYWNoZWRfcGVyaW9kX3Byb3ZpbmNlX2Rpc3RyaWN0X3RibCkKYGBgCgpXZSBjYW4gYWxzbyB1c2UgdGhlIGNvbW1hbmQgYGFjcm9zcygpYCwgdG8gYXBwbHkgdGhlIHNhbWUgb3BlcmF0aW9uIG92ZXIgYSBjb2xsZWN0aW9uIG9mIGNvbHVtbnMgaW4gYSBtb3JlIGNvbXBhY3Qgd2F5OgoKLSAgIEdyb3VwcyBgZGF0YXNldF9maWx0ZXJfYWN0aXZpdHlfYWxsYCBieSBgcGVyaW9kYCwgYHByb3ZpbmNlYCwgYW5kIGBkaXN0cmljdGAuCi0gICBTdW1tYXJpemVzIGB0b3RhbF9tYWxlYCwgYHRvdGFsX2ZlbWFsZWAsIGFuZCBgdG90YWxfcGVvcGxlX3JlYWNoZWRgIGZvciBlYWNoIGdyb3VwLCByZW1vdmluZyBOQXMuCi0gICBTdG9yZXMgcmVzdWx0IGluIGByZWFjaGVkX3BlcmlvZF9wcm92aW5jZV9kaXN0cmljdF90YmxgLgoKYGBge3J9CgpyZWFjaGVkX3BlcmlvZF9wcm92aW5jZV9kaXN0cmljdF90YmwgPC0gZGF0YXNldF9maWx0ZXJfYWN0aXZpdHlfYWxsICU+JSAKICBncm91cF9ieShwZXJpb2QsIHByb3ZpbmNlLCBkaXN0cmljdCkgJT4lIAogIHN1bW1hcmlzZShhY3Jvc3MoYyh0b3RhbF9tYWxlLCB0b3RhbF9mZW1hbGUsIHRvdGFsX3Blb3BsZV9yZWFjaGVkKSwgfiBzdW0oLiwgbmEucm0gPSBUUlVFKSkpCiAgCnByaW50KHJlYWNoZWRfcGVyaW9kX3Byb3ZpbmNlX2Rpc3RyaWN0X3RibCkKYGBgCgojIyMgUmVhY2hlZCBwZW9wbGUgYnkgaW5kaWNhdG9yCgpGb3Igc2ltcGxpY2l0eSwgd2Ugd2lsbCBhc3N1bWUgdGhhdCB3ZSBjYW4gc3VtIGV2ZXJ5dGhpbmcgdXAgdG8gbmF0aW9uYWwgbGV2ZWwsIGFuZCBhbHNvIGFjcm9zcyBtb250aHMKCmBgYHtyfQpyZWFjaGVkX2luZGljYXRvcl9uYXRpb25hbF90YmwgPC0gZGF0YXNldF9maWx0ZXJfYWN0aXZpdHlfYWxsICU+JSAKICBncm91cF9ieShjbHVzdGVyX2luZGljYXRvcikgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF9yZWFjaGVkID0gc3VtKHRvdGFsX3Blb3BsZV9yZWFjaGVkLCBuYS5ybSA9IFRSVUUpKQoKcmVhY2hlZF9pbmRpY2F0b3JfbmF0aW9uYWxfdGJsCmBgYAoKIyMjIFJlYWNoZWQgcGVvcGxlIGJ5IGluZGljYXRvciAoYSBtb3JlIGNvbXBsZXggc2l0dWF0aW9uKQoKTGV0J3Mgc2VlIHdoaWNoIGluZGljYXRvciB3ZSBoYXZlIGluIHRoZSBkYXRhc2V0OgoKYGBge3J9CnVuaXF1ZShkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGwkY2x1c3Rlcl9pbmRpY2F0b3IpCmBgYAoKTGV0J3MgYXNzdW1lIG5vdyB0aGF0IGZvciB0aGUgWzJdIGluZGljYXRvciwgd2UgaGF2ZSB0YWtlIHRoZSBtYXhpbXVtIGFjcm9zcyBtb250aHMgaW5zdGVhZCB0aGUgc3VtIGJlY2F1c2UgaXQgd2FzIGRlZmluZWQgbGlrZSB0aGF0LiBXZSBuZWVkIHRoZW4gdG8gYXBwbHkgZGlmZmVyZW50IGFnZ3JlZ2F0aW9uIGRlcGVuZGluZyBvbiB0aGUgaW5kaWNhdG9yLiBBIHdheSB0byBkbyBjYW4gYmU6CgotICAgR3JvdXBzIGBkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGxgIGJ5IGBwZXJpb2RgIGFuZCBgY2x1c3Rlcl9pbmRpY2F0b3JgLCBjYWxjdWxhdGVzIHRoZSBzdW0gb2YgYHRvdGFsX3Blb3BsZV9yZWFjaGVkYCBmb3IgZWFjaCBncm91cC4KLSAgIFVuZ3JvdXBzIHRoZSBkYXRhLgotICAgR3JvdXBzIGJ5IGBjbHVzdGVyX2luZGljYXRvcmAgYWdhaW4sIGFwcGxpZXMgZGlmZmVyZW50IGFnZ3JlZ2F0aW9uIGJhc2VkIG9uIHRoZSBjb250ZW50IG9mIGBjbHVzdGVyX2luZGljYXRvcmAgKG1heCBmb3IgdGhvc2UgY29udGFpbmluZyAiZm9ybWFsIiwgc3VtIGZvciBvdGhlcnMpLgotICAgVW5ncm91cHMgYW5kIHJlbW92ZXMgZHVwbGljYXRlcwotICAgUmVzdWx0cyBzdG9yZWQgaW4gYHJlYWNoZWRfaW5kaWNhdG9yX25hdGlvbmFsX3N1bV9tYXhfdGJsYC4KCkluIHRoaXMgY2FzZSwgdGhlIGBzdW1tYXJpc2VgIGZ1bmN0aW9uIGdlbmVyYXRlZCBzZXZlcmFsIHJlcGVhdGVkIHJvd3MsIGFzIGRpZmZlcmVudCBhZ2dyZWdhdGlvbiBwYXRocyAobGlrZSBzdW0gYW5kIG1heCkgY291bGQgcG90ZW50aWFsbHkgY3JlYXRlIHJvd3Mgd2l0aCBpZGVudGljYWwgdmFsdWVzIGFjcm9zcyB0aGUgY29sdW1ucyBiZWluZyBhbmFseXplZC4gV2UgdXNlIGBkaXN0aW5jdCgpYCB0byByZW1vdmUgdGhlIGR1cGxpY2F0ZXMuCgpgYGB7cn0KcmVhY2hlZF9pbmRpY2F0b3JfbmF0aW9uYWxfc3VtX21heF90YmwgPC0gZGF0YXNldF9maWx0ZXJfYWN0aXZpdHlfYWxsICU+JSAKICBncm91cF9ieShwZXJpb2QsIGNsdXN0ZXJfaW5kaWNhdG9yKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsX3JlYWNoZWQgPSBzdW0odG90YWxfcGVvcGxlX3JlYWNoZWQsIG5hLnJtID0gVFJVRSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KGNsdXN0ZXJfaW5kaWNhdG9yKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsX3JlYWNoZWQgPSBpZl9lbHNlKGdyZXBsKCJmb3JtYWwiLCBjbHVzdGVyX2luZGljYXRvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heCh0b3RhbF9yZWFjaGVkLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0odG90YWxfcmVhY2hlZCwgbmEucm0gPSBUUlVFKSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGRpc3RpbmN0KCkKCnJlYWNoZWRfaW5kaWNhdG9yX25hdGlvbmFsX3N1bV9tYXhfdGJsIApgYGAKCiMjIyBSZWFjaGVkIHBlb3BsZSBieSBtb250aAoKLSAgIEdyb3VwcyBgZGF0YXNldF9maWx0ZXJfYWN0aXZpdHlfYWxsYCBieSBgcGVyaW9kYC4KLSAgIFN1bW1hcml6ZXMgYHRvdGFsX3Blb3BsZV9yZWFjaGVkYCBpbnRvIGB0b3RhbF9yZWFjaGVkYCwgaWdub3JpbmcgTkFzLgotICAgQWRkcyBhIGBtb250aGAgY29sdW1uIHdpdGggbW9udGggbmFtZXMgZXh0cmFjdGVkIGZyb20gYHBlcmlvZGAuCi0gICBTZWxlY3RzIGFuZCByZWFycmFuZ2VzIGNvbHVtbnMgdG8gYHBlcmlvZGAsIGBtb250aGAsIGB0b3RhbF9yZWFjaGVkYC4KLSAgIFJlc3VsdHMgc3RvcmVkIGluIGByZWFjaGVkX3BlcmlvZF9uYXRpb25hbF90YmxgLgoKYGBge3J9CgpyZWFjaGVkX21vbnRoX25hdGlvbmFsX3RibCA8LSBkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGwgJT4lIAogIGdyb3VwX2J5KHBlcmlvZCkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF9yZWFjaGVkID0gc3VtKHRvdGFsX3Blb3BsZV9yZWFjaGVkLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgbXV0YXRlKG1vbnRoID0gbW9udGgocGVyaW9kLCBsYWJlbCA9IFRSVUUpKSAlPiUgCiAgc2VsZWN0KHBlcmlvZCwgbW9udGgsIHRvdGFsX3JlYWNoZWQpCgoKcHJpbnQocmVhY2hlZF9tb250aF9uYXRpb25hbF90YmwpCmBgYAoKIyMjIFJlYWNoZWQgcGVvcGxlIGJ5IG1vbnRoIGFuZCBpbmRpY2F0b3IKCmBgYHtyfQoKcmVhY2hlZF9tb250aF9pbmRpY2F0b3JfbmF0aW9uYWxfdGJsIDwtIGRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5X2FsbCAlPiUgCiAgZ3JvdXBfYnkocGVyaW9kLCBjbHVzdGVyX2luZGljYXRvcikgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF9yZWFjaGVkID0gc3VtKHRvdGFsX3Blb3BsZV9yZWFjaGVkLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgbXV0YXRlKG1vbnRoID0gbW9udGgocGVyaW9kLCBsYWJlbCA9IFRSVUUpKSAlPiUgCiAgc2VsZWN0KHBlcmlvZCwgbW9udGgsIGluZGljYXRvciA9IGNsdXN0ZXJfaW5kaWNhdG9yLCB0b3RhbF9yZWFjaGVkKQoKCnByaW50KHJlYWNoZWRfbW9udGhfaW5kaWNhdG9yX25hdGlvbmFsX3RibCkKYGBgCgojIyMgUmVhY2hlZCBwZW9wbGUgYnkgcHJvdmluY2UKCmBgYHtyfQpyZWFjaGVkX3Byb3ZpbmNlX3RibCA8LSBkYXRhc2V0X2ZpbHRlcl9hY3Rpdml0eV9hbGwgJT4lIAogIGdyb3VwX2J5KHByb3ZpbmNlLCBhZG0xKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsX3JlYWNoZWQgPSBzdW0odG90YWxfcGVvcGxlX3JlYWNoZWQsIG5hLnJtID0gVFJVRSkpICU+JSAKICBzZWxlY3QocHJvdmluY2UsIGFkbTEsIHRvdGFsX3JlYWNoZWQpCgpwcmludChyZWFjaGVkX3Byb3ZpbmNlX3RibCkKYGBgCgojIyMgUmVhY2hlZCBwZW9wbGUgYnkgbW9udGggYW5kIHByb3ZpbmNlCgpgYGB7cn0KCnJlYWNoZWRfbW9udGhfcHJvdmluY2VfdGJsIDwtIGRhdGFzZXRfZmlsdGVyX2FjdGl2aXR5X2FsbCAlPiUgCiAgZ3JvdXBfYnkocGVyaW9kLCBwcm92aW5jZSwgYWRtMSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF9yZWFjaGVkID0gc3VtKHRvdGFsX3Blb3BsZV9yZWFjaGVkLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUobW9udGggPSBtb250aChwZXJpb2QsIGxhYmVsID0gVFJVRSkpICU+JSAKICBzZWxlY3QobW9udGgsIHByb3ZpbmNlLCBhZG0xLCB0b3RhbF9yZWFjaGVkKQoKcHJpbnQocmVhY2hlZF9tb250aF9wcm92aW5jZV90YmwpCmBgYAoKIyMjIEFkZGluZyB0b3RhbHMgdXNpbmcgYGphbml0b3IoKWAgcGFja2FnZQoKV2hpbGUgd2UgY2FuIGNhbGN1bGF0ZSB0YWJsZSB0b3RhbHMgKGJvdGggYWNyb3NzIHJvdyBhbmQgYnkgY29sdW1uKSB1c2luZyBkcGx5ciwgdGhlcmUgaXMgYWxzbyBzb21lIHBhY2thZ2VzIHRoYXQgZmFjaWxpdGF0ZSB0aGlzIHRhc2ssIGZvciBleGFtcGxlIHRoZSBgamFuaXRvcigpYCBwYWNrYWdlLiBMZXQncyBzZWUgYW4gZXhhbXBsZSB3aXRoIHRoZSBpbmRpY2F0b3JzIGByZWFjaGVkX2luZGljYXRvcl9uYXRpb25hbF90YmxgIGRhdGFmcmFtZSB3ZSBvYnRhaW5lZCBiZWZvcmU6CgotICAgQWRkcyBhIHRvdGFsIHJvdyB0byB0aGUgYHJlYWNoZWRfaW5kaWNhdG9yX25hdGlvbmFsX3RibGAsIHN1bW1pbmcgdmFsdWVzIGFjcm9zcyB0aGUgYHZhbHVlYCBjb2x1bW4sIHVzaW5nIGBhZG9ybl90b3RhbHMod2hlcmUgPSAicm93IilgLgotICAgU3RvcmVzIHRoZSByZXN1bHRpbmcgdGFibGUgd2l0aCB0b3RhbHMgaW4gYHJlYWNoZWRfaW5kaWNhdG9yX25hdGlvbmFsX3RvdGFsX3RibGAuCgpgYGB7cn0KCnJlYWNoZWRfaW5kaWNhdG9yX25hdGlvbmFsX3RvdGFsX3RibCA8LSByZWFjaGVkX2luZGljYXRvcl9uYXRpb25hbF90YmwgJT4lIAogIGFkb3JuX3RvdGFscyh3aGVyZSA9ICJyb3ciKQoKcHJpbnQocmVhY2hlZF9pbmRpY2F0b3JfbmF0aW9uYWxfdG90YWxfdGJsKQpgYGAKCiMjIyBGb3JtYXR0aW5nIHRhYmxlcyB3aXRoIGBrYWJsZSgpYCBhbmQgYGthYmxlRXh0cmEoKWAgcGFja2FnZXMKCldlIGNhbiBhbHNvIGZvcm1hdCB0aGUgcmVzdWx0aW5nIHRhYmxlIGZvciBwdWJsaXNoaW5nLCB1c2luZyB0aGUgYGthYmxlKClgIGFuZCBga2FibGVFeHRyYSgpYCBwYWNrYWdlczoKCmBgYHtyfQpyZWFjaGVkX3BlcmlvZF9wcm92aW5jZV9kaXN0cmljdF90YmwgJT4lIGthYmxlKCkgJT4lIGthYmxlX2NsYXNzaWMoKQpgYGAKCiMjIFBsb3R0aW5nIHdpdGggYGdncGxvdGAKCioqYGdncGxvdDJgKiogaXMgYSBkYXRhIHZpc3VhbGl6YXRpb24gcGFja2FnZSBmb3IgdGhlIFIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UuIEl0J3MgYmFzZWQgb24gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MsIHByb3ZpZGluZyBhIHN5c3RlbSBmb3IgZGVjbGFyYXRpdmVseSBjcmVhdGluZyBncmFwaGljcyBiYXNlZCBvbiB0aGUgaWRlYSBvZiBidWlsZGluZyB1cCBhIHBsb3QgdXNpbmcgbGF5ZXJzLiBUaGUgcGxvdCBsYXllcnMgYXJlIGNvbm5lY3RlZCB1c2luZyB0aGUgc2lnbiBgK2AKCiMjIyBTaW1wbGUgYmFycGxvdAoKVGhpcyBjb2RlIHNuaXBwZXQgc2V0cyB1cCBhIGdncGxvdCBvYmplY3Qgd2l0aCB0aGUgYWVzdGhldGljcyBkZWZpbmVkIGZvciB4LWF4aXMgKHByb3ZpbmNlKSwgeS1heGlzICh0b3RhbCByZWFjaGVkKSwgYW5kIGJhciBmaWxsIGNvbG9yIChhbHNvIGJ5IHByb3ZpbmNlKSwgYW5kIHRoZW4gYWRkcyBhIGNvbHVtbiBnZW9tZXRyeSB0byByZW5kZXIgYSBiYXIgZm9yIGVhY2ggcHJvdmluY2UuIFRoZSBgZmlsbGAgYWVzdGhldGljIGVuc3VyZXMgdGhhdCBlYWNoIHByb3ZpbmNlJ3MgYmFyIGlzIGNvbG9yZWQgZGlmZmVyZW50bHksIGFpZGluZyBpbiBkaXN0aW5ndWlzaGluZyBiZXR3ZWVuIHRoZW0gdmlzdWFsbHkgaW4gdGhlIHBsb3QuCgpgYGB7cn0KCmdncGxvdChyZWFjaGVkX3Byb3ZpbmNlX3RibCwgYWVzKHggPSBwcm92aW5jZSwgeSA9IHRvdGFsX3JlYWNoZWQsIGZpbGwgPSBwcm92aW5jZSkpICsKICBnZW9tX2NvbCgpCgpgYGAKCldlIGNhbiBhbHNvICdwaXBlJyB0aGUgZGF0YWZyYW1lIGFuZCBzZW5kIGl0IHRvIGBnZ3Bsb3Q6YAoKYGBge3J9CgpyZWFjaGVkX3Byb3ZpbmNlX3RibCAlPiUgZ2dwbG90KGFlcyh4ID0gcHJvdmluY2UsIHkgPSB0b3RhbF9yZWFjaGVkLCBmaWxsID0gcHJvdmluY2UpKSArCiAgZ2VvbV9jb2woKQoKYGBgCgpJZiB3ZSBkb24ndCB3YW50IHRvIHVzZSB0aGUgcGxvdCBhdCB0aGUgbW9tZW50IG9mIGNyZWF0aW9uLCBpdCBjYW4gYmUgYWxzbyBzdG9yZSBpbiBhIHZhcmlhYmxlIGZvciBsYXRlciBwcmludCBvciBzYXZpbmc6CgpgYGB7cn0KcCA8LSByZWFjaGVkX3Byb3ZpbmNlX3RibCAlPiUgZ2dwbG90KGFlcyh4ID0gcHJvdmluY2UsIHkgPSB0b3RhbF9yZWFjaGVkLCBmaWxsID0gcHJvdmluY2UpKSArCiAgZ2VvbV9jb2woKQoKcApgYGAKCiMjIyBDdXN0b21pemluZyB0aGUgY2hhcnQKClRoaXMgY29kZSBjcmVhdGVzIGEgYmFyIGNoYXJ0IHVzaW5nIGBnZ3Bsb3QyYDoKCi0gICAqKmBhZXMoeCA9IHByb3ZpbmNlLCB5ID0gdG90YWxfcmVhY2hlZCwgZmlsbCA9IHByb3ZpbmNlKWAqKjogTWFwcyBgcHJvdmluY2VgIHRvIHRoZSB4LWF4aXMgYW5kIHRoZSBmaWxsIGNvbG9yIG9mIHRoZSBiYXJzLCBhbmQgYHRvdGFsX3JlYWNoZWRgIHRvIHRoZSB5LWF4aXMuCi0gICAqKmBnZW9tX2NvbCgpYCoqOiBQbG90cyBiYXJzIGZvciBlYWNoIHByb3ZpbmNlIHdpdGggaGVpZ2h0cyBkZXRlcm1pbmVkIGJ5IGB0b3RhbF9yZWFjaGVkYC4KLSAgICoqYHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFVOSUNFRl9QQUxFVFRFKWAqKjogQXBwbGllcyBjdXN0b20gY29sb3JzIGZyb20gYFVOSUNFRl9QQUxFVFRFYCB0byB0aGUgYmFycywgb25lIGZvciBlYWNoIHByb3ZpbmNlLgotICAgKipgdGhlbWVfbWluaW1hbCgpYCoqOiBVc2VzIGEgbWluaW1hbCB0aGVtZSBmb3IgYSBjbGVhbiBwcmVzZW50YXRpb24uCi0gICAqKmBsYWJzKClgKio6IFNldHMgbGFiZWxzIGZvciB0aGUgeC1heGlzLCB5LWF4aXMsIGFuZCBhIHRpdGxlIGZvciB0aGUgY2hhcnQuCgpgYGB7cn0KCnAgPC0gZ2dwbG90KHJlYWNoZWRfcHJvdmluY2VfdGJsLCBhZXMoeCA9IHByb3ZpbmNlLCB5ID0gdG90YWxfcmVhY2hlZCwgZmlsbCA9IHByb3ZpbmNlKSkgKwogIGdlb21fY29sKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFVOSUNFRl9QQUxFVFRFKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSAiUHJvdmluY2UiLCB5ID0gIlRvdGFsIFJlYWNoZWQiLCB0aXRsZSA9ICJUb3RhbCBSZWFjaGVkIGJ5IFByb3ZpbmNlIikKCnAKYGBgCgojIyMgRmxpcHBlZCBiYXJwbG90CgpVc3VhbGx5LCB3ZSBjYW4gZmxpcCBhbnkgY2hhcnQgYnkgc3dhcHBpbmcgdGhlIHZhcmlhYmxlcyBpbiB0aGUgeCBhbmQgeSBheGlzIGFzIGZvbGxvd3M6CgpgYGB7cn0KCmdncGxvdChyZWFjaGVkX3Byb3ZpbmNlX3RibCwgYWVzKHkgPSByZW9yZGVyKHByb3ZpbmNlLCB0b3RhbF9yZWFjaGVkKSwgeCA9IHRvdGFsX3JlYWNoZWQsIGZpbGwgPSBwcm92aW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBVTklDRUZfUEFMRVRURSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh4ID0gIlByb3ZpbmNlIiwgeSA9ICJUb3RhbCBSZWFjaGVkIiwgdGl0bGUgPSAiVG90YWwgUmVhY2hlZCBieSBQcm92aW5jZSIpCgpgYGAKCkFsdGVybmF0aXZlbHksIHdlIGNhbiBhbHNvIHVzZSB0aGUgY29tbWFuZCBgY29vcmRfZmxpcCgpYDoKCmBgYHtyfQpwIDwtIGdncGxvdChyZWFjaGVkX3Byb3ZpbmNlX3RibCwgYWVzKHggPSByZW9yZGVyKHByb3ZpbmNlLCB0b3RhbF9yZWFjaGVkKSwgeSA9IHRvdGFsX3JlYWNoZWQsIGZpbGwgPSBwcm92aW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFVOSUNFRl9QQUxFVFRFKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSAiUHJvdmluY2UiLCB5ID0gIlRvdGFsIFJlYWNoZWQiLCB0aXRsZSA9ICJUb3RhbCBSZWFjaGVkIGJ5IFByb3ZpbmNlIikKCnAKYGBgCgojIyMgU3RhY2tlZCBiYXJwbG90CgpXZSBhcmUgZ3JvdXBpbmcgYG1vbnRoYCByZXN1bHRzIGluIHRoZSBzYW1lIGJhcjoKCi0gICBgZ2dwbG90KHJlYWNoZWRfbW9udGhfcHJvdmluY2VfdGJsLCBhZXMoeCA9IHByb3ZpbmNlLCB5ID0gdG90YWxfcmVhY2hlZCwgZmlsbCA9IG1vbnRoKSlgOiBJbml0aWFsaXplcyBhIGdncGxvdCBncmFwaCB1c2luZyBgcmVhY2hlZF9tb250aF9wcm92aW5jZV90YmxgIGRhdGEsIHNldHRpbmcgYHByb3ZpbmNlYCBhcyB0aGUgeC1heGlzLCBgdG90YWxfcmVhY2hlZGAgYXMgdGhlIHktYXhpcywgYW5kIGBtb250aGAgdG8gY29sb3IgdGhlIGJhcnMuCi0gICBgZ2VvbV9jb2woKWA6IEFkZHMgY29sdW1ucyB0byB0aGUgcGxvdCwgd2l0aCBoZWlnaHRzIHJlcHJlc2VudGluZyBgdG90YWxfcmVhY2hlZGAgdmFsdWVzLgotICAgYHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFVOSUNFRl9QQUxFVFRFKWA6IEN1c3RvbWl6ZXMgdGhlIGNvbG9yIHBhbGV0dGUgb2YgdGhlIGZpbGwgKG1vbnRoKSB1c2luZyBwcmVkZWZpbmVkIGNvbG9ycyBpbiBgVU5JQ0VGX1BBTEVUVEVgLgotICAgYHRoZW1lX21pbmltYWwoKWA6IEFwcGxpZXMgYSBtaW5pbWFsaXN0aWMgdGhlbWUgdG8gdGhlIHBsb3QsIHJlZHVjaW5nIHZpc3VhbCBub2lzZS4KLSAgIGBsYWJzKHggPSAiUHJvdmluY2UiLCB5ID0gIlRvdGFsIFJlYWNoZWQiLCB0aXRsZSA9ICJUb3RhbCBSZWFjaGVkIGJ5IFByb3ZpbmNlIGFuZCBNb250aCIpYDogU2V0cyBsYWJlbHMgZm9yIHRoZSB4LWF4aXMsIHktYXhpcywgYW5kIHRoZSBwbG90IHRpdGxlLgoKYGBge3J9CgpwIDwtIGdncGxvdChyZWFjaGVkX21vbnRoX3Byb3ZpbmNlX3RibCwgYWVzKHggPSBwcm92aW5jZSwgeSA9IHRvdGFsX3JlYWNoZWQsIGZpbGwgPSBtb250aCkpICsKICBnZW9tX2NvbCgpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gVU5JQ0VGX1BBTEVUVEUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoeCA9ICJQcm92aW5jZSIsIHkgPSAiVG90YWwgUmVhY2hlZCIsIHRpdGxlID0gIlRvdGFsIFJlYWNoZWQgYnkgUHJvdmluY2UgYW5kIE1vbnRoIikKCnAKYGBgCgojIyMgR3JvdXBlZCBiYXJwbG90CgpXZSBhcmUgZ3JvdXBpbmcgYnkgbW9udGggYXMgYmVmb3JlLCBidXQgaW4gdGhpcyBjYXNlIHdlIHdhbnQgc2VwYXJhdGUgY29udGlndW91cyBiYXJzLiBXZSBjb250cm9sIHRoaXMgd2l0aCBgZ2VvbV9jb2wocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKWA6CgotICAgYGdncGxvdChyZWFjaGVkX21vbnRoX3Byb3ZpbmNlX3RibCwgYWVzKHggPSBwcm92aW5jZSwgeSA9IHRvdGFsX3JlYWNoZWQsIGZpbGwgPSBtb250aCkpYDogU2V0cyB1cCBhIGdncGxvdCB3aXRoIGBwcm92aW5jZWAgYXMgdGhlIHgtYXhpcywgYHRvdGFsX3JlYWNoZWRgIGFzIHRoZSB5LWF4aXMsIGFuZCBkaWZmZXJlbnQgY29sb3JzIGZvciBlYWNoIGBtb250aGAuCi0gICBgZ2VvbV9jb2wocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKWA6IEFkZHMgY29sdW1ucyB0byB0aGUgcGxvdCwgc2VwYXJhdGluZyB0aGVtIGJ5IGBtb250aGAgdG8gYXZvaWQgb3ZlcmxhcCBhbmQgbWFrZSBjb21wYXJpc29ucyBlYXNpZXIuCi0gICBgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gVU5JQ0VGX1BBTEVUVEUpYDogQ3VzdG9taXplcyB0aGUgZmlsbCBjb2xvcnMgdXNpbmcgdGhlIGBVTklDRUZfUEFMRVRURWAuCi0gICBgdGhlbWVfbWluaW1hbCgpYDogQXBwbGllcyBhIG1pbmltYWxpc3RpYyB0aGVtZSwgcmVkdWNpbmcgdmlzdWFsIGNsdXR0ZXIuCi0gICBgbGFicyh4ID0gIlByb3ZpbmNlIiwgeSA9ICJUb3RhbCBSZWFjaGVkIiwgdGl0bGUgPSAiVG90YWwgUmVhY2hlZCBieSBQcm92aW5jZSBhbmQgTW9udGgiKWA6IFNldHMgbGFiZWxzIGFuZCB0aXRsZSBmb3IgY2xhcml0eS4KCmBgYHtyfQpwIDwtIGdncGxvdChyZWFjaGVkX21vbnRoX3Byb3ZpbmNlX3RibCwgYWVzKHggPSBwcm92aW5jZSwgeSA9IHRvdGFsX3JlYWNoZWQsIGZpbGwgPSBtb250aCkpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCkpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gVU5JQ0VGX1BBTEVUVEUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoeCA9ICJQcm92aW5jZSIsIHkgPSAiVG90YWwgUmVhY2hlZCIsIHRpdGxlID0gIlRvdGFsIFJlYWNoZWQgYnkgUHJvdmluY2UgYW5kIE1vbnRoIikKCnAKYGBgCgojIyMgTG9sbGlwb3AgcGxvdAoKVGhpcyBpcyBhIHVzZWZ1bCBwbG90IHRoYXQgY2FuIGJlIGNvbnN0cnVjdGVkIGJ5IGNvbWJpbmluZyBkaWZmZXJlbnQgYGdncGxvdGAgZWxlbWVudHM6CgotICAgYGdncGxvdChyZWFjaGVkX3Byb3ZpbmNlX3RibCwgYWVzKHg9cHJvdmluY2UsIHk9dG90YWxfcmVhY2hlZCkpYDogSW5pdGlhbGl6ZXMgYSBnZ3Bsb3QgZ3JhcGggdXNpbmcgYHJlYWNoZWRfcHJvdmluY2VfdGJsYCwgbWFwcGluZyBgcHJvdmluY2VgIHRvIHRoZSB4LWF4aXMgYW5kIGB0b3RhbF9yZWFjaGVkYCB0byB0aGUgeS1heGlzLgotICAgYGdlb21fc2VnbWVudChhZXMoeD1wcm92aW5jZSwgeGVuZD1wcm92aW5jZSwgeT0wLCB5ZW5kPXRvdGFsX3JlYWNoZWQsIGNvbG9yPXByb3ZpbmNlKSlgOiBBZGRzIHZlcnRpY2FsIGxpbmUgc2VnbWVudHMgZnJvbSB5PTAgdG8gYHRvdGFsX3JlYWNoZWRgIGZvciBlYWNoIGBwcm92aW5jZWAsIGNvbG9yZWQgYnkgYHByb3ZpbmNlYC4KLSAgIGBnZW9tX3BvaW50KGFlcyhjb2xvcj1wcm92aW5jZSksIHNpemU9NClgOiBBZGRzIHBvaW50cyBhdCB0aGUgZW5kIG9mIGVhY2ggc2VnbWVudCwgY29sb3JlZCBieSBgcHJvdmluY2VgIGFuZCB3aXRoIGEgc3BlY2lmaWVkIHNpemUuCi0gICBgY29vcmRfZmxpcCgpYDogRmxpcHMgdGhlIHggYW5kIHkgYXhlcywgbWFraW5nIHRoZSBiYXJzIGhvcml6b250YWwuCi0gICBgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IFVOSUNFRl9QQUxFVFRFKWA6IFNldHMgdGhlIGNvbG9ycyBmb3IgdGhlIGRpZmZlcmVudCBwcm92aW5jZXMgdXNpbmcgYFVOSUNFRl9QQUxFVFRFYC4KLSAgIGB0aGVtZV9taW5pbWFsKClgOiBBcHBsaWVzIGEgbWluaW1hbCB0aGVtZSB0byByZWR1Y2UgdmlzdWFsIGNsdXR0ZXIuCgpgYGB7cn0KCnAgPC0gZ2dwbG90KHJlYWNoZWRfcHJvdmluY2VfdGJsLCBhZXMoeD1wcm92aW5jZSwgeT10b3RhbF9yZWFjaGVkKSkgKwogIGdlb21fc2VnbWVudCggYWVzKHg9cHJvdmluY2UsIHhlbmQ9cHJvdmluY2UsIHk9MCwgeWVuZD10b3RhbF9yZWFjaGVkLCBjb2xvcj1wcm92aW5jZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1wcm92aW5jZSksIHNpemU9NCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IFVOSUNFRl9QQUxFVFRFKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwCgpgYGAKCiMjIyBUaW1lIHNlcmllczogRG90cwoKV2Ugd2lsbCBub3cgYmUgcGxvdHRpbmcgdGltZSBzZXJpZXMgZGF0YSBpbiBkaWZmZXJlbnQgd2F5cy4gSW4gaGVyZSBpdCB3YXMgdXNlZnVsIHRvIHRyYW5zZm9ybSB0aGUgYHBlcmlvZGAgY29sdW1uIGludG8gYSBgZGF0ZWAgZm9ybWF0LCBzbyB3ZSBjb3VsZCBkZXJpdmUgdGhlIG1vbnRoIHZhcmlhYmxlIGluIHRoZSByaWdodCBjaHJvbm9sb2dpY2FsIG9yZGVyIChpZiBub3QsIHRoZSBtb250aCB3b3VsZCBhcHBlYXIgb3JkZXJlZCBhbHBoYWJldGljYWxseSkKCi0gICBgZ2dwbG90KHJlYWNoZWRfbW9udGhfbmF0aW9uYWxfdGJsLCBhZXMoeCA9IG1vbnRoLCB5ID0gdG90YWxfcmVhY2hlZCkpYDogSW5pdGlhbGl6ZXMgYSBnZ3Bsb3QgZ3JhcGggd2l0aCBgbW9udGhgIG9uIHRoZSB4LWF4aXMgYW5kIGB0b3RhbF9yZWFjaGVkYCBvbiB0aGUgeS1heGlzLgotICAgYGdlb21fcG9pbnQoYWxwaGE9MSwgc2hhcGU9MjAsIGNvbG9yPVVOSUNFRl9QQUxFVFRFWzFdLCBzaXplPTE1KWA6IEFkZHMgcG9pbnRzIHJlcHJlc2VudGluZyBlYWNoIGRhdGEgZW50cnkuIEVhY2ggcG9pbnQgaXMgZnVsbHkgb3BhcXVlIChgYWxwaGE9MWApLCB1c2VzIGEgZmlsbGVkIGNpcmNsZSBzaGFwZSAoYHNoYXBlPTIwYCksIGNvbG9yZWQgd2l0aCB0aGUgZmlyc3QgY29sb3Igb2YgdGhlIGBVTklDRUZfUEFMRVRURWAsIGFuZCBpcyBsYXJnZSAoYHNpemU9MTVgKS4KLSAgIGBnZW9tX3RleHQoYWVzKGxhYmVsID0gdG90YWxfcmVhY2hlZCksIHNpemUgPSAyLCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlPSJib2xkIilgOiBBZGRzIHRleHQgbGFiZWxzIHRvIGVhY2ggcG9pbnQgZGlzcGxheWluZyBgdG90YWxfcmVhY2hlZGAsIHdpdGggc21hbGwgd2hpdGUgYm9sZCB0ZXh0IChgc2l6ZT0yYCkuCi0gICBgdGhlbWVfbWluaW1hbCgpYDogQXBwbGllcyBhIG1pbmltYWwgdGhlbWUgdG8gcmVkdWNlIHZpc3VhbCBjbHV0dGVyLgoKYGBge3J9CnAgPC0gZ2dwbG90KHJlYWNoZWRfbW9udGhfbmF0aW9uYWxfdGJsLCBhZXMoeCA9IG1vbnRoLCB5ID0gdG90YWxfcmVhY2hlZCkpICsKICBnZW9tX3BvaW50KGFscGhhPTEsIHNoYXBlPTIwLCBjb2xvcj1VTklDRUZfUEFMRVRURVsxXSwgc2l6ZT0xNSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSB0b3RhbF9yZWFjaGVkKSwgc2l6ZSA9IDIsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2U9ImJvbGQiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwcmludChwKQoKYGBgCgojIyMgVGltZSBzZXJpZXM6IExpbmUgcGxvdCB3aXRoIGRvdHMKCldlIGNhbiBqb2ludCB0aGUgZG90cyB3aXRoIGEgbGluZSBwbG90IHRvIGVuaGFuY2UgdGhlIHZpc3VhbGl6YXRpb246CgotICAgYGdncGxvdChyZWFjaGVkX21vbnRoX25hdGlvbmFsX3RibCwgYWVzKHggPSBtb250aCwgeSA9IHRvdGFsX3JlYWNoZWQsIGdyb3VwPTEpKWA6IFNldHMgdXAgYSBnZ3Bsb3QgZm9yIGByZWFjaGVkX21vbnRoX25hdGlvbmFsX3RibGAgd2l0aCBgbW9udGhgIG9uIHRoZSB4LWF4aXMsIGB0b3RhbF9yZWFjaGVkYCBvbiB0aGUgeS1heGlzLCBhbmQgYWxsIGRhdGEgY29uc2lkZXJlZCBwYXJ0IG9mIHRoZSBzYW1lIGdyb3VwIChlbmFibGluZyBsaW5lIGNvbm5lY3Rpdml0eSkuCi0gICBgZ2VvbV9saW5lKHNpemUgPSAwLjUsIGNvbG9yID0gVU5JQ0VGX1BBTEVUVEVbMV0pYDogQWRkcyBhIGxpbmUgY29ubmVjdGluZyB0aGUgZGF0YSBwb2ludHMsIHdpdGggYSBzcGVjaWZpZWQgY29sb3IgZnJvbSB0aGUgYFVOSUNFRl9QQUxFVFRFYCBhbmQgdGhpY2tuZXNzLgotICAgYGdlb21fcG9pbnQoc2hhcGU9MjAsIGNvbG9yPVVOSUNFRl9QQUxFVFRFWzFdLCBzaXplPTE1KWA6IEFkZHMgbGFyZ2UsIHNvbGlkIGNpcmNsZSBwb2ludHMgYXQgZWFjaCBkYXRhIHBvaW50LCBtYXRjaGluZyB0aGUgbGluZSBjb2xvci4KLSAgIGBnZW9tX3RleHQoYWVzKGxhYmVsID0gdG90YWxfcmVhY2hlZCksIHNpemUgPSAyLCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlPSJib2xkIilgOiBMYWJlbHMgZWFjaCBwb2ludCB3aXRoIGl0cyBgdG90YWxfcmVhY2hlZGAgdmFsdWUgaW4gYm9sZCwgd2hpdGUgdGV4dC4KLSAgIGB0aGVtZV9taW5pbWFsKClgOiBBcHBsaWVzIGEgbWluaW1hbGlzdGljIHRoZW1lIGZvciBhIGNsZWFuIGFwcGVhcmFuY2UuCgpgYGB7cn0KCnAgPC0gZ2dwbG90KHJlYWNoZWRfbW9udGhfbmF0aW9uYWxfdGJsLCBhZXMoeCA9IG1vbnRoLCB5ID0gdG90YWxfcmVhY2hlZCwgZ3JvdXA9MSkpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDAuNSwgY29sb3IgPSBVTklDRUZfUEFMRVRURVsxXSkgKwogIGdlb21fcG9pbnQoc2hhcGU9MjAsIGNvbG9yPVVOSUNFRl9QQUxFVFRFWzFdLCBzaXplPTE1KSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRvdGFsX3JlYWNoZWQpLCBzaXplID0gMiwgY29sb3IgPSAid2hpdGUiLCBmb250ZmFjZT0iYm9sZCIpICsKICB0aGVtZV9taW5pbWFsKCkKCnByaW50KHApCmBgYAoKIyMjIFRpbWUgc2VyaWVzOiBNdWx0aS1saW5lIHBsb3RzCgpXZSBjYW4gdXNlIGEgZ3JvdXBpbmcgdmFyaWFibGUsIGluIHRoaXMgY2FzZSBgcHJvdmluY2VgLCB0byBwbG90IHNldmVyYWwgbGluZXBsb3RzIGFjcm9zcyB0aW1lOgoKLSAgIGBnZ3Bsb3QocmVhY2hlZF9tb250aF9wcm92aW5jZV90YmwsIGFlcyh4ID0gbW9udGgsIHkgPSB0b3RhbF9yZWFjaGVkLCBncm91cCA9IHByb3ZpbmNlLCBjb2xvciA9IHByb3ZpbmNlKSlgOiBJbml0aWFsaXplcyBhIGdncGxvdCB3aXRoIGBtb250aGAgb24gdGhlIHgtYXhpcywgYHRvdGFsX3JlYWNoZWRgIG9uIHRoZSB5LWF4aXMsIGFuZCBsaW5lcyBncm91cGVkIGFuZCBjb2xvcmVkIGJ5IGBwcm92aW5jZWAuCi0gICBgZ2VvbV9saW5lKHNpemUgPSAwLjUpYDogQWRkcyBsaW5lcyB0byBjb25uZWN0IGRhdGEgcG9pbnRzIHdpdGhpbiBlYWNoIGBwcm92aW5jZWAgZ3JvdXAsIHdpdGggYSBzcGVjaWZpZWQgbGluZSB0aGlja25lc3MuCi0gICBgZ2VvbV9wb2ludChhbHBoYT0wLjcsIHNoYXBlPTIwLCBzaXplPTEyKWA6IEFkZHMgc2VtaS10cmFuc3BhcmVudCBjaXJjbGUgcG9pbnRzIGF0IGVhY2ggZGF0YSBwb2ludCwgd2l0aCBzcGVjaWZpZWQgc2l6ZSBhbmQgb3BhY2l0eS4KLSAgIGBnZW9tX3RleHQoYWVzKGxhYmVsID0gdG90YWxfcmVhY2hlZCksIHNpemUgPSAxLjUsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2U9ImJvbGQiKWA6IExhYmVscyBlYWNoIHBvaW50IHdpdGggaXRzIGB0b3RhbF9yZWFjaGVkYCB2YWx1ZSBpbiBib2xkLCB3aGl0ZSB0ZXh0LgotICAgYHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBVTklDRUZfUEFMRVRURSlgOiBDdXN0b21pemVzIHRoZSBsaW5lIGNvbG9ycyB1c2luZyB0aGUgYFVOSUNFRl9QQUxFVFRFYC4KLSAgIGB0aGVtZV9taW5pbWFsKClgOiBBcHBsaWVzIGEgbWluaW1hbCB0aGVtZSB0byByZWR1Y2UgdmlzdWFsIGNsdXR0ZXIuCi0gICBgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpYDogTW92ZXMgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tIG9mIHRoZSBwbG90IGZvciBiZXR0ZXIgdmlzaWJpbGl0eSBhbmQgbGF5b3V0LgoKYGBge3J9CgptdWx0aWxpbmVfcGxvdCA8LSBnZ3Bsb3QocmVhY2hlZF9tb250aF9wcm92aW5jZV90YmwsIGFlcyh4ID0gbW9udGgsIHkgPSB0b3RhbF9yZWFjaGVkLCBncm91cCA9IHByb3ZpbmNlLCBjb2xvciA9IHByb3ZpbmNlKSkgKwogIGdlb21fbGluZShzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjcsIHNoYXBlPTIwLCBzaXplPTEyKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRvdGFsX3JlYWNoZWQpLCBzaXplID0gMS41LCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlPSJib2xkIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBVTklDRUZfUEFMRVRURSkgKyAgIyBDdXN0b21pemUgdGhlIGNvbG9ycwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpwcmludChtdWx0aWxpbmVfcGxvdCkKYGBgCgojIyMgU2F2aW5nIGEgcGxvdAoKTGV0J3Mgc2F2ZSB0aGUgbGF0ZXN0IHBsb3QgYW5kIGV4cG9ydCBpdCBhcyBhIFBERi4gV2Ugb25seSBuZWVkIHRvIHdyaXRlIHRoZSBwYXRoIGFuZCBuYW1lIHRvIHRoZSBmaWxlLCBhbmQgY2hvb3NlIHRoZSByaWdodCBleHRlbnNpb24gJy5wZGYnLiBJdCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgc2F2ZWQgYXMgUERGOgoKYGBge3J9CgpnZ3NhdmUocGxvdCA9IG11bHRpbGluZV9wbG90LCBmaWxlbmFtZSA9ICJwbG90cy9tdWx0aWxpbmUucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCkKCmBgYAoKQWx0ZXJuYXRpdmVsbHksIHdlIGNhbiBhbHNvIHNhdmUgaXQgYXMgUE5HIGZvbGxvd2luZyB0aGUgc2FtZSBhcHByb2FjaAoKYGBge3J9CgpnZ3NhdmUocGxvdCA9IG11bHRpbGluZV9wbG90LCBmaWxlbmFtZSA9ICJwbG90cy9tdWx0aWxpbmUucG5nIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCkKYGBgCg==