1 Inleiding

Verwacht hier niet een uitputtende beschrijving en uitleg te vinden. In 80% van de gevallen zul je echter heel goed uit de voeten kunnen met wat je op deze website kunt vinden.

Data wordt makkelijker geconsumeerd als visualisaties. Het is veelal de beste methode om de informatie uit de data te communiceren. Alle inspanningen kunnen voor niks zijn geweest als je de inzichten uit data en bijbehorende analyses niet goed over kunt over kunt brengen.
Ondanks het feit dat het maken van visualisaties ongelofelijk eenvoudig kan zijn in R, speelt ggplot2 een zeer belangrijke rol als dé standaard in visualisaties voor R.

Het is mij opgevallen dat er maar weinig documentatie over het ggplot2 is in het Nederlands is. Zodoende ben ik begonnen een naslagwerk voor mezelf te maken. In de open source geest van R en ggplot2, lijkt het me niet meer dan logisch om dit openbaar te delen met iedereen die het wil lezen.
Wat er wel is, is een Nederlandstalige versie van het ggplot2 cheatsheet. Ik gebruik dit spiekbriefje (met dank aan rstudio rstudio spiekbriefjes) bij de uitleg en houd de volgorde van het spiekbriefje aan.

1.1 Wat is ggplot2?

ggplot2 is een package voor R https://www.r-project.org/about.html. Met ggplot2 is er gepoogd een structuur te ontwikkelen voor het maken van visualisaties. Daarom wordt er ook wel gesproken over de gramatica van visualisaties.
Er is uitstekende (Engelstalige) informatie beschikbaar over ggplot2, waaruit ik mede mijn inspiratie op heb gedaan.

Uiteraard zijn er nog veel andere (gratis) online bronnen.
Veel van de voorbeelden die je aan zult treffen komen uit, of zijn geïnspireerd op bovenstaande bronnen. Waar mogelijk zal ik naar bronnen verwijzen.

1.2 Voorwaarden om de voorbeelden zelf te kunnen maken

Je kunt alleen met ggplot2 werken als je het package geïnstalleerd en geladen hebt. Installeren doe je eenmalig met de opdracht install.packages("ggplot2"). Het laden van het package in de R-sessie doe je middels library(ggplot2).
Overige benodigde packages zijn:

  • tidyverse - voor databewerkingen
  • gridExtra - om grafieken naast en onder elkaar weer te kunnen geven
  • maps - voor kaartegegevens

Als je de onderstaande code altijd aan je script toevoegt, dan zijn alle tools die je nodig hebt voor het maken van visualisaties beschikbaar.

# Package names
packages <- c("ggplot2",
              "maps",      # maken van kaarten als input voor ggplot
              "patchwork", # grafieken naast elkaar kunnen tonen
              "tidyverse")

# Install packages not yet installed
installed_packages <- packages %in% rownames(installed.packages())
if (any(installed_packages == FALSE)) {
  install.packages(packages[!installed_packages])
}

# Packages loading
lapply(packages, library, character.only = TRUE)

2 De basis

In de basis heb je voor een visualisatie maar een paar dingen nodig, namelijk:

  1. een dataset (in basis zal hier uitgegaan worden van de datasets die bij ggplot2 horen).
  2. een of meerdere weergavelagen voor de data. De geoms.
  3. een coördinatensysteem (hoeft vaak niet expliciet aangegeven te worden) voor de assen.
  4. de eigenschappen van de visualisatie, zoals wat aan welke as toegewezen wordt en hoe er met kleur omgegaan moet worden. Dit zijn de aes of aesthetics.
    Deze kunnen gekoppeld zijn aan de data of juist onafhankelijk van de data gebruikt worden.

Conform het voorbeeld (in het blok met de witte achtergrond) op dit gedeelte van het spiekbriefje:

# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x = hwy, y = cty)) +    # data toewijzen x- en y-as
                
    # weergavelagen
    geom_point(aes(color = cyl)) + # spreidingsdiagram, kleur per aantal cylinders
    geom_smooth(method = "lm")     # lineare regressielijn door de data punten

Met qplot kun je exact dezelfde grafiek maken. Deze sla ik echter over, omdat ik qplot vrijwel nooit gebruik.

Uitleg:

  • mpg is de dataset
  • ggplot(aes(x=hwy, y=cty)) wijst de variabele hwy toe aan de x-as en cty aan de y-as
  • + toevoeging van een laag of een element
  • geom_point(aes(color = cyl)) voegt een weergavelaag toe van het type spreidingsdiagram (laat de data in puunten zien) en maakt dat de kleur afhankelijk is van de waarde van de variabele cyl.
  • + toevoeging van een laag of een element
  • geom_smooth(method = "lm") voegt een lineaire regressielijn toe inclusief het betrouwbaarheidsinterval.

Dit is vrij eenvoudig uit te breiden met bijvoorbeeld kleur, doorzichtigheid en titels:

# data
mpg %>% 
    mutate(cyl = as.factor(cyl)) %>% # categorische variabele van cyl maken
    
    # canvas / globale toewijzingen
    ggplot(aes(x = hwy, y = cty)) +  # data toewijzen x- en y-as
    
    # weergavelagen
    geom_point(
        aes(color = cyl), # spreidingsdiagram, kleur per aantal cylinders
        alpha = 0.25      # mate van ondoorzichtigheid
        ) +
    geom_smooth(method = "lm") + # lijn voor lineare regressie door de data punten
    
    # opmaak
    labs(
        title    = "Verbruik op snelweg t.o.v in de stad",     # grafiektitel
        subtitle = "Verbruiken hangen sterk met elkaar samen", # subtitel
        color    = "aantal\ncylinders",                        # legenda titel (\n = nieuwe regel)
        x        = "Afstand per gallon op de snelweg",         # titel x-as
        y        = "Afstand per gallon in de stad",            # titel y-as
        caption  = "dataset: mpg"                              # bijschrift
        ) +
    theme_minimal() # thema met minimale opmaak

Met elke + wordt een element of laag toegevoegd. Daarnaast wordt alles wat je in de opdracht ggplot() meegeeft door de onderliggende elementen overerfd. Zo kun je de toewijzing van variabelen aan de x- en/of y-as, een vulkleur enzovoort ook al in deze opdracht meegeven.

Uitleg:

  • mpg is de dataset
  • mutate(cyl = as.factor(cyl)) zorgt ervoor dat de waarden in de variabele cyl als categorieën gezien worden
  • ggplot(aes(x=hwy, y=cty)) wijst de variabele hwy toe aan de x-as en cty aan de y-as
  • geom_point(aes(color = cyl)) voegt een weergavelaag toe van het type spreidingsdiagram (laat de data in puunten zien) en maakt dat de kleur afhankelijk is van de waarde/categorie van de variabele cyl.
  • geom_smooth(method = "lm") voegt een lineaire regressielijn toe inclusief het betrouwbaarheidsinterval.
  • labs voegt labels toe, namelijk:
    • De titel (title)

    • De subtitel (subtitle)

    • Voor de legenda (color)

    • Voor de x-as (x)

    • Voor de y-as (y)

    • Het bijschrift (caption)

  • er wordt een thema toegepast met voorgeprogrammeerde instellingen (theme_minimal()).

Hierdoor ziet de grafiek er al een heel stuk beter, leesbaarder en informatiever uit dan enkel met de minimale inspanning.

3 De meest gebruikte weergavelagen (geoms)

Van de onderdelen in het spiekbriefje, zullen de volgende soorten weergavelagen aan bod komen:

  1. 1 continue variabele

  2. 1 categorische variabele

  3. 2 continue variabelen

  4. 1 categorische en 1 continue variabele

  5. 2 categorische variabelen

  6. 3 variabelen

  7. Kaarten

  8. tijdreeksen

  9. Visualiseren van fouten

In alle gevallen ligt de focus op de meest gebruikte weergavelagen. Hier heb je het meeste aan en als je deze onder de knie hebt, lukt het waarschijnlijk ook goed om met de andere een goede visualisatie te maken.
De geoms zullen in de genoemde volgorde behandeld worden, waarbij eerst een eenvoudig voorbeeld getoond wordt (het voorbeeld op het spiekbriefje) en vervolgens een vergelijkbare visualisatie met uitgebreidere opmaak en opties.

3.1 1 continue variabele

Van de weegavelagen zullen de volgende behandeld worden:

  • De dichtheidsgrafiek: geom_density()

  • Het histogram: geom_hist()

3.1.1 Dichtheidsgrafiek - geom_density()

Geom_density resulteert in een dichtheidsgrafiek. De totale oppervlakte onder de lijn (of van het ingekleurde vlak) = 1. Daarmee wordt de verdeling van een variabele inzichtelijk gemaakt. Voordeel ten opzichte van een histogram is dat er geen risico op vertekening is door de keuze van het aantal staven dat getoond wordt. De x-as heeft een continue verdeling.

3.1.1.1 Voorbeelden

3.1.1.1.1 Eenvoudig
# data
mpg %>% 
    
    # globale toewijzingen
    ggplot(aes(x = hwy)) +
    
    # weergavelagen
    geom_density() + 
    
    # titel
    labs(title = "basisgrafiek")

3.1.1.1.2 Uitgebreid
# data
mpg %>% 
    
    # globale toewijzingen
    ggplot(aes(x = hwy)) +
    
    # weergavelagen
    geom_density(
        aes(
            fill  = drv, # vulkleur, soort aandrijving
            color = drv  # kleur van de rand, soort aandrijving
        ),
        alpha = 0.25
        ) +
    
    # verder opmaak
    labs(title    = "Verdeling van Verbruik op de snelweg", # titel
         x        = "verbruik op de snelweg",               # titel x-as
         y        = NULL,                                   # geen titel y-as
         caption  = "mpg dataset",
         fill     = "type\naandrijving"      # legenda-titel (obv vulkleur)
         ) +          
    theme_minimal() +    # thema vrijwel zonder opsmuk
    theme(
        panel.grid.major = element_blank(), # geen primaire rasterlijnen
        panel.grid.minor = element_blank()  # geen secundaire rasterlijnen
        ) + 
    guides(color=FALSE)                     # geen legenda voor randkleur

Voor een uitgebreide dichtheidsgrafiek voeg je elementen toe aan de eerder genoemde basis-elementen. Bijvoorbeeld:

  1. Uitsplitsing naar soort aandrijving fill = drv (vulkleur) en colour = drv (lijnkleur) en het vrijwel doorzichtig maken van de vulkleur alpha = 0.25. Dit laatste moet buiten de aes() omdat dit niet een toewijzing van een variabele uit de data is, maar een globale aanduiding dat de vulkleuren 25% ondoorzichtig moeten zijn;
  2. (Aangepaste) titel, as-titels, legenda-titel en bijschrift door middel van labs();
  3. Een hele basic / schone weergave door gebruik van theme_minimal();
  4. Het niet weergeven van rasterlijnen en legenda met de opdrachten binnen theme();
  5. Het niet weergeven van een legenda voor color. Als deze niet weggelaten wordt, krijg je zowel een legenda voor fill als voor color, omdat deze beide gespecificeerd zijn in de aes() van geom_density().

3.1.2 Histogram - geom_hist()

Het histogram is een bekende grafiek en toont de verdeling van een variabele door de de waarden te groeperen en de telling van het aantal waarnemingen te tonen middels balken.

De belangrijkste eigenschap van het histogram is het aantal groepen of de groepsbreedte. Een foute keuze hierin kan leiden tot een vertekend beeld van de verdeling. Ga hier dus voorzichtig mee om.

Dit type grafiek is niet erg geschikt om meerdere verdelingen in 1 grafiek te tonen. Door de grafieken naast elkaar te tonen, kan dit wel. Verderop zal uitgelegd worden hoe je dit doet.

3.1.2.1 Voorbeelden

3.1.2.1.1 Eenvoudig
# data
mpg %>% 
    
    # globale toewijzingen
    ggplot(aes(x = hwy)) +   # hwy toewijzen aan de x-as 
    
    # weergavelagen
    geom_histogram()         # histogram met standaardinstelling

3.1.2.1.2 Uitgebreid
# data
mpg %>% 
    
    # globale toewijzingen
    ggplot(aes(x = hwy)) +   # hwy toewijzen aan de x-as 
    
    # weergavelagen
    geom_histogram(
        aes(fill = drv),
        bins     = 25,           # aantal balken in het histogram
        fill     = "Darkblue",   # opvulkleur
        color    = "white"       # kleur van de rand
        ) +
    stat_bin(
        aes(y = ..count.., label = ..count..), # waarden voor y en de labels
        bins  = 25,                            # aantal balken
        geom  = "text",                        # is als geom_text
        vjust = 1,                             # aanpassing positie van de tekst
        color = "white"                        # kleur van de tekst
        ) +
    
    # overige opmaak
    labs(
        # titels, assen en dergelijke
        title   = "Telling van auto modellen naar verbruik op de snelweg",
        x       = "verbruik op de snelweg",
        y       = "aantal modellen",
        caption = "dataset: mpg"
    ) + 
    theme_minimal() +                            # minimale opmaak als thema
    scale_x_continuous(
        breaks = seq(from = 10, to = 45, by = 5) # aangepaste x-as labels
        )

Het eenvoudige voorbeeld is een stuk aantrekkelijker te maken door de volgende wijzigingen toe te passen:

  1. Het aantal balken iets lager te zetten dan de standaard 30 met bins = 25

  2. De kleurstelling aan te passen, zodat de balken blauw zijn fill = "Darkblue" en de verschillende balken goed van elkaar te onderscheiden zijn color = "white"

  3. De aantallen in wit toe te voegen binnenin de balken met stat_bin()

  4. Titels en dergelijke toevoegen in het labs() argument

  5. Minimale opmaak toepassen theme_minimal()

  6. De schaalverdeling op de x-as aan te passen met scale_x_continuous().
    Minimum: 10, maximum: 45, in stappen van 5.

3.2 1 categorische variabele

3.2.1 Staafdiagram - geom_bar()

Het staafdiagram is vergelijkbaar met het histogram. Echter de variabele op de x-as hoeft niet in groepen of balken ingedeeld te worden voordat de het aantal observaties weergegeven kan worden.

Het staafdiagram komt ook nog terug bij de combinatie van een categorische variabele met een continue variabele. Dan gebruik je geom_col. Bij geom_bar() is het uitgangspunt dat het (gewogen) aantal observaties getoond wordt.

3.2.1.1 Voorbeelden

3.2.1.1.1 Eenvoudig
# data
mpg %>% 
    
    # globale toewijzingen
    ggplot(aes(x = fl)) +
    
    # weergavelagen
    geom_bar()

3.2.1.1.2 Uitgebreid
mpg %>% # dataset

ggplot(aes(x = fl)) +         # brandstoftype op de x-as
    geom_bar(                 # staafdiagram
        aes(fill = fl)        # kleurvulling --> brandstoftype
        , colour = "black"    # kleur van de buitenrand van de staven
             ) +               
    geom_text(                # labels voor de staven
        stat='count',         # stat='count' geeft het aantal van de x-variabele
        aes(label=..count..), # aantal als label
                              # label = verwijst naar 'count' in stat='count'
        colour = "white",     # tekstkleur voor het label
        vjust=1               # aanpassing van het label --> in de staaf
        ) +
    labs(title    = "MPG dataset",                         # grafiektitel
         subtitle = "Aantal modellen naar brandstoftype",  # subtitel
         x        = "brandstof type",                      # titel x-as
         y        = "aantal") +                            # titel y-as
    theme_minimal() +    # thema vrijwel zonder opsmuk
    theme(
        panel.grid.major = element_blank(),  # geen primaire rasterlijnen
        panel.grid.minor = element_blank(),  # geen secundaire rasterlijnen
        legend.position='none'               # geen legenda
        )

Voor een uitgebreid staafdiagram voeg je elementen toe aan de eerder genoemde basis-elementen. Bijvoorbeeld:

  1. Een kleur die aan de staaf toegewezen wordt op basis van het brandstoftype (fl). Dit doordat fill = fl is opgenomen in de aes van geom_bar;

  2. Het aantal waarnemingen in het wit in de staaf weergeven.

    1. geom_text (tekst);
    2. stat='count' (geeft het aantal waarnemingen) en label=..count.. (geeft het aantal waarnemingen door aan het tekstlabel);
    3. colour = "white" (label in het wit);
    4. vjust=1 (kleine verticale aanpassing, standaard wordt het label boven de staaf geprojecteerd).
  3. (Aangepaste) titel, subtitel en as-titels door middel van labs();

  4. Een hele basic / schone weergave door gebruik van theme_minimal();

  5. Het niet weergeven van rasterlijnen en legenda met de opdrachten binnen theme().

3.3 2 continue variabelen

Van de weergavelagen op het spiekbriefje zal ik aandacht besteden aan:

  • Het spreidingsdiagram: geom_jitter() & geom_point()

  • De trendlijn: geom_smooth()

  • Tekst: geom_text()

3.3.1 Spreidingsgrafiek - geom_jitter() & geom_point()

geom_jitter() en geom_point() lijken erg op elkaar. Het zijn beide spreidingsgrafieken.
Daar waar geom_point() de punten in de grafiek exact op de plek plaats die de data aangeeft, voegt geom_jitter() en wat ruis aan toe, zodat de punten niet/minder overlappen.

Als je veel datapunten hebt, dan kan geom_jitter() een betere keuze zijn, maar ook het gebruik van de doorzichtigheid (alpha) kan hier erg nuttig bij zijn.

Bekijk de grafieken en zie de verschillen die exact dezelfde code met alleen een andere geom veroorzaakt.

3.3.1.1 Voorbeelden

3.3.1.1.1 Eenvoudig (jitter)
# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=hwy, y=cty)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_jitter()

Je ziet duidelijk dat met geom_jitter() de individuele waarnemingen beter te zien zijn dan de grafiek met geom_point().

3.3.1.1.2 Eenvoudig (point)
# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=hwy, y=cty)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_point()

3.3.1.1.3 Uitgebreid (jitter)
# data
mpg %>% 
    mutate(cyl = as.factor(cyl)) %>% # categorische variabele van cyl maken
    
    # canvas / globale toewijzingen
    ggplot(aes(x=hwy, y=cty)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_jitter(aes(color = cyl),
                alpha = 0.5) +
    geom_hline(yintercept = mean(mpg$cty)) +
    geom_vline(xintercept = mean(mpg$hwy)) +
    
    # opmaak
    labs(
        title    = "Stad vs snelweg verbruik",
        subtitle = "Modellen met 4 cilinders doen het bovengemiddeld goed",
        x        = "Mijlen per gallon op de snelweg",         # titel x-as
        y        = "Mijlen per gallon in de stad",            # titel y-as
        caption  = "data set: mpg"
    ) +
    theme_minimal()

De volgende aanpassingen laten de grafieken er aantrekkelijker uitzien:

  1. Kleur en doorzichtigheid van de de punten via aes(color = cyl), alpha = 0.5.

  2. Een horzontale (geom_hline(yintercept = mean(mpg$cty))) en verticale referentielijn (geom_hline(yintercept = mean(mpg$cty))) om het gemiddelde daan te geven

  3. Wijzigen van de titel, de as-titels en toevoegen van de bron van de data via labs();

  4. Een hele basic / schone weergave door gebruik van theme_minimal().

3.3.1.1.4 Uitgebreid (point)
# data
mpg %>% 
    mutate(cyl = as.factor(cyl)) %>% # categorische variabele van cyl maken
    
    # canvas / globale toewijzingen
    ggplot(aes(x = hwy, y = cty)) +  # data toewijzen x- en y-as
    
    # weergavelagen
    geom_point(
        aes(color = cyl), # spreidingsdiagram, kleur per aantal cylinders
        alpha = 0.25      # mate van ondoorzichtigheid
        ) +
    geom_hline(yintercept = mean(mpg$cty)) +
    geom_vline(xintercept = mean(mpg$hwy)) +
    
    # opmaak
    labs(
        title    = "Verbruik op snelweg t.o.v in de stad",     # grafiektitel
        subtitle = "Meer cilinders betekent kleinere afstand per gallon",
        color    = "aantal\ncylinders",                        # legenda titel
        x        = "Mijlen per gallon op de snelweg",         # titel x-as
        y        = "Mijlen per gallon in de stad",            # titel y-as
        caption  = "dataset: mpg"                              # bijschrift
        ) +
    theme_minimal() # thema met minimale opmaak

De volgende aanpassingen laten de grafieken er aantrekkelijker uitzien:

  1. Kleur en doorzichtigheid van de de punten via aes(color = cyl), alpha = 0.5.

  2. Een horzontale (geom_hline(yintercept = mean(mpg$cty))) en verticale referentielijn (geom_hline(yintercept = mean(mpg$cty))) om het gemiddelde daan te geven

  3. Wijzigen van de titel, de as-titels en toevoegen van de bron van de data via labs();

  4. Een hele basic / schone weergave door gebruik van theme_minimal().

Ook met deze 2 grafieken is het verschil uitermate duidelijk. Ook in dit geval geeft geom_jitter() meer informatie. Bij veel datapunten is geom_jitter() dus de betere keuze.

3.3.2 Trendlijn - geom_smooth()

geom_smooth geeft een gemodelleerde lijn weer door de dataset met onzekerheidsmarges (standaard een gebied om de lijn dat ingekleurd wordt). Als je geen argumenten meegeeft, wordt er een standaard methode en formule voor de schattingslijn gekozen op basis van de data.

3.3.2.1 Voorbeelden

3.3.2.1.1 Eenvoudig
# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=hwy, y=cty)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_smooth(method = "lm") # trendlijn linear model (= lineaire regressie)

3.3.2.1.2 Uitgebreid
# data
mpg %>% 
    mutate(cyl = as.factor(cyl)) %>% # categorische variabele van cyl maken
    
    # canvas / globale toewijzingen
    ggplot(aes(x=hwy, y=cty)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_jitter(aes(color = cyl),
                alpha = 0.5) +
    geom_hline(yintercept = mean(mpg$cty)) +
    geom_vline(xintercept = mean(mpg$hwy)) +
    geom_smooth(method = "lm") +
    
    # opmaak
    labs(
        title    = "Stad vs snelweg verbruik",
        subtitle = "Modellen met 4 cilinders doen het bovengemiddeld goed",
        x        = "Mijlen per gallon op de snelweg",         # titel x-as
        y        = "Mijlen per gallon in de stad",            # titel y-as
        caption  = "data set: mpg"
    ) +
    theme_minimal()

Als uitgebreid voorbeeld hier geom_smooth() toegevoegd aan het uitgebreide voorbeeld voor geom_jitter(). Veelal zul je geom_smooth() in combinatie met een spreidingsgrafiek gebruiken. Het aanroepen van geom_smooth() hierin niet anders dan in het eenvoudige voorbeeld. De overige delen komen uit het uitgebreide voorbeeld rondom geom_jitter().

3.3.3 Tekst - geom_text()

Tekst in een grafiek kan heel verhelderend zijn. Bijvoorbeeld door waardes of labels in een grafiek toe te voegen. Maar ook door specifieke waardes uit te lichten of een zogenaamde wordcloud (die wordt hier niet behandeld).

Tekst in een grafiek zul je vaak, maar niet altijd combineren met andere weergavelagen. Dat hebben we bijvoorbeeld al gezien bij het uitgebreide staafdiagram. De waardes in de balken/staven zijn tekstwaardes. Een ander voorbeeld waarin labels vaak gebruikt worden is een lollipop-diagram. Daarvan is een voorbeeld opgenomen in Lollipop-diagram.

3.3.3.1 Voorbeelden

3.3.3.1.1 Eenvoudig
# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=hwy, y=cty)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_text(aes(label = cty))

3.3.3.1.2 Uitgebreid
# data
mpg %>% 
    # samenvatten per type aandrijving
    group_by(drv) %>%                
    summarise(cty = mean(cty), 
              hwy = mean(hwy)) %>% 
    ungroup() %>% 
    # drv in leesbare termen
    mutate(drv = recode(drv, 
                        f = "voorwielaandrijving", 
                        r = "achterwielaandrijving", 
                        "4" = "vierwielaandrijving", 
                        .default = NA_character_)) %>% 
    
    # canvas / globale toewijzingen
    ggplot(
        aes(x = cty, y = hwy, color = drv) # data toewijzen x-as, y-as en kleur
        ) + 
    
    # weergavelagen
    # laag met punten voor waardes
    geom_point() +
    # laag met tekst
    geom_text(
        aes(label = drv), # tekst toewijzen aan drv variabele
        hjust = 0.5,      # horizontale aanpassing positie tekst
        vjust = -0.5      # verticale aanpassing positie tekst
        ) +
    
    # opmaak
    labs(
        title    = "Stad vs snelweg verbruik",
        subtitle = "Modellen met Voorwielaandrijving zijn het zuinigst",
        x        = "Mijlen per gallon op de snelweg",         # titel x-as
        y        = "Mijlen per gallon in de stad",            # titel y-as
        caption  = "data set: mpg"
    ) +
    theme_minimal() +
    expand_limits(x = c(5, 25),    # x-as uitbreiden om de tekst volledig te tonen
                  y = c(15, 30)) + # y-as uitbreiden om de tekst volledig te tonen
    theme(legend.position = "none")

Voor het uitgebreide voorbeeld zijn de volgende stappen genomen:

  1. De data voorbereiden voor de grafiek;

  2. cty aan de x-as en hwy aan de y-as toewijzen, de kleur wordt bepaald door de waarde van drv;

  3. Tekst toevoegen geom_text(), waarbij de tekst bepaald wordt door de variabele drv en de positie zowel horizontaal als verticaal aangepast wordt (daarover later meer;

  4. Titels en dergelijke toevoegen labs();

  5. Minimale opmaak voor het thema theme_minimal();

  6. De boven- en ondergrens van de x- en y-as vastleggen via expand_limits();

  7. De legenda verwijderen via theme();

3.4 1 categorische en 1 continue variabele

Van de weergavelagen op het spiekbriefje zal ik aandacht besteden aan:

  • Het staafdiagram: geom_bar()

  • De boxplot: geom_boxplot()

  • Het viooldiagram: geom_violin()

3.4.1 Staafiagram - geom_bar() / geom_col()

We hebben het staafdiagram al eerder gezien, namelijk in de paragraaf 1 categorische variabele.
Daar werd het staafdiagram getoond met 1 categorische variabele en een telling van het aantal waarnemingen. Net als een histogram dus.

Nu dus een staafdiagram met 1 categorische variabele en 1 continue. Het voorbeeld van het spiekbriefje is een weinig betekenisvol voorbeeld. Het toont de som van het aantal mijlen per gallon als waarde van de class (type) auto. De continue variabele kun je echter niet zo op een betekenisvolle manier optellen. Voor het voorbeeld heb ik dus eerst het gemiddelde berekend, zodat de gemiddelde afstand per model per gallon getoond wordt.

geom_bar() verwacht eigenlijk geen y-waarde. Het gedraagt zich standaard als een histogram (telt het aantal waarnemingen en toont die waardes). Als je de gegevens wil tonen zoals ze in de dataset voorkomen (of hoe jij ze zelf samenvat), dan moet je stat = "identity" gebruiken.
Je zou ook geom_col() kunnen gebruiken. Standaard toont deze de waardes die je aanlevert. Hieronder zie je dat beide varianten exact dezelfde grafiek opleveren.
In deze categorie gaat mijn voorkeur daarom uit naar geom_col().

3.4.1.1 Voorbeelden

3.4.1.1.1 Eenvoudig
# data
mpg %>% 
    # 1 modeljaar laten zien
    filter(year == 2008) %>% 
    # gemiddelde per `class` uitrekenen
    group_by(class) %>% 
    summarise(hwy = mean(hwy)) %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=class, y=hwy)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_bar(stat = "identity") +
    labs(title = "geom_bar(stat = 'identity')")

# data
mpg %>% 
    # 1 modeljaar laten zien
    filter(year == 2008) %>% 
    # gemiddelde per `class` uitrekenen
    group_by(class) %>% 
    summarise(hwy = mean(hwy)) %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=class, y=hwy)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_col() +
    labs(title = "geom_col()")

3.4.1.1.2 Uitgebreid
# data
mpg %>% 
    # 1 modeljaar laten zien
    filter(year == 2008) %>% 
    # gemiddelde per `class` uitrekenen
    group_by(class) %>% 
    summarise(hwy = mean(hwy)) %>% 
    arrange(desc(hwy)) %>% 
    mutate(class = as_factor(class) %>% fct_rev()) %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=class, y=hwy, fill = class)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_col() +
    
    # opmaak
    coord_flip() +
    labs(
        title    = "Gemiddelde afstand op de snelweg",  # grafiek titel
        subtitle = "naar type auto",                    # subtitel
        x        = NULL,                                # titel x-as
        y        = "Mijlen per gallon op de snelweg",   # titel y-as
        caption  = "data set: mpg"                      # toelichting
    ) +
    theme_minimal() +
    theme(legend.position = "none")

Aanpassingen voor het uitgebreide voorbeeld:

  1. De data bewerken zodat er gesorteerd wordt op de waarde in hwy;

  2. Opvulkleur per categorie van class via fill = class in aes();

  3. Uitgebreide titels via labs();

  4. Minimale thema toepassen via theme_minimal();

  5. De legenda verwijderen via theme().

3.4.2 boxplot - geom_boxplot()

De boxplot geeft inzicht in de verdeling van de waarnemingen van de variabele. Voor uitleg zie boxplot op wikipedia.

3.4.2.1 Voorbeelden

3.4.2.1.1 Eenvoudig
# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=class, y=hwy)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_boxplot()

3.4.2.1.2 Uitgebreid
# data
mpg %>% 
    # sorteren op mediaan van hwy
    group_by(class) %>% 
    mutate(hwy_median = median(hwy)) %>% 
    ungroup() %>% 
    arrange(desc(hwy_median)) %>%
    mutate(class = as_factor(class) %>% fct_rev()) %>% 
    
    # canvas / globale toewijzingen
    # data toewijzen x- en y-as & kleur toewijzen obv class
    ggplot(aes(x=class, y=hwy, fill = class)) +
    
    # weergavelagen
    stat_boxplot(geom = "errorbar") + # voegt de dwarsliggende lijnen toe
    geom_boxplot(notch = TRUE) +      # inkeping bij de mediaan (90% vd waarn.)

    # opmaak
    coord_flip() +  # grafiek kantelen, zodat class op de y-as komt
    labs(
        title    = "Verdeling afstand per gallon",    # grafiektitel
        subtitle = "naar type auto",                  # subtitel
        x        = NULL,                              # titel x-as
        y        = "Mijlen per gallon op de snelweg", # titel y-as
        caption  = "data set: mpg"                    # toelichting
    ) +
    theme_minimal() +
    theme(legend.position = "none")

Aanpassingen voor de uitgebreide versie:

  1. De data bewerken zodat er gesorteerd wordt op de mediane waarde in hwy;
  2. Opvulkleur per categorie van class via fill = class in aes();
  3. Foutbalken toevoegen via stat_boxplot(geom = "errorbar");
  4. Inkeping bij de mediane waarde door notch = TRUE te gebruiken;
  5. Uitgebreide titels via labs();
  6. Minimale thema toepassen via theme_minimal();
  7. De legenda verwijderen via theme().

3.4.3 viooldiagram - geom_violin()

Het viooldiagram is een bewerking van de boxplot, die de verdeling wat beter laat zien. Daar waar het diagram smal is, zijn weinig waarnemingen en waar het breed is, veel.

3.4.3.1 Voorbeelden

3.4.3.1.1 Eenvoudig
# data
mpg %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=class, y=hwy)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_violin()

3.4.3.1.2 Uitgebreid
# data
mpg %>% 
    # sorteren op mediaan van hwy
    group_by(class) %>% 
    mutate(hwy_median = median(hwy)) %>% 
    ungroup() %>% 
    arrange(desc(hwy_median)) %>%
    mutate(class = as_factor(class) %>% fct_rev()) %>% 
    
    # canvas / globale toewijzingen
    # data toewijzen x- en y-as & kleur toewijzen obv class
    ggplot(aes(x=class, y=hwy, fill = class, color = class)) +    
    
    # weergavelagen
    geom_boxplot(notch = TRUE, alpha = 0.5) +
    geom_jitter(alpha = 0.25) +
    geom_violin(alpha = 0.5) +
    
    
    # opmaak
    coord_flip() +
    labs(
        title    = "Verdeling afstand per gallon",
        subtitle = "naar type auto",
        x        = NULL,         # titel x-as
        y        = "Mijlen per gallon op de snelweg",            # titel y-as
        caption  = "data set: mpg"
    ) +
    theme_minimal() +
    theme(legend.position = "none")

Aanpassingen voor de uitgebreide versie:

  1. De data bewerken zodat er gesorteerd wordt op de mediane waarde in hwy;
  2. Opvulkleur en randkleur per categorie van class via fill = class, color = class in aes();
  3. Een half doorzichtige boxplot met inkeping toevoegen via geom_boxplot();
  4. Waarnemingen weergeven via geom_jitter();
  5. geom_violin() gedeeltelijk doorzichtig maken via alpha = 0.5;
  6. Uitgebreide titels via labs();
  7. Minimale thema toepassen via theme_minimal();
  8. De legenda verwijderen via theme().

3.5 2 categorische variabelen

Van de weergavelagen op het spiekbriefje zal ik aandacht besteden aan:

  • De spreidingsgrafiek: geom_jitter()

3.5.1 Spreidingsgrafiek - geom_jitter()

3.5.1.1 Voorbeelden

3.5.1.1.1 Eenvoudig
# data
diamonds %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=cut, y=color, color = clarity)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_jitter()

Persoonlijk vind ik het bovenstaande geen goed voorbeeld. Ik heb al de clarity toegevoegd om het aangenamer te maken voor het oog. Wat goed te zien is, is waar er veel en waar er weinig waarnemingen zijn.

3.5.1.1.2 Uitgebreid
# data
msleep %>% 
    select(conservation, order, vore) %>% 
    drop_na() %>% 
    arrange(order, conservation) %>% 
    mutate(order = as_factor(order) %>% fct_rev()) %>% 
    
    # canvas / globale toewijzingen
    ggplot(aes(x=conservation, y=order, color = vore)) +    # data toewijzen x- en y-as
    
    # weergavelagen
    geom_count() +
    
    # opmaak
    labs(
        title = "Mammals in sleep dataset",
        y     = "Mammal order",
        color = "diet"
    ) +
    theme_minimal() 

Dit voorbeeld is gemaakt met geom_count().

Er zijn nog wel andere mogelijkheden, maar die worden hier niet behandeld.

3.6 3 variabelen

Van de weergavelagen op het spiekbriefje zal ik aandacht besteden aan:

  • Het tegeldiagram: geom_tile()

3.6.1 Tegeldiagram - geom_tile()

Met deze geom teken je rechthoeken, die met de z-variabele een kleur of tint. Dit maakt deze visualisatie erg geschikt om heatmaps te maken.

De dataset die in het spiekbriefje gebruikt wordt, spreekt niet erg aan. Er is voor de dataset diamonds gekozen, omdat ik deze beter/duidelijk vind voor het voorbeeld.

3.6.1.1 Voorbeelden

3.6.1.1.1 Eenvoudig
 diamonds %>% 
     group_by(color, cut) %>% 
     summarise(price = median(price)) %>% 
     ungroup() %>% 
     ggplot(aes(x = color, y = cut, z = price)) + 
     geom_tile(aes(fill = price))

3.6.1.1.2 Uitgebreid
# data
# welke categorie heeft de hoogste prijs
best_cat <- 
    diamonds %>% 
    group_by(color, cut) %>% 
    summarise(price = median(price)) %>% 
    ungroup() %>% 
    slice_max(order_by = price, n = 1)
# levels voor variabelen om een rand toe te voegen aan de cel met de hoogste prijs
color_list <- levels(diamonds$color)
cut_list   <- levels(diamonds$cut)

# grafiek opbouwen
# data
diamonds %>% 
    group_by(color, cut) %>% 
    summarise(price = median(price)) %>% 
    ungroup() %>% 
    
    # ggplot
    ggplot(aes(x = color, y = cut, z = price)) + 
    # weergavelagen
    # - tegeldiagram
    geom_tile(aes(fill = price)) +
    # - waarden in cellen
    geom_text(aes(label = scales::dollar(price, accuracy = 100)), 
              color = "white") +
    # - rand toevoegen aan de hoogste waarde
    geom_rect(size = 2, fill = NA, colour="white",
            aes(xmin=which((color_list %>% fct_rev()) %in% best_cat$color) - 0.5, 
                xmax=which(color_list %in% best_cat$color) + 0.5, 
                ymin=which(cut_list %in% best_cat$cut) - 0.5, 
                ymax=which(cut_list %in% best_cat$cut) + 0.5)) +
    
    # opmaak
    labs(
        title    = "Prijs van diamanten",
        subtitle = "Mediane prijs per groep",
        x        = "kleur",
        y        = "slijpvorm",
        color    = "prijs"
    ) +
    theme_minimal() 

Voor het uitgebreide voorbeeld zijn de volgende stappen genomen:

  1. De data voorbereiden voor de grafiek;

  2. Vulkleur op basis van price;

  3. de waarde van price weergeven in elke “tegel”;

  4. Een rand rondom de tegel met de hoogste prijs met geom_rect();

  5. Titels en dergelijke toevoegen labs();

  6. Minimale opmaak voor het thema theme_minimal().

3.7 Kaarten

Als je serieus met geografische informatie aan de slag gaat, dan raad ik je aan om kaarten te maken met packages die daar erg geschikt voor zijn, zoals leaflet en mapview. Deze tools vragen wel om data met een geografische component

Eenvoudige kaarten kunnen ook in ggplot gemaakt worden met het maps package.

3.7.1 geom_map()

3.7.1.1 Voorbeelden

3.7.1.1.1 Eenvoudig
data <- 
    data.frame(murder = USArrests$Murder, 
               state  = tolower(rownames(USArrests)))
map <- map_data("state")

data %>% 
    ggplot(aes(fill = murder)) + 
    geom_map(aes(map_id = state), map = map) +
    expand_limits(x = map$long, y = map$lat)

3.7.1.1.2 Uitgebreid
# data
data <- 
    data.frame(murder = USArrests$Murder, 
               state  = tolower(rownames(USArrests)))
map <- map_data("state")

data %>% 
    ggplot(aes(fill = murder)) + 
    # weergavelagen
    geom_map(aes(map_id = state), map = map) +
    expand_limits(x = map$long, y = map$lat) +  #
    
    # opmaak
    labs(
        title    = "Hoe gevaarlijk is een 'US state?'",
        x        = "",
        y        = "",
        fill    = paste0("aantal\nmoorden")
    ) +
    theme_minimal() +
    theme(axis.text  = element_blank(),
          panel.grid = element_blank())

Voor het uitgebreide voorbeeld zijn de volgende stappen genomen:

  1. Titels en dergelijke toevoegen labs();

  2. Minimale opmaak voor het thema theme_minimal();

  3. Verwijderen van de waardes op de x- en y-as met theme(axis.text = element_blank());

  4. Het raster verwijderen met theme(panel.grid = element_blank()).

3.8 Tijdreeksen

Tijdreeksen zijn vaak lijndiagrammen, maar kunnen ook bijvoorbeeld boxplots zijn. De overeenkomst is dat er altijd een tijd, datum of periode op de x-as staat. Je kunt deze grafieken/diagrammen helemaal met ggplot2 maken. Als je hier serieus mee aan de slag wil, dan zou ik je aanraden om het timetk package te gebruiken, waarin mooie plot-functies opgenomen zijn.

3.8.0.1 Voorbeelden

3.8.0.1.1 Eenvoudig
# data
economics %>% 
    
    # basis grafiek
    ggplot(aes(date, unemploy)) +
    geom_line()

3.8.0.1.2 Uitgebreid
# data
economics %>% 
    
    # basis grafiek
    ggplot(aes(date, unemploy)) +
    # weergavelagen
    geom_line() +
    geom_smooth(se = FALSE) +
    
    # opmaak
    labs(
        title    = "Werkloosheid in de VS",
        x        = "",
        y        = "x 1000 personen",
        caption  = "bron: https://fred.stlouisfed.org/series/UNEMPLOY"
    ) +
    theme_minimal() +
    scale_y_continuous(labels = scales::comma)

Voor het uitgebreide voorbeeld zijn de volgende stappen genomen:

  1. Toevoegen van een trendlijn middels geom_smooth(se = FALSE) (zonder bandbreedte);

  2. Titels en dergelijke toevoegen labs();

  3. Minimale opmaak voor het thema theme_minimal();

  4. Waardes op de y-as netjes opmaken met scale_y_continuous(labels = scales::comma).

3.9 Visualiseren van fouten

Zelf vind ik de duidelijkste variant om een foutrange aan te geven een combinatie van geom_errorbar() met geom_point().

Deze zal ik hieronder laten zien.

Voor het visualiseren van fouten wordt fictieve data gegenereerd en niet een dataset uit het ggplot2 package gebruikt. Het eenvoudige voorbeeld ziet er dan als volgt uit:

3.9.0.1 Voorbeelden

3.9.0.1.1 Eenvoudig
# data
df <- data.frame(grp = c("A", "B", "C"), fit = c(4, 5, 5.5), se = c(1, 2, 0.5))
# om de data te bekijken
df
##   grp fit  se
## 1   A 4.0 1.0
## 2   B 5.0 2.0
## 3   C 5.5 0.5
df %>% 
    
    # basis grafiek
    ggplot(
        aes(x = grp,       # variabele voor de x-as
            y = fit,       # punt van de waarneming
            ymin = fit-se, # y-waarde voor de onderste foutbalk
            ymax = fit+se) # y-waarde voor de bovenste foutbalk
           ) +
    
    # weergavelagen
    geom_errorbar() +        # foutbalken
    geom_point()             # punt van de waarneming

3.9.0.1.2 Uitgebreid
# data
df <- data.frame(grp = c("A", "B", "C"), fit = c(4, 5, 5.5), se = c(1, 2, 0.5))
# om de data te bekijken
df
##   grp fit  se
## 1   A 4.0 1.0
## 2   B 5.0 2.0
## 3   C 5.5 0.5
df %>% 
    
    # basis grafiek
    ggplot(
        aes(x     = grp,    # variabele voor de x-as
            color = grp,    # kleur voor waarneming en lijnen
            y     = fit,    # punt van de waarneming
            ymin  = fit-se, # y-waarde voor de onderste foutbalk
            ymax  = fit+se) # y-waarde voor de bovenste foutbalk
           ) +
    
    # weergavelagen
    geom_errorbar(size = 1) +  # foutbalken
    geom_point(size = 3) +     # punt van de waarneming (wat groter gemaakt)
    
    # opmaak
    labs(
        title    = "Waarneming en foutmarge per categorie",
        x        = "",
        y        = "waarde"
    ) +
    expand_limits(y = c(0, max(df$fit)+max(df$se))) + # y-as beginnen bij 0
    theme_minimal() + 
    theme(legend.position = "none")  # verwijderen van de legenda

Voor het uitgebreide voorbeeld zijn de volgende stappen genomen:

  1. Kleur op basis van de variabele grp toegevoegd in de aes();

  2. De dikte van de errorbar en punt van de waarneming iets vergroot door het argeument size te gebruiken;

  3. Titels en dergelijke toevoegen labs();

  4. Y-as bij 0 laten beginnen met

  5. Minimale opmaak voor het thema theme_minimal();

  6. Legenda verwijderd met theme(legend.position = "none").

4 Aanpassingen in posities (position)

Positie aanpassingen bepalen hoe overlappende weergaves getoond worden.

De opties zijn:

  1. dodge –> naast elkaar

  2. fill –> stapelt tot 100%

  3. stack –> stapelt op basis van de waardes

  4. jitter –> voegt ruis toe, zodat waardes niet overlappen

Het spiekbriefje legt het eigenlijk heel goed uit.

De basisgrafiek is als volgt:

4.0.0.1 Voorbeelden

4.0.0.1.1 basis
mpg %>% 
    
    # basis grafiek
    ggplot(aes(x = fl, fill = drv)) +
    geom_bar()

4.0.0.1.2 dodge

Als we position = "dodge" gebruiken, dan worden de balken naast elkaar geplaatst.

mpg %>% 
    
    # basis grafiek
    ggplot(aes(x = fl, fill = drv)) +
    # positie van de balken
    geom_bar(position = "dodge")

Je kunt met position = position_dodge(width = x) ook aangeven hoe ver de verschuiving moet zijn. Met x = 0.5 zullen de balken elkaar overlappen en met x = 1 zullen de balken iets uit elkaar staan (zie hieronder).

Deze instelling kun je ook voor andere weergavelagen (geoms) gebruiken. Zie daarvoor
?postion_dodge().

mpg %>% 
    
    # basis grafiek
    ggplot(aes(x = fl, fill = drv)) +
    # positie van de balken
    geom_bar(position = position_dodge(width = 1))

4.0.0.1.3 fill

Als we position = "fill" gebruiken, dan worden de balken gestapeld en genormaliseerd (stapelt tot 100%). Dit is erg geschikt om het aandeel per categorie aan te geven.

mpg %>% 
    
    # basis grafiek
    ggplot(aes(x = fl, fill = drv)) +
    # positie van de balken
    geom_bar(position = "fill")

4.0.0.1.4 stack

Als we position = "stack" gebruiken, dan worden de balken gestapeld zonder verdere bewerking. Dit is de standaardinstelling. Daarmee is de grafiek dus hetzelfde als de basis grafiek.

mpg %>% 
    
    # basis grafiek
    ggplot(aes(x = fl, fill = drv)) +
    # positie van de balken
    geom_bar(position = "stack")

4.0.0.1.5 jitter

Om duidelijk te maken wat position = "jitter" (net als geom_jitter()) doet, is hieronder een grafiek opgenomen met de originele punten in rood en sterk doorzichtig en de "jitter" in het zwart met enige doorzichtigheid.

Doordat sommige punten roder zijn dan anderen, kunnen we zien dat er overlap in waarnemingen is. De zwarte punten kennen nauwelijks overlap.

mpg %>% 
    
    # basis grafiek
    ggplot(aes(x = cty, y = hwy)) +
    # spreidingsgrafiek met doorzichtige punten
    geom_point(color = "red", alpha = 0.25) + 
    # jitter om individuele punten beter zichtbaar te maken
    geom_point(position = "jitter", alpha = 0.5)

5 Schalen (scale)

Schalen kunnen in belangrijke mate bepalen hoe je grafiek eruit ziet. Ze hebben invloed op:

  • De doorzichtigheid (scale_alpha_*)

  • Kleuren in je grafiek (scale_color_*, scale_fill_*)

  • Vormen (scale_shape_*, scale_linetype_*)

  • De afmetingen van o.a. lijnen en punten (scale_size_*)

  • De assen (scale_x_*, scale_y_*)

Op elk van de * (behalve voor de assen) kunnen voorkomen:

  • continuous, bijvoorbeeld scale_fill_continous

  • discrete, bijvoorbeeld: scale_color_discrete

  • identity, bijvoorbeeld: scale_color_identity

  • manual, bijvoorbeeld: scale_color_manual

Het spiekbriefje gaat in op:

  1. Schalen en kleuren
  2. Schalen en vormen
  3. Schalen en afmetingen

5.1 Schalen en kleuren

Hiermee heb je controle over het kleurgebruik in de grafiek.

In de voorbeelden van het spiekbriefje worden alleen voorbeelden met vulkleuren getoond. Je zult hieronder ook voorbeelden van color vinden in plaats van alleen met fill.

5.1.1 Discreet, vulkleuren

5.1.1.1 Voorbeelden

5.1.1.1.1 Basis
# zonder aanpassingen
mpg %>% 
    ggplot(aes(x = fl)) +
    geom_bar()

5.1.1.1.2 Met discrete vulkleur
# blauwe balken volgens het aangegeven palette op brandstof type
mpg %>% 
    ggplot(aes(x = fl)) +
    geom_bar(aes(fill = fl)) +
    scale_fill_brewer(palette = "Accent")

5.1.2 Discreet kleur

5.1.2.1 Voorbeelden

5.1.2.1.1 Basis
# zonder aanpassingen
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point()

5.1.2.1.2 Met discrete kleur
# kleuren op basis van de typologie van de auto
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(color = class)) +
    scale_color_brewer(palette = "Accent")

5.1.3 Continue vulkleuren

5.1.3.1 Voorbeelden

5.1.3.1.1 Basis
# zonder aanpassingen
mpg %>% 
    ggplot(aes(x = hwy)) +
    geom_bar()

5.1.3.1.2 Met continue vulkleur
# ingekleurd op basis van brandstofgebruik op de snelweg
mpg %>% 
    ggplot(aes(x = hwy)) +
    geom_bar(aes(fill = ..x..)) + # ..x.. verwijst naar x-waarde van de balk
                                  # dat is dus het resultaat van het groeperen van
                                  # de x-waarde in aes(x = hwy)
    scale_fill_gradient(
        low = "red",
        high = "yellow"
        )

5.1.4 Continue kleur

5.1.4.1 Voorbeelden

5.1.4.1.1 Basis
# zonder aanpassingen
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point()

5.1.4.1.2 Met continue kleur
# kleuren op basis van de typologie van de auto
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(color = displ)) +
    scale_color_gradient(
        low = "blue",
        high = "red"
        )

5.2 Schalen en vormen

Hiermee heb je controle over welke vormen je in de grafiek laat zien. Bij geom_point() is dit standaard een gevuld bolletje.

Het spiekbriefje biedt hier, behalve een voorbeeld ook een handig overzicht van welk cijfer bij welke vorm hoort.
# zonder aanpassingen
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(shape = fl)) # formen op basis van type brandstof

# toon alleen de randen/lijnen van de vorm
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(shape = fl)) + 
    scale_shape(solid = FALSE)

# handmatig gekozen vormen
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(shape = fl)) +
    scale_shape_manual(values = c(3:7))

5.3 Schalen en afmetingen

Hiermee heb je controle over bijvoorbeeld de grootte van de punten in een spreidingsgrafiek.

Let op: het voorbeeld op het spiekbriefje klopt hier niet. Het argument max is vervangen door max_size.
# zonder aanpassingen, met grootte op basis van het aantal cylinders
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(size = cyl))

# maximale puntgrootte beperken
mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point(aes(size = cyl)) +
    scale_size_area(max_size = 6)

6 Coördinatensysteem (coord)

Het coördinatensysteem heeft invloed op de x- en de y-as van de grafiek.

Niet alle mogelijkheden worden vaak (door mij) gebruikt. Daarom worden ook niet alle mogelijkheden behandeld.

Hieronder zal gekeken worden naar:

  1. coord_cartesian()
  2. coord_flip()
  3. coord_trans()

6.1 Cartesiaanse coördinaten

Dit is het normale coördinatensysteem voor grafieken. Je kunt daarbij als het ware inzoomen op de grafiek door grenswaardes voor de x- en/of y-as op te geven.

# zonder aanpassingen
g1 <- mpg %>% 
    ggplot(aes(x = hwy)) +
    geom_bar() +
    labs(title = "zonder aanpassingen")

# alleen de waardes (op de x-as) tussen 10 en 20 tonen
g2 <-  mpg %>% 
    ggplot(aes(x = hwy)) +
    geom_bar() +
    coord_cartesian(xlim = c(10, 20)) +
    labs(title = "x-as beperkt tot tussen 10 en 20")

g1 + g2 # grefieken naast elkaar tonen

6.2 Assen wisselen

Door coord_flip() te gebruiken, kun je de x- en y-as omwisselen. Je kunt dan bijvoorbeeld een liggend staafdiagram maken.

# zonder aanpassingen
g1 <- mpg %>% 
    ggplot(aes(x = fl)) +
    geom_bar() +
    labs(title = "zonder aanpassingen")

# alleen de waardes (op de x-as) tussen 10 en 20 tonen
g2 <- mpg %>% 
    ggplot(aes(x = fl)) +
    geom_bar() +
    coord_flip() +
    labs(title = "x- en y-as omgewisseld")

g1 + g2 # grefieken naast elkaar tonen

6.3 Schaal as omrekenen

# zonder aanpassingen
g1 <- mpg %>% 
    ggplot(aes(x = hwy)) +
    geom_bar() +
    labs(title = "zonder aanpassingen")

# alleen de waardes (op de x-as) tussen 10 en 20 tonen
g2 <- mpg %>% 
    ggplot(aes(x = hwy)) +
    geom_bar() +
    coord_trans(x = "log10", y = "sqrt") + # omrekenen van de assen
    labs(title = "x-as in log schaal, y-as wortel")

g1 + g2 # grafieken naast elkaar tonen

7 Ordening van meerdere grafieken (facet)

Ggplot2 kent ook handige functies om je grafiek volgens een discrete variabele op te delen in meerdere grafieken. Dit wordt vaak ook “small multiples” genoemd (bijvoorbeeld in Power BI).

Met facet_grid bepaal je welke variabele op de x-as en/of y-as uitgesplitst zal worden.
Als je 2 variabelen levert dat een matrix op met alle categorieën van de ene variabele op de x-as en alle categorieën van de andere variabele op de y-as. Dit kan lege grafieken opleveren

Met facet_wrap worden alle grafieken met gegevens gemaakt en vervolgens getoond in het aantal rijen/kolommen dat je aangeeft.

Voorbeelden van facet_grid() worden hieronder uitgewerkt. Daarbij heb ik een beter leesbare code gebruikt dan op het spiekbriefje. In plaats van .~variabele is dit uitgeschreven als cols = vars(variabele) en variabele~. is uitgeschreven als rows = vars(variabele).
Dit is uiteraard ook te combineren als cols = vars(kolom-variabele), rows = vars(rij-variabele).

Om te beginnen een eenvoudig voorbeeld waarbij de grafieken automatisch uitgesplits worden in een aantal kolommen uitgesplits wordt op basis van de fl variabele (= het brandstof type).

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point() +
    labs(title = "uitgesplits per brandstof type")

# uitsplitsen in grafieken op basis van fl
t + facet_grid(cols = vars(fl))

Op dezelfde manier kun je ook uitsplitsen per rij. In onderstaand voorbeeld gebeurt dit op basis van van het bouwjaar.

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point() +
    labs(title = "uitgesplitst naar bouwjaar")

# uitsplitsen in grafieken op basis van year
t + facet_grid(rows = vars(year))

Vervolgens een combinatie met uitsplitsing in kolommen op basis van brandstof type en rijen io basis van bouwjaar.

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point() +
    labs(title = "uitgesplitst naar brandstof type én bouwjaar")

# uitsplitsen in grafieken op basis van year + brandstof type
t + facet_grid(cols = vars(fl), rows = vars(year))

In deze grafiek valt op dat dat sommige combinaties leeg blijven. Bij facet_grid wordt elke mogelijke combinatie getoond. Ook als er geen gegevens zijn om in deze combinatie van variabelen.
Als je dat wil voorkomen, kun je facet_wrap gebruiken. Dezelde grafiek als hierboven maak je dan als volgt:

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point() +
    labs(title = "uitgesplitst naar brandstof type én bouwjaar")

# uitsplitsen in grafieken op basis van year + brandstof type
t + facet_wrap(facets = vars(fl, year))

Voordeel is dat alleen combinaties met gegevens getoond worden. De variant met facet_grid kan toch de voorkeur hebben, omdat categoriën makkelijker te vergelijken zijn.

Wellicht is het opgevallen dat de assen van alle grafieken met dezelfde reeks aan waarden werken. Dit kun je aanpassen door het argument scales te gebruiken. Je hebt daarbij de keuze uit:

  • “free_x”: waardes op de x-as kunnen variëren;
  • “free_y”: waardes op de y-as kunnen variëren;
  • “free”: waardes kunnen variëren op zowel de x- als de y-as.
# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point() +
    labs(title = "uitgesplitst naar brandstof type én bouwjaar")

# uitsplitsen in grafieken op basis van year + brandstof type
# & "vrije" x- en y-as
t + facet_wrap(facets = vars(fl, year), scales = "free")

Ook hier heb je te maken met een afweging tussen duidelijke individuele grafieken en vergelijkbaarheid.

8 Titels/labels en legenda

Titel, labels van assen, legenda en dergelijke kunnen erg belangrijke elementen zijn voor de leesbaarheid van de grafiek/visual die je maakt.
Het is aan te raden om hier goed over na te denken, en geen overbodige informatie te tonen. Wat een goede titel, legenda, enzovoort is, is geen onderdeel van deze uitleg. Maar het loont beslist de moeite om goed na te denken over deze onderdelen.

8.1 Titels en labels

Titel en labels zijn op meerdere manieren toe te voegen. De eenvoudigste manier is om de labs te gebruiken in plaats van een functie voor elk van de onderdelen die je toe wil voegen.

In het onderstaande voorbeeld zijn de veel gebruikte opties van labs meegenomen.

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy)) +
    geom_point()

# toevoegen van label opties
t  +
    labs(
        title    = "Dit is de titel",
        subtitle = "Dit is de subtitel",
        caption  = "Dit is het bijschrift (caption)", 
        x        = "x-as label",
        y        = "y-as label"
        )

8.2 Legenda

Een legenda kun je op verschillende manieren toe- voegen c.q. bewerken. Er zijn behoorlijk wat mogelijkheden voor aanpas- singen. Buiten de mogelijkheden op het spiek- briefje, vind je hieronder ook nog enkele andere mogelijkheden.

Bedenk bij legenda’s altijd dat deze afhankelijk zijn van de aesthetics (of aes) die je toevoegt. Je krijgt bijvoorbeeld een legenda op basis van color, op basis van fill, op basis van size en op basis van shape.
Bij een combinatie van deze aesthetics, zullen er meerdere legenda’s aan de grafiek toegevoegd worden

8.2.1 legenda positie

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy, color = fl)) +
    geom_point()

t + theme(legend.position = "bottom")      # met een steekwoord, zoals op het spiekbriefje

t + theme(legend.position = c(0.7, 0.2),   # exacte positie
          legend.direction = "horizontal")

# legenda niet tonen
t + theme(legend.position = "none") + 
    labs(subtitle = "geen legenda door: theme(legend.position = 'none')")

t + guides(color = "none") +
    labs(subtitle = "geen legenda voor 'color' door: guides(color = 'none')")

### legenda titel

# basis grafiek
t <- mpg %>% 
    ggplot(aes(x = cty, y = hwy, color = fl)) +
    geom_point()

# legenda titel
t + theme(legend.title = element_blank()) + 
    labs(subtitle = "geen legenda titel door: theme(legend.title = element_blank()")

t + labs(color = "Legenda titel via labs")

t + scale_color_discrete(name = "Legenda tital via scale_color", labels = c("A", "B", "C", "D", "E"))

9 Kleur(en)

Hoewel kleuren al in 5 Schalen (scale) behandeld zijn, is het goed om er een extra onderdeel aan te wijden.

Kleuren zijn immers erg belangrijk in visuals. Zij bepalen vaak voor een groot deel hoe een visual gezien/geïnterpreteerd wordt (en hoe aantrekkelijk de visual gevonden wordt). In ggplot2 kun je elke kleur gebruiken die je kunt bedenken. Het gaat echter vaak om de combinaties van kleuren en hoe ze in combinatie met elkaar gezien worden.
Hoewel dit niet in het spiekbriefje te vinden is, is het een dusdanig belangrijk onderwerp dat het hier toch behandeld wordt.
Zo kun je consequent kleuren gebruiken voor bepaalde groepen, maar ook de aandacht naar een bepaald onderdeel trekken door kleurgebruik (bijvoorbeeld door de groepen die niet belangrijk zijn grijs te maken).

De inspiratie voor dit onderdeel komt van de volgende blog: https://paulvanderlaken.com/2020/03/20/how-to-standardize-group-colors-in-data-visualizations-in-r/

Het kleurenpallet dat je gebruikt, is uiteraard ook erg belangrijk. Dat zal hier echter niet specifiek behandeld worden. Je kunt uit het onderstaande wel een idee krijgen hoe je zo’n kleurenpallet maakt. Als je echt visuals op maat wil maken, met een consequente opmaak, bekijk dan vooral ook het volgende hoofdstuk ‘Thema’s’.

9.1 Kleuren gelijk houden voor categorieën

Laten we als eerste naar een visaulisatie voor de mpg dataset kijken. Hieronder wordt het verbruik (miles per galon) in de stad vergeleken naar klasse. Daarbij worden de kleuren uit het tidyquant pakket gebruikt, omdat deze er standaard aantrekkelijker uitzien dan de kleuren die ggplot toont.

# data
mpg %>% 
    group_by(class) %>% 
    summarise(avg_cty = mean(cty)) %>% 
    ungroup() %>% 
    
    # grafiek met standaardinstellingen
    ggplot(aes(x = class, y = avg_cty, fill = class)) +
    geom_col() +
    tidyquant::scale_fill_tq()

Als je in de analyse dan “in wil zoomen” op bepaalde klassen, bijvoorbeeld met een verbruik in de stad (cty) > 18 miles per galon, dan gebeurt er het volgende:

# data
mpg %>% 
    group_by(class) %>% 
    summarise(avg_cty = mean(cty)) %>% 
    ungroup() %>% 
    filter(avg_cty > 18) %>% 
    
    # grafiek met standaardinstellingen
    ggplot(aes(x = class, y = avg_cty, fill = class)) +
    geom_col() +
    tidyquant::scale_fill_tq()

De kleuren “verspringen”. Dit is erg verwarrend voor de lezer of in een presentatie. Je zou de kleuren graag constant houden.

Een oplossing voor dit probleem is namen aan het kleurenpalet toe te kennen die overeenkomen met de categorieën. tidyquant::scale_fill_tq() gebruikt de kleuren uit de functie tidyquant::palette_light(). Als we hier een eigen kleurenpalet van maken die bestaat uit het aantal kleuren dat overeenkomt met het aantal klassen (class) in de dataset en deze de namen van deze klassen meegeven, dan krijgen we het volgende kleurenpalet.

# kleuren in tidyquant::scale_fill_tq()
print(tidyquant::palette_light())
##         blue          red        green       yellow   steel_blue    navy_blue 
##    "#2C3E50"    "#E31A1C"    "#18BC9C"    "#CCBE93"    "#A6CEE3"    "#1F78B4" 
##  light_green         pink light_orange       orange light_purple       purple 
##    "#B2DF8A"    "#FB9A99"    "#FDBF6F"    "#FF7F00"    "#CAB2D6"    "#6A3D9A"
# maak er een eigen palette van (we hebben maar 7 kleuren nodig en daar moeten we namen aan geven)
custom_pal <- 
    tidyquant::palette_light()[1:length(unique(mpg$class))] %>% 
    setNames(sort(unique(mpg$class)))
print(custom_pal)
##    2seater    compact    midsize    minivan     pickup subcompact        suv 
##  "#2C3E50"  "#E31A1C"  "#18BC9C"  "#CCBE93"  "#A6CEE3"  "#1F78B4"  "#B2DF8A"

Daarmee kun je de kleuren constant houden in verschillende grafieken met en zonder selecties / filter op de data. Het kan zijn dat je een kleine aanpassing in de legenda nodig hebt, omdat je niet alle mogelijke kleuren wil tonen, maar alleen de kleuren die in de grafiek te zien zijn.
Dit doe je door het breaks argument te gebruiken in de scale_*_manual functie te gebruiken.

# oorspronkelijke grafiek
mpg %>% 
    group_by(class) %>% 
    summarise(avg_cty = mean(cty)) %>% 
    ungroup() %>% 
    
    # grafiek met standaardinstellingen
    ggplot(aes(x = class, y = avg_cty, fill = class)) +
    geom_col()  +
    scale_fill_manual(values = custom_pal)

# grafiek voor gefilterde data
# data
mpg %>% 
    group_by(class) %>% 
    summarise(avg_cty = mean(cty)) %>% 
    ungroup() %>% 
    filter(avg_cty > 18) %>% 
    
    # grafiek 
    # (gebruik het data-argument om in het breaks-argument aan te kunnen roepen)
    ggplot(aes(x = class, y = avg_cty, fill = class), data = .) +
    geom_col() +
    scale_fill_manual(values = custom_pal, breaks = data$class)

9.2 Nadruk leggen met kleur

In een analyse kan het heel goed zijn om de aandacht naar een bepaalde categorie te trekken. Dit kan heel goed met kleurgebruik binnen een grafiek. Aanvullend op het voorgaande, kun je dan bijvoorbeeld een kleurenpalet maken dat een grijze kleur bevat voor alle waarnemingen die niet van belang zijn en wel de eigen kleur te gebruiken voor het onderdeel waar je de nadruk op wil leggen.
Dat kan door eerst een kleurenpalet te maken met namen, zoals we hierboven hebben gedaan. Daarin ken je aan elke naam dezelfde kleur toe, die de aandacht NIET trekt, zoals lichtgrijs. Daarna kies je de variabele waar je nadruk op wil leggen en wijs je die toe aan een r-object. Dit object kun je vervolgens gebruiken om de kleur uit het kleurenpalet met namen uit het vorige voorbeeld toe te wijzen aan dezelfde waarde in het kleurenpalet dat je in de grafiek gebruikt. Dit levert het gewenste resultaat op.
In de code zie je het stap voor stap gebeuren.

# begin met een basis met alleen grijs als kleur
nadruk_pal_basis <- 
    rep("light grey", length(unique(mpg$class)))%>% 
    setNames(sort(unique(mpg$class)))
print(nadruk_pal_basis)
##      2seater      compact      midsize      minivan       pickup   subcompact 
## "light grey" "light grey" "light grey" "light grey" "light grey" "light grey" 
##          suv 
## "light grey"
# wijs de variabele toe waar de nadruk op moet komen te liggen
nadruk_var <- "subcompact"
print(nadruk_var)
## [1] "subcompact"
# maak een kopie van het palet, zodat je dit telkens opnieuw als basis kunt gebruiken
nadruk_pal <- nadruk_pal_basis

# wijs de kleur voor de nadruk variabele uit het custom_pal toe
nadruk_pal[nadruk_var] <- custom_pal[nadruk_var]
print(nadruk_pal)
##      2seater      compact      midsize      minivan       pickup   subcompact 
## "light grey" "light grey" "light grey" "light grey" "light grey"    "#1F78B4" 
##          suv 
## "light grey"
# maak de grafiek
mpg %>% 
    group_by(class) %>% 
    summarise(avg_cty = mean(cty)) %>% 
    ungroup() %>% 
    
    # grafiek 
    ggplot(aes(x = class, y = avg_cty, fill = class)) +
    geom_col() +
    scale_fill_manual(values = nadruk_pal) + 
    theme_minimal()

Je kunt er uiteraard ook voor kiezen een andere kleur te gebruiken om de aandacht te trekken. De werkwijze is dan vergelijkbaar.

10 Thema’s

Thema’s zijn een set aan instellingen die je visual heel snel een ander uiterlijk kunnen geven. Meestal kun je goed uit de voeten met 1 van de beschikbare thema’s in ggplot2 of een ander package, zoals ggthemes. Hierbij gaat het vooral om zaken als het uiterlijk van het canvas, de assen, legenda en dergelijke.

Er zijn verschillende standaard thema’s in ggplot2. Het ggthemes package biedt een uitgebreider aanbod.

Hieronder kun je de voorbeelden van het spiek- briefje zien.

10.1 Voorbeelden

10.1.1 Standaard thema

# maak de basis grafiek
t <- mpg %>% 
    ggplot(aes(x = fl, fill = fl)) +
    geom_bar() +
    labs(
        y = "aantal",
        x = "brandstof type",
        fill = "brandstof type"
    )

# pas thema toe en toon dit in de titel en subtitel
t + 
    theme_grey() +
    labs(
        title = "Standaard thema",
        subtitle = "theme_grey()"
    )

10.1.2 theme_bw()

# pas thema toe en toon dit in de titel en subtitel
t + 
    theme_bw() +
    labs(
        title = "Zwart-wit thema",
        subtitle = "theme_bw()"
    )

10.1.3 theme_classic()

# pas thema toe en toon dit in de titel en subtitel
t + 
    theme_classic() +
    labs(
        title = "Klassiek thema",
        subtitle = "theme_classic()"
    )

10.1.4 theme_minimal()

# pas thema toe en toon dit in de titel en subtitel
t + 
    theme_minimal() +
    labs(
        title = "Minimal thema",
        subtitle = "theme_minimal()"
    )

11 Voorbeelden van uitgebreide visualisaties

De visuals die hier te zien zijn, moeten duidelijk maken dat je heel professionele visuals kunt maken met ggplot2. Daar waar de voorgaande hoofdstukken wellicht wat saaie grafieken lieten zien, is dat hier anders.

De visuals in dit hoofdstuk kunnen er nog mooier uitzien. Omdat rmarkdown zaken schaalt en omzet naar html, worden er (kleine) aanpassingen gedaan. Normaal gesproken zou je de visual naar bestand opslaan en het dan delen. Dat heeft als belangrijk nadeel dat je de code dan niet kunt zien. Deze schoonheidsfoutjes neem ik dus voor lief. Waar nodig heb ik de opgeslagen versie van de visual opgenomen, zodat je de kwaliteit kunt vergelijken.

11.1 Samenvatting projecten

De eerste visual is een combinatie van 3 grafieken. De inspiratie heb ik gevonden op: https://www.storytellingwithdata.com/blog/2016/1/26/connecting-the-dots. Het is tevens een mooi voorbeeld van hoe gegevens anders getoond kunnen worden dan in de oorspronkelijke grafieken die ook op deze webpagina getoond worden.
Hieronder zowel de opgeslagen versie (waarvan je de code niet kunt bekijken) én de gegenereerde versie van deze visual.

11.1.1 opgeslagen versie

11.1.2 gegenereerd

# 1.1 projecten overzicht ----
# * data ----
project_per_status_tbl <- 
    tibble(
        status = c("Totaal Projecten", "Relevant", "Voorstellen", 
                   "Binnengehaald") %>% as_factor() %>% fct_rev(),
        aantal = c(340, 264, 92, 37),
        totaal = c(340, 264, 264, 92),
        kleur_totaal = c("grey", "grey", "orange", "darkred"),
        verschil = c(0, 0, 172, 55),
        label_verschil = c(NA_character_, NA_character_, "Niet ingediend", 
                           "Verloren")
    )

# * visual ----
p1 <- project_per_status_tbl %>% 
    
    # basis grafiek
    ggplot(aes(x = status, y = aantal)) +
    # totaal als kolom met kleur van kleur_totaal en geen opvulling
    geom_col(aes(y = totaal, color = kleur_totaal), 
             fill = NA, 
             width = 0.7, 
             linetype = "dashed", 
             size = 1) +
    geom_col(fill = "grey", color = "grey", width = 0.7, size = 1) +
    # waardes toevoegen op de kolommen
    geom_text(aes(label = aantal), color = "white", hjust = 1.2, size = 5) +
    # tekst voor de verschillen
    geom_text(aes(y = totaal, label = label_verschil, color = kleur_totaal), 
              hjust = 1.1, size = 4) +
    
    # opmaak
    labs(
        title = "Project samenvatting",
        subtitle = "Januari - December",
        x = "",
        y = ""
    ) +
    annotate("text", x = 4.525, y = 150, 
             label = "# projecten per stap in het proces", 
             color = "grey", size = 5) +
    # grafiek draaien
    coord_flip() +
    # thema wijzigen
    theme_minimal() + 
    # opmaak van titel, subtitel en grid
    theme(
        plot.title          = element_text(size=20, face = "bold"),
        plot.title.position = "plot",
        plot.subtitle       = element_text(size = 15),
        axis.text.x         = element_blank(), 
        panel.grid.major    = element_blank(),
        panel.grid.minor    = element_blank()
        ) +
    # randkleur op basis van status-variabele (voor totaal)
    scale_color_identity()

# 1.2 waarom niet ingediend ----
# * data ----
reden_niet_ingediend_tbl <- 
    tibble(
    reden = c("Konden deadline niet halen", "Onvoldoende capaciteit", 
              "Onvoldoende mogelijkheden", "Te klein", 
              "Onmogelijk binnen te halen", "Buiten de doelmarkt",  
              "Slechte relatie") %>% as_factor() %>% fct_rev(),
    aantal = c(56, 42, 24, 22, 12, 8, 8)
    ) %>% 
    mutate(
        pct = (aantal / sum(aantal)) %>% round(digits = 2) %>% scales::percent()
    )

# * visual ----
p2 <- reden_niet_ingediend_tbl %>% 
    
    # basis grafiek
    ggplot(aes(x = reden, y = aantal)) +
    geom_col(fill = "orange", width = 0.7, size = 1) +
    # waardes toevoegen op de kolommen
    geom_text(aes(label = aantal), color = "white", hjust = 1.5, size = 3) +
    # percentages toevoegen boven de kolommen
    geom_text(aes(label = pct), color = "grey", hjust = -0.25, size = 3) +
    
    # opmaak
    labs(
        title = bquote("Waarom dienen we voorstellen"~bold("niet in")),
        x = "",
        y = ""
    ) +
    # ruimte maken voor de annotatie 'aantal | % tot'
    scale_x_discrete(
        limits = c(reden_niet_ingediend_tbl %>% 
                       arrange(aantal) %>% 
                       pull(reden) %>% 
                       as.character(), "")
        ) +
    # tekst boven de grafiek
    annotate("text", x = 7.75, y = 50, label = "aantal", 
             color = "orange", size = 3) +
    annotate("text", x = 7.75, y = 61, label = "| % tot", 
             color = "grey", size = 3) +
    # grafiek draaien
    coord_flip() +
    # thema wijzigen
    theme_minimal() + 
    # opmaak van titel, subtitel en grid
    theme(
        plot.title          = element_text(size=15),
        plot.title.position = "plot",
        plot.subtitle       = element_text(size = 15),
        axis.text.x         = element_blank(), 
        panel.grid.major    = element_blank(),
        panel.grid.minor    = element_blank()
    )

# 1.3 waarom niet gewonnen ----
# * data ----
verloren_biedingen_tbl <- 
    tibble(
        reden = c("Levertijd", "Reikwijdte werk", "Prijs", "Slechte relatie") %>% 
            as_factor() %>% fct_rev(),
        aantal = c(19, 18, 11, 7)
    ) %>% 
    mutate(
        pct = (aantal / sum(aantal)) %>% round(digits = 2) %>% scales::percent()
    )

# - visual ----
p3 <- verloren_biedingen_tbl %>% 
    
    # basis grafiek
    ggplot(aes(x = reden, y = aantal)) +
    geom_col(fill = "darkred", width = 0.7, size = 1) +
    # waardes toevoegen op de kolommen
    geom_text(aes(label = aantal), color = "white", hjust = 1.5, size = 3) +
    # percentages toevoegen boven de kolommen
    geom_text(aes(label = pct), color = "grey", hjust = -0.25, size = 3) +
    
    # opmaak
    labs(
        title = bquote("Waarom we biedingen"~bold("verliezen")),
        x = "",
        y = ""
    ) +
    # ruimte maken voor de annotatie 'aantal | % tot'
    scale_x_discrete(
        limits = c(verloren_biedingen_tbl %>% 
                       arrange(aantal) %>% 
                       pull(reden) %>% 
                       as.character(), "")
    ) +
    # tekst boven de grafiek
    annotate("text", x = 5, y = 16.5, label = "aantal", 
             color = "darkred", size = 3) +
    annotate("text", x = 5, y = 21, label = "| % tot", 
             color = "grey", size = 3) +
    # grafiek draaien
    coord_flip() +
    # thema wijzigen
    theme_minimal() + 
    # opmaak van titel, subtitel en grid
    theme(
        plot.title          = element_text(size=15),
        plot.title.position = "plot",
        plot.subtitle       = element_text(size = 15),
        axis.text.x         = element_blank(), 
        panel.grid.major    = element_blank(),
        panel.grid.minor    = element_blank()
    )

p1 / (p2 + p3) + 
    plot_layout(heights = unit(c(6, 3), c('cm', 'cm')))

11.2 Lollipop-diagram

De volgende visual is gebaseerd op fictieve data.
Bijzonderheden:

  • Gesorteerd op omzet, maar ‘Overige’ als laatste;
  • Logo’s in plaats van een waarde op de as;
  • Kleur van de lijn en de tekst zijn overgenomen uit de logo’s;
  • De dikte van de lijn van de lollipop is afhankelijk van de omzet (dit zou je ook op de tekstwaarde aan het eind van de lollipop toe kunnen passen).
# data
# Lijst met Nederlandse fietsmerken maken
fietsmerken <- c("Batavus", "Gazelle", "Giant", "Koga", "Sparta", "Overige")
# gegevens over kleuren en logo's
merkgegevens <- 
    tibble(
        fietsmerk = fietsmerken,
        kleur     = c("#c41d35", "#2a328f", "#006daf", "#0e7dc7", "#45ac36", 
                        "#8e24bb") %>% setNames(fietsmerken),
        logo      = c("logo_batavus.jpg", "logo_gazelle.jpg", "logo_giant.png", 
                        "logo_koga.jpg", "logo_sparta.png", "logo_overige.jpg")
    )

# Dataset maken met fictieve omzetcijfers
set.seed(123)
verkoopdata <- 
    tibble(fietsmerk = fietsmerken, 
           omzet     = round(runif(length(fietsmerken), min = 500, max = 5000))) %>% 
    arrange(desc(omzet))

# Nabewerken data voor juiste volgorde in grafiek e.d.
data <- verkoopdata %>% 
    left_join(merkgegevens) %>% 
    mutate(
        omzet = ifelse(fietsmerk == "Overige", omzet * 4, omzet),
        fietsmerk = fietsmerk %>% as_factor() %>% fct_rev() %>% relevel("Overige")
        ) %>% 
    mutate(level = as.numeric(fietsmerk)) %>% 
    mutate(logo = str_glue("www/{logo}"),
           omzet_label = scales::number(omzet))

# Maak lollipop grafiek
g <-
    # toewijzing van data
    ggplot(data, aes(x = fietsmerk, y = omzet)) +
    # lijnen voor de omzet van 0 tot omzet hoogte
    geom_segment(aes(x = fietsmerk, xend = fietsmerk, 
                     y = 0, yend = omzet, color = kleur), size = data$omzet / 2500) +
    # omzet cijfer aan het eind van de lijn toevoegen
    geom_label(aes(label = omzet_label, color = kleur)) +
    
    # opmaak
    # titels en as-labels
    labs(
        title   = "Fietsmerken en omzet",
        x       = "",
        y       = "Omzet (x1000 €)",
        caption = "fictieve data"
    ) +
    # thema toepassn
    theme_minimal() +
    # kleur toewijzen op basis van fietsmerk
    scale_color_identity() +
    # x-as volledig blanco maken
    scale_x_discrete(labels = NULL, breaks = NULL) +
    # y-as volledig blanco maken
    scale_y_continuous(labels = NULL, breaks = NULL) +
    # grafiek draaien, zodat de merken op de 'y-as' komen
    coord_flip()  +
    # opmaak van titel, subtitel en grid
    theme(
        plot.title          = element_text(size=15),
        plot.title.position = "plot",
        panel.grid.major    = element_blank(),
        panel.grid.minor    = element_blank()
    )

# logo als x-as 'tick'
library(ggimage)
g +
    geom_image(aes(image = logo, y = -300), size = 0.095, by = "width") 

LS0tDQp0aXRsZTogImdncGxvdDIgaW4gaGV0IE5lZGVybGFuZHMiDQphdXRob3I6ICJMZW8gVGltbWVybWFucyINCmRhdGU6ICJsYWF0c3RlIHVwZGF0ZTogYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiAlWScpYCINCm91dHB1dDogDQogICAgaHRtbF9kb2N1bWVudDoNCiAgICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAgICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgICAgICB0aGVtZTogZmxhdGx5DQogICAgICAgIHRvYzogdHJ1ZQ0KICAgICAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICAgICAgdG9jX2RlcHRoOiAzDQogICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICAgIGVjaG8gPSBUUlVFLA0KICAgIGV2YWwgPSBUUlVFLA0KICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICB3YXJuaW5nID0gRkFMU0UsDQogICAgZHBpID0gMzAwLA0KICAgIG91dC53aWR0aCA9ICIxMDAlIiwNCiAgICBmaWcuYWxpZ24gPSAiY2VudGVyIg0KICAgICkNCmBgYA0KDQoNCiMgSW5sZWlkaW5nDQoNClZlcndhY2h0IGhpZXIgbmlldCBlZW4gdWl0cHV0dGVuZGUgYmVzY2hyaWp2aW5nIGVuIHVpdGxlZyB0ZSB2aW5kZW4uIEluIDgwJSB2YW4gZGUgZ2V2YWxsZW4genVsIGplIGVjaHRlciBoZWVsIGdvZWQgdWl0IGRlIHZvZXRlbiBrdW5uZW4gbWV0IHdhdCBqZSBvcCBkZXplIHdlYnNpdGUga3VudCB2aW5kZW4uDQoNCkRhdGEgd29yZHQgbWFra2VsaWprZXIgZ2Vjb25zdW1lZXJkIGFscyB2aXN1YWxpc2F0aWVzLiBIZXQgaXMgdmVlbGFsIGRlIGJlc3RlIG1ldGhvZGUgb20gZGUgaW5mb3JtYXRpZSB1aXQgZGUgZGF0YSB0ZSBjb21tdW5pY2VyZW4uIEFsbGUgaW5zcGFubmluZ2VuIGt1bm5lbiB2b29yIG5pa3MgemlqbiBnZXdlZXN0IGFscyBqZSBkZSBpbnppY2h0ZW4gdWl0IGRhdGEgZW4gYmlqYmVob3JlbmRlIGFuYWx5c2VzIG5pZXQgZ29lZCBvdmVyIGt1bnQgb3ZlciBrdW50IGJyZW5nZW4uXA0KT25kYW5rcyBoZXQgZmVpdCBkYXQgaGV0IG1ha2VuIHZhbiB2aXN1YWxpc2F0aWVzIG9uZ2Vsb2ZlbGlqayBlZW52b3VkaWcga2FuIHppam4gaW4gUiwgc3BlZWx0IGdncGxvdDIgZWVuIHplZXIgYmVsYW5ncmlqa2Ugcm9sIGFscyBkw6kgc3RhbmRhYXJkIGluIHZpc3VhbGlzYXRpZXMgdm9vciBSLg0KDQpIZXQgaXMgbWlqIG9wZ2V2YWxsZW4gZGF0IGVyIG1hYXIgd2VpbmlnIGRvY3VtZW50YXRpZSBvdmVyIGhldCBnZ3Bsb3QyIGlzIGluIGhldCBOZWRlcmxhbmRzIGlzLiBab2RvZW5kZSBiZW4gaWsgYmVnb25uZW4gZWVuIG5hc2xhZ3dlcmsgdm9vciBtZXplbGYgdGUgbWFrZW4uIEluIGRlIG9wZW4gc291cmNlIGdlZXN0IHZhbiBSIGVuIGdncGxvdDIsIGxpamt0IGhldCBtZSBuaWV0IG1lZXIgZGFuIGxvZ2lzY2ggb20gZGl0IG9wZW5iYWFyIHRlIGRlbGVuIG1ldCBpZWRlcmVlbiBkaWUgaGV0IHdpbCBsZXplbi5cDQpXYXQgZXIgd2VsIGlzLCBpcyBlZW4gTmVkZXJsYW5kc3RhbGlnZSB2ZXJzaWUgdmFuIGhldCBnZ3Bsb3QyIGNoZWF0c2hlZXQuIElrIGdlYnJ1aWsgZGl0IFtzcGlla2JyaWVmamVdKHd3dy9nZ3Bsb3QyLWR1dGNoLnBkZiAic3BpZWticmllZmplIikgKG1ldCBkYW5rIGFhbiByc3R1ZGlvIFtyc3R1ZGlvIHNwaWVrYnJpZWZqZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3RyZWUvbWFpbi90cmFuc2xhdGlvbnMvZHV0Y2gpKSBiaWogZGUgdWl0bGVnIGVuIGhvdWQgZGUgdm9sZ29yZGUgdmFuIGhldCBzcGlla2JyaWVmamUgYWFuLg0KDQojIyBXYXQgaXMgZ2dwbG90Mj8NCg0KZ2dwbG90MiBpcyBlZW4gcGFja2FnZSB2b29yIFIgPGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvYWJvdXQuaHRtbD4uIE1ldCBnZ3Bsb3QyIGlzIGVyIGdlcG9vZ2QgZWVuIHN0cnVjdHV1ciB0ZSBvbnR3aWtrZWxlbiB2b29yIGhldCBtYWtlbiB2YW4gdmlzdWFsaXNhdGllcy4gRGFhcm9tIHdvcmR0IGVyIG9vayB3ZWwgZ2VzcHJva2VuIG92ZXIgZGUgZ3JhbWF0aWNhIHZhbiB2aXN1YWxpc2F0aWVzLlwNCkVyIGlzIHVpdHN0ZWtlbmRlIChFbmdlbHN0YWxpZ2UpIGluZm9ybWF0aWUgYmVzY2hpa2JhYXIgb3ZlciBnZ3Bsb3QyLCB3YWFydWl0IGlrIG1lZGUgbWlqbiBpbnNwaXJhdGllIG9wIGhlYiBnZWRhYW4uDQoNCi0gICBnZ3Bsb3QyIHJlZmVyZW5jZSBndWlkZSA8aHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvaW5kZXguaHRtbD4NCi0gICBlZW4gd2Vic2l0ZSBtZXQgdm9vcmJlZWxkZ3JhZmlla2VuOiA8aHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8+DQotICAgZWVuIGFkZGluIHZvb3IgcnN0dWRpbyB3YWFybWVlIGplIGdyYWZpZWtlbiBrdW50IGdlbmVyZXJlbiBbZXNxdWlzc2VdKGh0dHBzOi8vZHJlYW1ycy5naXRodWIuaW8vZXNxdWlzc2UvaW5kZXguaHRtbCkNCg0KVWl0ZXJhYXJkIHppam4gZXIgbm9nIHZlZWwgYW5kZXJlIChncmF0aXMpIG9ubGluZSBicm9ubmVuLlwNClZlZWwgdmFuIGRlIHZvb3JiZWVsZGVuIGRpZSBqZSBhYW4genVsdCB0cmVmZmVuIGtvbWVuIHVpdCwgb2YgemlqbiBnZcOvbnNwaXJlZXJkIG9wIGJvdmVuc3RhYW5kZSBicm9ubmVuLiBXYWFyIG1vZ2VsaWprIHphbCBpayBuYWFyIGJyb25uZW4gdmVyd2lqemVuLg0KDQojIyBWb29yd2FhcmRlbiBvbSBkZSB2b29yYmVlbGRlbiB6ZWxmIHRlIGt1bm5lbiBtYWtlbg0KDQpKZSBrdW50IGFsbGVlbiBtZXQgZ2dwbG90MiB3ZXJrZW4gYWxzIGplIGhldCBwYWNrYWdlIGdlw69uc3RhbGxlZXJkIGVuIGdlbGFkZW4gaGVidC4gSW5zdGFsbGVyZW4gZG9lIGplIGVlbm1hbGlnIG1ldCBkZSBvcGRyYWNodCBgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpYC4gSGV0IGxhZGVuIHZhbiBoZXQgcGFja2FnZSBpbiBkZSBSLXNlc3NpZSBkb2UgamUgbWlkZGVscyBgbGlicmFyeShnZ3Bsb3QyKWAuXA0KT3ZlcmlnZSBiZW5vZGlnZGUgcGFja2FnZXMgemlqbjoNCg0KLSAgIHRpZHl2ZXJzZSAtIHZvb3IgZGF0YWJld2Vya2luZ2VuDQotICAgZ3JpZEV4dHJhIC0gb20gZ3JhZmlla2VuIG5hYXN0IGVuIG9uZGVyIGVsa2FhciB3ZWVyIHRlIGt1bm5lbiBnZXZlbg0KLSAgIG1hcHMgLSB2b29yIGthYXJ0ZWdlZ2V2ZW5zDQoNCkFscyBqZSBkZSBvbmRlcnN0YWFuZGUgY29kZSBhbHRpamQgYWFuIGplIHNjcmlwdCB0b2V2b2VndCwgZGFuIHppam4gYWxsZSB0b29scyBkaWUgamUgbm9kaWcgaGVidCB2b29yIGhldCBtYWtlbiB2YW4gdmlzdWFsaXNhdGllcyBiZXNjaGlrYmFhci4NCg0KYGBge3IgcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyBQYWNrYWdlIG5hbWVzDQpwYWNrYWdlcyA8LSBjKCJnZ3Bsb3QyIiwNCiAgICAgICAgICAgICAgIm1hcHMiLCAgICAgICMgbWFrZW4gdmFuIGthYXJ0ZW4gYWxzIGlucHV0IHZvb3IgZ2dwbG90DQogICAgICAgICAgICAgICJwYXRjaHdvcmsiLCAjIGdyYWZpZWtlbiBuYWFzdCBlbGthYXIga3VubmVuIHRvbmVuDQogICAgICAgICAgICAgICJ0aWR5dmVyc2UiKQ0KDQojIEluc3RhbGwgcGFja2FnZXMgbm90IHlldCBpbnN0YWxsZWQNCmluc3RhbGxlZF9wYWNrYWdlcyA8LSBwYWNrYWdlcyAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKQ0KaWYgKGFueShpbnN0YWxsZWRfcGFja2FnZXMgPT0gRkFMU0UpKSB7DQogIGluc3RhbGwucGFja2FnZXMocGFja2FnZXNbIWluc3RhbGxlZF9wYWNrYWdlc10pDQp9DQoNCiMgUGFja2FnZXMgbG9hZGluZw0KbGFwcGx5KHBhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KIyBEZSBiYXNpcw0KDQpJbiBkZSBiYXNpcyBoZWIgamUgdm9vciBlZW4gdmlzdWFsaXNhdGllIG1hYXIgZWVuIHBhYXIgZGluZ2VuIG5vZGlnLCBuYW1lbGlqazoNCg0KMS4gIGVlbiBkYXRhc2V0IChpbiBiYXNpcyB6YWwgaGllciB1aXRnZWdhYW4gd29yZGVuIHZhbiBkZSBbZGF0YXNldHNdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8jZGF0YSkgZGllIGJpaiBnZ3Bsb3QyIGhvcmVuKS5cDQoyLiAgZWVuIG9mIG1lZXJkZXJlIHdlZXJnYXZlbGFnZW4gdm9vciBkZSBkYXRhLiBEZSBnZW9tcy5cDQozLiAgZWVuIGNvw7ZyZGluYXRlbnN5c3RlZW0gKGhvZWZ0IHZhYWsgbmlldCBleHBsaWNpZXQgYWFuZ2VnZXZlbiB0ZSB3b3JkZW4pIHZvb3IgZGUgYXNzZW4uXA0KNC4gIGRlIGVpZ2Vuc2NoYXBwZW4gdmFuIGRlIHZpc3VhbGlzYXRpZSwgem9hbHMgd2F0IGFhbiB3ZWxrZSBhcyB0b2VnZXdlemVuIHdvcmR0IGVuIGhvZSBlciBtZXQga2xldXIgb21nZWdhYW4gbW9ldCB3b3JkZW4uIERpdCB6aWpuIGRlIGFlcyBvZiBhZXN0aGV0aWNzLlwNCiAgICBEZXplIGt1bm5lbiBnZWtvcHBlbGQgemlqbiBhYW4gZGUgZGF0YSBvZiBqdWlzdCBvbmFmaGFua2VsaWprIHZhbiBkZSBkYXRhIGdlYnJ1aWt0IHdvcmRlbi4NCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICFbXSh3d3cvMS4lMjBkZSUyMGJhc2lzJTIwLSUyMGRlZWwlMjAxLkpQRykgfCAhW10od3d3LzEuJTIwZGUlMjBiYXNpcyUyMC0lMjBkZWVsJTIwMi5KUEcpIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQpDb25mb3JtIGhldCB2b29yYmVlbGQgKGluIGhldCBibG9rIG1ldCBkZSB3aXR0ZSBhY2h0ZXJncm9uZCkgb3AgZGl0IGdlZGVlbHRlIHZhbiBoZXQgc3BpZWticmllZmplOg0KDQpgYGB7ciBnZ3Bsb3RfYmFzaXNfMSwgbWVzc2FnZT1GQUxTRX0NCiMgZGF0YQ0KbXBnICU+JSANCiAgICANCiAgICAjIGNhbnZhcyAvIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4ID0gaHd5LCB5ID0gY3R5KSkgKyAgICAjIGRhdGEgdG9ld2lqemVuIHgtIGVuIHktYXMNCiAgICAgICAgICAgICAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGN5bCkpICsgIyBzcHJlaWRpbmdzZGlhZ3JhbSwga2xldXIgcGVyIGFhbnRhbCBjeWxpbmRlcnMNCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSAgICAgIyBsaW5lYXJlIHJlZ3Jlc3NpZWxpam4gZG9vciBkZSBkYXRhIHB1bnRlbg0KYGBgDQoNCk1ldCBxcGxvdCBrdW4gamUgZXhhY3QgZGV6ZWxmZGUgZ3JhZmllayBtYWtlbi4gRGV6ZSBzbGEgaWsgZWNodGVyIG92ZXIsIG9tZGF0IGlrIHFwbG90IHZyaWp3ZWwgbm9vaXQgZ2VicnVpay4NCg0KVWl0bGVnOg0KDQotICAgYG1wZ2AgaXMgZGUgZGF0YXNldA0KLSAgIGBnZ3Bsb3QoYWVzKHg9aHd5LCB5PWN0eSkpYCB3aWpzdCBkZSB2YXJpYWJlbGUgYGh3eWAgdG9lIGFhbiBkZSB4LWFzIGVuIGBjdHlgIGFhbiBkZSB5LWFzDQotICAgYCtgIHRvZXZvZWdpbmcgdmFuIGVlbiBsYWFnIG9mIGVlbiBlbGVtZW50DQotICAgYGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY3lsKSlgIHZvZWd0IGVlbiB3ZWVyZ2F2ZWxhYWcgdG9lIHZhbiBoZXQgdHlwZSBzcHJlaWRpbmdzZGlhZ3JhbSAobGFhdCBkZSBkYXRhIGluIHB1dW50ZW4gemllbikgZW4gbWFha3QgZGF0IGRlIGtsZXVyIGFmaGFua2VsaWprIGlzIHZhbiBkZSB3YWFyZGUgdmFuIGRlIHZhcmlhYmVsZSBgY3lsYC4NCi0gICBgK2AgdG9ldm9lZ2luZyB2YW4gZWVuIGxhYWcgb2YgZWVuIGVsZW1lbnQNCi0gICBgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIilgIHZvZWd0IGVlbiBsaW5lYWlyZSByZWdyZXNzaWVsaWpuIHRvZSBpbmNsdXNpZWYgaGV0IGJldHJvdXdiYWFyaGVpZHNpbnRlcnZhbC4NCg0KRGl0IGlzIHZyaWogZWVudm91ZGlnIHVpdCB0ZSBicmVpZGVuIG1ldCBiaWp2b29yYmVlbGQga2xldXIsIGRvb3J6aWNodGlnaGVpZCBlbiB0aXRlbHM6DQoNCmBgYHtyIGdncGxvdF9iYXNpc18yLCBtZXNzYWdlPUZBTFNFfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIG11dGF0ZShjeWwgPSBhcy5mYWN0b3IoY3lsKSkgJT4lICMgY2F0ZWdvcmlzY2hlIHZhcmlhYmVsZSB2YW4gY3lsIG1ha2VuDQogICAgDQogICAgIyBjYW52YXMgLyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgIGdncGxvdChhZXMoeCA9IGh3eSwgeSA9IGN0eSkpICsgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fcG9pbnQoDQogICAgICAgIGFlcyhjb2xvciA9IGN5bCksICMgc3ByZWlkaW5nc2RpYWdyYW0sIGtsZXVyIHBlciBhYW50YWwgY3lsaW5kZXJzDQogICAgICAgIGFscGhhID0gMC4yNSAgICAgICMgbWF0ZSB2YW4gb25kb29yemljaHRpZ2hlaWQNCiAgICAgICAgKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKyAjIGxpam4gdm9vciBsaW5lYXJlIHJlZ3Jlc3NpZSBkb29yIGRlIGRhdGEgcHVudGVuDQogICAgDQogICAgIyBvcG1hYWsNCiAgICBsYWJzKA0KICAgICAgICB0aXRsZSAgICA9ICJWZXJicnVpayBvcCBzbmVsd2VnIHQuby52IGluIGRlIHN0YWQiLCAgICAgIyBncmFmaWVrdGl0ZWwNCiAgICAgICAgc3VidGl0bGUgPSAiVmVyYnJ1aWtlbiBoYW5nZW4gc3RlcmsgbWV0IGVsa2FhciBzYW1lbiIsICMgc3VidGl0ZWwNCiAgICAgICAgY29sb3IgICAgPSAiYWFudGFsXG5jeWxpbmRlcnMiLCAgICAgICAgICAgICAgICAgICAgICAgICMgbGVnZW5kYSB0aXRlbCAoXG4gPSBuaWV1d2UgcmVnZWwpDQogICAgICAgIHggICAgICAgID0gIkFmc3RhbmQgcGVyIGdhbGxvbiBvcCBkZSBzbmVsd2VnIiwgICAgICAgICAjIHRpdGVsIHgtYXMNCiAgICAgICAgeSAgICAgICAgPSAiQWZzdGFuZCBwZXIgZ2FsbG9uIGluIGRlIHN0YWQiLCAgICAgICAgICAgICMgdGl0ZWwgeS1hcw0KICAgICAgICBjYXB0aW9uICA9ICJkYXRhc2V0OiBtcGciICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBiaWpzY2hyaWZ0DQogICAgICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSAjIHRoZW1hIG1ldCBtaW5pbWFsZSBvcG1hYWsNCmBgYA0KDQpNZXQgZWxrZSBgK2Agd29yZHQgZWVuIGVsZW1lbnQgb2YgbGFhZyB0b2VnZXZvZWdkLiBEYWFybmFhc3Qgd29yZHQgYWxsZXMgd2F0IGplIGluIGRlIG9wZHJhY2h0IGBnZ3Bsb3QoKWAgbWVlZ2VlZnQgZG9vciBkZSBvbmRlcmxpZ2dlbmRlIGVsZW1lbnRlbiBvdmVyZXJmZC4gWm8ga3VuIGplIGRlIHRvZXdpanppbmcgdmFuIHZhcmlhYmVsZW4gYWFuIGRlIHgtIGVuL29mIHktYXMsIGVlbiB2dWxrbGV1ciBlbnpvdm9vcnQgb29rIGFsIGluIGRlemUgb3BkcmFjaHQgbWVlZ2V2ZW4uDQoNClVpdGxlZzoNCg0KLSAgIGBtcGdgIGlzIGRlIGRhdGFzZXQNCi0gICBgbXV0YXRlKGN5bCA9IGFzLmZhY3RvcihjeWwpKWAgem9yZ3QgZXJ2b29yIGRhdCBkZSB3YWFyZGVuIGluIGRlIHZhcmlhYmVsZSBgY3lsYCBhbHMgY2F0ZWdvcmllw6tuIGdlemllbiB3b3JkZW4NCi0gICBgZ2dwbG90KGFlcyh4PWh3eSwgeT1jdHkpKWAgd2lqc3QgZGUgdmFyaWFiZWxlIGBod3lgIHRvZSBhYW4gZGUgeC1hcyBlbiBgY3R5YCBhYW4gZGUgeS1hcw0KLSAgIGBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGN5bCkpYCB2b2VndCBlZW4gd2VlcmdhdmVsYWFnIHRvZSB2YW4gaGV0IHR5cGUgc3ByZWlkaW5nc2RpYWdyYW0gKGxhYXQgZGUgZGF0YSBpbiBwdXVudGVuIHppZW4pIGVuIG1hYWt0IGRhdCBkZSBrbGV1ciBhZmhhbmtlbGlqayBpcyB2YW4gZGUgd2FhcmRlL2NhdGVnb3JpZSB2YW4gZGUgdmFyaWFiZWxlIGBjeWxgLg0KLSAgIGBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKWAgdm9lZ3QgZWVuIGxpbmVhaXJlIHJlZ3Jlc3NpZWxpam4gdG9lIGluY2x1c2llZiBoZXQgYmV0cm91d2JhYXJoZWlkc2ludGVydmFsLg0KLSAgIGxhYnMgdm9lZ3QgbGFiZWxzIHRvZSwgbmFtZWxpams6DQogICAgLSAgIERlIHRpdGVsIChgdGl0bGVgKQ0KDQogICAgLSAgIERlIHN1YnRpdGVsIChgc3VidGl0bGVgKQ0KDQogICAgLSAgIFZvb3IgZGUgbGVnZW5kYSAoYGNvbG9yYCkNCg0KICAgIC0gICBWb29yIGRlIHgtYXMgKGB4YCkNCg0KICAgIC0gICBWb29yIGRlIHktYXMgKGB5YCkNCg0KICAgIC0gICBIZXQgYmlqc2NocmlmdCAoYGNhcHRpb25gKQ0KLSAgIGVyIHdvcmR0IGVlbiB0aGVtYSB0b2VnZXBhc3QgbWV0IHZvb3JnZXByb2dyYW1tZWVyZGUgaW5zdGVsbGluZ2VuIChgdGhlbWVfbWluaW1hbCgpYCkuDQoNCkhpZXJkb29yIHppZXQgZGUgZ3JhZmllayBlciBhbCBlZW4gaGVlbCBzdHVrIGJldGVyLCBsZWVzYmFhcmRlciBlbiBpbmZvcm1hdGlldmVyIHVpdCBkYW4gZW5rZWwgbWV0IGRlIG1pbmltYWxlIGluc3Bhbm5pbmcuDQoNCiMgRGUgbWVlc3QgZ2VicnVpa3RlIHdlZXJnYXZlbGFnZW4gKGdlb21zKQ0KDQpWYW4gZGUgb25kZXJkZWxlbiBpbiBoZXQgc3BpZWticmllZmplLCB6dWxsZW4gZGUgdm9sZ2VuZGUgc29vcnRlbiB3ZWVyZ2F2ZWxhZ2VuIGFhbiBib2Qga29tZW46DQoNCjEuICBbMSBjb250aW51ZSB2YXJpYWJlbGVdDQoNCjIuICBbMSBjYXRlZ29yaXNjaGUgdmFyaWFiZWxlXQ0KDQozLiAgWzIgY29udGludWUgdmFyaWFiZWxlbl0NCg0KNC4gIFsxIGNhdGVnb3Jpc2NoZSBlbiAxIGNvbnRpbnVlIHZhcmlhYmVsZV0NCg0KNS4gIFsyIGNhdGVnb3Jpc2NoZSB2YXJpYWJlbGVuXQ0KDQo2LiAgWzMgdmFyaWFiZWxlbl0NCg0KNy4gIFtLYWFydGVuXQ0KDQo4LiAgW3RpamRyZWVrc2VuXSgjdGlqZHJlZWtzZW4tMSkNCg0KOS4gIFtWaXN1YWxpc2VyZW4gdmFuIGZvdXRlbl0NCg0KSW4gYWxsZSBnZXZhbGxlbiBsaWd0IGRlIGZvY3VzIG9wIGRlIG1lZXN0IGdlYnJ1aWt0ZSB3ZWVyZ2F2ZWxhZ2VuLiBIaWVyIGhlYiBqZSBoZXQgbWVlc3RlIGFhbiBlbiBhbHMgamUgZGV6ZSBvbmRlciBkZSBrbmllIGhlYnQsIGx1a3QgaGV0IHdhYXJzY2hpam5saWprIG9vayBnb2VkIG9tIG1ldCBkZSBhbmRlcmUgZWVuIGdvZWRlIHZpc3VhbGlzYXRpZSB0ZSBtYWtlbi5cDQpEZSBnZW9tcyB6dWxsZW4gaW4gZGUgZ2Vub2VtZGUgdm9sZ29yZGUgYmVoYW5kZWxkIHdvcmRlbiwgd2FhcmJpaiBlZXJzdCBlZW4gZWVudm91ZGlnIHZvb3JiZWVsZCBnZXRvb25kIHdvcmR0IChoZXQgdm9vcmJlZWxkIG9wIGhldCBzcGlla2JyaWVmamUpIGVuIHZlcnZvbGdlbnMgZWVuIHZlcmdlbGlqa2JhcmUgdmlzdWFsaXNhdGllIG1ldCB1aXRnZWJyZWlkZXJlIG9wbWFhayBlbiBvcHRpZXMuDQoNCiMjIDEgY29udGludWUgdmFyaWFiZWxlDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgIVtdKHd3dy8yLjElMjB2aXN1YWxpc2F0aWVsYWdlbiUyMC0lMjAxJTIwY29udGludWUlMjB2YXJpYWJlbGUuSlBHKSB8IFZhbiBkZSB3ZWVnYXZlbGFnZW4genVsbGVuIGRlIHZvbGdlbmRlIGJlaGFuZGVsZCB3b3JkZW46IHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBEZSBkaWNodGhlaWRzZ3JhZmllazogYGdlb21fZGVuc2l0eSgpYCAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBIZXQgaGlzdG9ncmFtOiBgZ2VvbV9oaXN0KClgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMjIERpY2h0aGVpZHNncmFmaWVrIC0gZ2VvbV9kZW5zaXR5KCkNCg0KR2VvbV9kZW5zaXR5IHJlc3VsdGVlcnQgaW4gZWVuIGRpY2h0aGVpZHNncmFmaWVrLiBEZSB0b3RhbGUgb3BwZXJ2bGFrdGUgb25kZXIgZGUgbGlqbiAob2YgdmFuIGhldCBpbmdla2xldXJkZSB2bGFrKSA9IDEuIERhYXJtZWUgd29yZHQgZGUgdmVyZGVsaW5nIHZhbiBlZW4gdmFyaWFiZWxlIGluemljaHRlbGlqayBnZW1hYWt0LiBWb29yZGVlbCB0ZW4gb3B6aWNodGUgdmFuIGVlbiBoaXN0b2dyYW0gaXMgZGF0IGVyIGdlZW4gcmlzaWNvIG9wIHZlcnRla2VuaW5nIGlzIGRvb3IgZGUga2V1emUgdmFuIGhldCBhYW50YWwgc3RhdmVuIGRhdCBnZXRvb25kIHdvcmR0LiBEZSB4LWFzIGhlZWZ0IGVlbiBjb250aW51ZSB2ZXJkZWxpbmcuDQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIEVlbnZvdWRpZw0KDQpgYGB7ciB2Yl9kZW5zaXR5X2VlbnZvdWRpZ30NCiMgZGF0YQ0KbXBnICU+JSANCiAgICANCiAgICAjIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4ID0gaHd5KSkgKw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fZGVuc2l0eSgpICsgDQogICAgDQogICAgIyB0aXRlbA0KICAgIGxhYnModGl0bGUgPSAiYmFzaXNncmFmaWVrIikNCmBgYA0KDQojIyMjIyBVaXRnZWJyZWlkDQoNCmBgYHtyIHZiX2RlbnNpdHlfdWl0Z2VicmVpZH0NCiMgZGF0YQ0KbXBnICU+JSANCiAgICANCiAgICAjIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4ID0gaHd5KSkgKw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fZGVuc2l0eSgNCiAgICAgICAgYWVzKA0KICAgICAgICAgICAgZmlsbCAgPSBkcnYsICMgdnVsa2xldXIsIHNvb3J0IGFhbmRyaWp2aW5nDQogICAgICAgICAgICBjb2xvciA9IGRydiAgIyBrbGV1ciB2YW4gZGUgcmFuZCwgc29vcnQgYWFuZHJpanZpbmcNCiAgICAgICAgKSwNCiAgICAgICAgYWxwaGEgPSAwLjI1DQogICAgICAgICkgKw0KICAgIA0KICAgICMgdmVyZGVyIG9wbWFhaw0KICAgIGxhYnModGl0bGUgICAgPSAiVmVyZGVsaW5nIHZhbiBWZXJicnVpayBvcCBkZSBzbmVsd2VnIiwgIyB0aXRlbA0KICAgICAgICAgeCAgICAgICAgPSAidmVyYnJ1aWsgb3AgZGUgc25lbHdlZyIsICAgICAgICAgICAgICAgIyB0aXRlbCB4LWFzDQogICAgICAgICB5ICAgICAgICA9IE5VTEwsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGdlZW4gdGl0ZWwgeS1hcw0KICAgICAgICAgY2FwdGlvbiAgPSAibXBnIGRhdGFzZXQiLA0KICAgICAgICAgZmlsbCAgICAgPSAidHlwZVxuYWFuZHJpanZpbmciICAgICAgIyBsZWdlbmRhLXRpdGVsIChvYnYgdnVsa2xldXIpDQogICAgICAgICApICsgICAgICAgICAgDQogICAgdGhlbWVfbWluaW1hbCgpICsgICAgIyB0aGVtYSB2cmlqd2VsIHpvbmRlciBvcHNtdWsNCiAgICB0aGVtZSgNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgIyBnZWVuIHByaW1haXJlIHJhc3Rlcmxpam5lbg0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpICAjIGdlZW4gc2VjdW5kYWlyZSByYXN0ZXJsaWpuZW4NCiAgICAgICAgKSArIA0KICAgIGd1aWRlcyhjb2xvcj1GQUxTRSkgICAgICAgICAgICAgICAgICAgICAjIGdlZW4gbGVnZW5kYSB2b29yIHJhbmRrbGV1cg0KYGBgDQoNClZvb3IgZWVuIHVpdGdlYnJlaWRlIGRpY2h0aGVpZHNncmFmaWVrIHZvZWcgamUgZWxlbWVudGVuIHRvZSBhYW4gZGUgZWVyZGVyIGdlbm9lbWRlIGJhc2lzLWVsZW1lbnRlbi4gQmlqdm9vcmJlZWxkOg0KDQoxLiAgVWl0c3BsaXRzaW5nIG5hYXIgc29vcnQgYWFuZHJpanZpbmcgYGZpbGwgPSBkcnZgICh2dWxrbGV1cikgZW4gYGNvbG91ciA9IGRydmAgKGxpam5rbGV1cikgZW4gaGV0IHZyaWp3ZWwgZG9vcnppY2h0aWcgbWFrZW4gdmFuIGRlIHZ1bGtsZXVyIGBhbHBoYSA9IDAuMjVgLiBEaXQgbGFhdHN0ZSBtb2V0IGJ1aXRlbiBkZSBgYWVzKClgIG9tZGF0IGRpdCBuaWV0IGVlbiB0b2V3aWp6aW5nIHZhbiBlZW4gdmFyaWFiZWxlIHVpdCBkZSBkYXRhIGlzLCBtYWFyIGVlbiBnbG9iYWxlIGFhbmR1aWRpbmcgZGF0IGRlIHZ1bGtsZXVyZW4gMjUlIG9uZG9vcnppY2h0aWcgbW9ldGVuIHppam47DQoyLiAgKEFhbmdlcGFzdGUpIHRpdGVsLCBhcy10aXRlbHMsIGxlZ2VuZGEtdGl0ZWwgZW4gYmlqc2NocmlmdCBkb29yIG1pZGRlbCB2YW4gYGxhYnMoKWA7DQozLiAgRWVuIGhlbGUgYmFzaWMgLyBzY2hvbmUgd2VlcmdhdmUgZG9vciBnZWJydWlrIHZhbiBgdGhlbWVfbWluaW1hbCgpYDsNCjQuICBIZXQgbmlldCB3ZWVyZ2V2ZW4gdmFuIHJhc3Rlcmxpam5lbiBlbiBsZWdlbmRhIG1ldCBkZSBvcGRyYWNodGVuIGJpbm5lbiBgdGhlbWUoKWA7DQo1LiAgSGV0IG5pZXQgd2VlcmdldmVuIHZhbiBlZW4gbGVnZW5kYSB2b29yIGBjb2xvcmAuIEFscyBkZXplIG5pZXQgd2VnZ2VsYXRlbiB3b3JkdCwga3JpamcgamUgem93ZWwgZWVuIGxlZ2VuZGEgdm9vciBgZmlsbGAgYWxzIHZvb3IgYGNvbG9yYCwgb21kYXQgZGV6ZSBiZWlkZSBnZXNwZWNpZmljZWVyZCB6aWpuIGluIGRlIGBhZXMoKWAgdmFuIGBnZW9tX2RlbnNpdHkoKWAuDQoNCiMjIyBIaXN0b2dyYW0gLSBnZW9tX2hpc3QoKQ0KDQpIZXQgaGlzdG9ncmFtIGlzIGVlbiBiZWtlbmRlIGdyYWZpZWsgZW4gdG9vbnQgZGUgdmVyZGVsaW5nIHZhbiBlZW4gdmFyaWFiZWxlIGRvb3IgZGUgZGUgd2FhcmRlbiB0ZSBncm9lcGVyZW4gZW4gZGUgdGVsbGluZyB2YW4gaGV0IGFhbnRhbCB3YWFybmVtaW5nZW4gdGUgdG9uZW4gbWlkZGVscyBiYWxrZW4uDQoNCkRlIGJlbGFuZ3JpamtzdGUgZWlnZW5zY2hhcCB2YW4gaGV0IGhpc3RvZ3JhbSBpcyBoZXQgYWFudGFsIGdyb2VwZW4gb2YgZGUgZ3JvZXBzYnJlZWR0ZS4gRWVuIGZvdXRlIGtldXplIGhpZXJpbiBrYW4gbGVpZGVuIHRvdCBlZW4gdmVydGVrZW5kIGJlZWxkIHZhbiBkZSB2ZXJkZWxpbmcuIEdhIGhpZXIgZHVzIHZvb3J6aWNodGlnIG1lZSBvbS4NCg0KRGl0IHR5cGUgZ3JhZmllayBpcyBuaWV0IGVyZyBnZXNjaGlrdCBvbSBtZWVyZGVyZSB2ZXJkZWxpbmdlbiBpbiAxIGdyYWZpZWsgdGUgdG9uZW4uIERvb3IgZGUgZ3JhZmlla2VuIG5hYXN0IGVsa2FhciB0ZSB0b25lbiwga2FuIGRpdCB3ZWwuIFZlcmRlcm9wIHphbCB1aXRnZWxlZ2Qgd29yZGVuIGhvZSBqZSBkaXQgZG9ldC4NCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgRWVudm91ZGlnDQoNCmBgYHtyIHZiX2hpc3RfZWVudm91ZGlnLCBtZXNzYWdlPUZBTFNFfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIA0KICAgICMgZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHggPSBod3kpKSArICAgIyBod3kgdG9ld2lqemVuIGFhbiBkZSB4LWFzIA0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21faGlzdG9ncmFtKCkgICAgICAgICAjIGhpc3RvZ3JhbSBtZXQgc3RhbmRhYXJkaW5zdGVsbGluZw0KYGBgDQoNCiMjIyMjIFVpdGdlYnJlaWQNCg0KYGBge3IgdmJfaGlzdF91aXRnZWJyZWlkfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIA0KICAgICMgZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHggPSBod3kpKSArICAgIyBod3kgdG9ld2lqemVuIGFhbiBkZSB4LWFzIA0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21faGlzdG9ncmFtKA0KICAgICAgICBhZXMoZmlsbCA9IGRydiksDQogICAgICAgIGJpbnMgICAgID0gMjUsICAgICAgICAgICAjIGFhbnRhbCBiYWxrZW4gaW4gaGV0IGhpc3RvZ3JhbQ0KICAgICAgICBmaWxsICAgICA9ICJEYXJrYmx1ZSIsICAgIyBvcHZ1bGtsZXVyDQogICAgICAgIGNvbG9yICAgID0gIndoaXRlIiAgICAgICAjIGtsZXVyIHZhbiBkZSByYW5kDQogICAgICAgICkgKw0KICAgIHN0YXRfYmluKA0KICAgICAgICBhZXMoeSA9IC4uY291bnQuLiwgbGFiZWwgPSAuLmNvdW50Li4pLCAjIHdhYXJkZW4gdm9vciB5IGVuIGRlIGxhYmVscw0KICAgICAgICBiaW5zICA9IDI1LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFhbnRhbCBiYWxrZW4NCiAgICAgICAgZ2VvbSAgPSAidGV4dCIsICAgICAgICAgICAgICAgICAgICAgICAgIyBpcyBhbHMgZ2VvbV90ZXh0DQogICAgICAgIHZqdXN0ID0gMSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWFucGFzc2luZyBwb3NpdGllIHZhbiBkZSB0ZWtzdA0KICAgICAgICBjb2xvciA9ICJ3aGl0ZSIgICAgICAgICAgICAgICAgICAgICAgICAjIGtsZXVyIHZhbiBkZSB0ZWtzdA0KICAgICAgICApICsNCiAgICANCiAgICAjIG92ZXJpZ2Ugb3BtYWFrDQogICAgbGFicygNCiAgICAgICAgIyB0aXRlbHMsIGFzc2VuIGVuIGRlcmdlbGlqa2UNCiAgICAgICAgdGl0bGUgICA9ICJUZWxsaW5nIHZhbiBhdXRvIG1vZGVsbGVuIG5hYXIgdmVyYnJ1aWsgb3AgZGUgc25lbHdlZyIsDQogICAgICAgIHggICAgICAgPSAidmVyYnJ1aWsgb3AgZGUgc25lbHdlZyIsDQogICAgICAgIHkgICAgICAgPSAiYWFudGFsIG1vZGVsbGVuIiwNCiAgICAgICAgY2FwdGlvbiA9ICJkYXRhc2V0OiBtcGciDQogICAgKSArIA0KICAgIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbWluaW1hbGUgb3BtYWFrIGFscyB0aGVtYQ0KICAgIHNjYWxlX3hfY29udGludW91cygNCiAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAxMCwgdG8gPSA0NSwgYnkgPSA1KSAjIGFhbmdlcGFzdGUgeC1hcyBsYWJlbHMNCiAgICAgICAgKQ0KYGBgDQoNCkhldCBlZW52b3VkaWdlIHZvb3JiZWVsZCBpcyBlZW4gc3R1ayBhYW50cmVra2VsaWprZXIgdGUgbWFrZW4gZG9vciBkZSB2b2xnZW5kZSB3aWp6aWdpbmdlbiB0b2UgdGUgcGFzc2VuOg0KDQoxLiAgSGV0IGFhbnRhbCBiYWxrZW4gaWV0cyBsYWdlciB0ZSB6ZXR0ZW4gZGFuIGRlIHN0YW5kYWFyZCAzMCBtZXQgYGJpbnMgPSAyNWANCg0KMi4gIERlIGtsZXVyc3RlbGxpbmcgYWFuIHRlIHBhc3Nlbiwgem9kYXQgZGUgYmFsa2VuIGJsYXV3IHppam4gYGZpbGwgPSAiRGFya2JsdWUiYCBlbiBkZSB2ZXJzY2hpbGxlbmRlIGJhbGtlbiBnb2VkIHZhbiBlbGthYXIgdGUgb25kZXJzY2hlaWRlbiB6aWpuIGBjb2xvciA9ICJ3aGl0ZSJgDQoNCjMuICBEZSBhYW50YWxsZW4gaW4gd2l0IHRvZSB0ZSB2b2VnZW4gYmlubmVuaW4gZGUgYmFsa2VuIG1ldCBgc3RhdF9iaW4oKWANCg0KNC4gIFRpdGVscyBlbiBkZXJnZWxpamtlIHRvZXZvZWdlbiBpbiBoZXQgbGFicygpIGFyZ3VtZW50DQoNCjUuICBNaW5pbWFsZSBvcG1hYWsgdG9lcGFzc2VuIHRoZW1lX21pbmltYWwoKQ0KDQo2LiAgRGUgc2NoYWFsdmVyZGVsaW5nIG9wIGRlIHgtYXMgYWFuIHRlIHBhc3NlbiBtZXQgYHNjYWxlX3hfY29udGludW91cygpLmBcDQogICAgTWluaW11bTogMTAsIG1heGltdW06IDQ1LCBpbiBzdGFwcGVuIHZhbiA1Lg0KDQojIyAxIGNhdGVnb3Jpc2NoZSB2YXJpYWJlbGUNCg0KIVtdKHd3dy8yLjIlMjB2aXN1YWxpc2F0aWVsYWdlbiUyMC0lMjAxJTIwY2F0ZWdvcmlzY2hlJTIwdmFyaWFiZWxlLkpQRykNCg0KIyMjIFN0YWFmZGlhZ3JhbSAtIGdlb21fYmFyKCkNCg0KSGV0IHN0YWFmZGlhZ3JhbSBpcyB2ZXJnZWxpamtiYWFyIG1ldCBoZXQgaGlzdG9ncmFtLiBFY2h0ZXIgZGUgdmFyaWFiZWxlIG9wIGRlIHgtYXMgaG9lZnQgbmlldCBpbiBncm9lcGVuIG9mIGJhbGtlbiBpbmdlZGVlbGQgdGUgd29yZGVuIHZvb3JkYXQgZGUgaGV0IGFhbnRhbCBvYnNlcnZhdGllcyB3ZWVyZ2VnZXZlbiBrYW4gd29yZGVuLg0KDQpIZXQgc3RhYWZkaWFncmFtIGtvbXQgb29rIG5vZyB0ZXJ1ZyBiaWogZGUgY29tYmluYXRpZSB2YW4gZWVuIGNhdGVnb3Jpc2NoZSB2YXJpYWJlbGUgbWV0IGVlbiBjb250aW51ZSB2YXJpYWJlbGUuIERhbiBnZWJydWlrIGplIGBnZW9tX2NvbGAuIEJpaiBgZ2VvbV9iYXIoKWAgaXMgaGV0IHVpdGdhbmdzcHVudCBkYXQgaGV0IChnZXdvZ2VuKSBhYW50YWwgb2JzZXJ2YXRpZXMgZ2V0b29uZCB3b3JkdC4NCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgRWVudm91ZGlnDQoNCmBgYHtyIHZiX2Jhcl9lZW52b3VkaWd9DQojIGRhdGENCm1wZyAlPiUgDQogICAgDQogICAgIyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgIGdncGxvdChhZXMoeCA9IGZsKSkgKw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fYmFyKCkNCmBgYA0KDQojIyMjIyBVaXRnZWJyZWlkDQoNCmBgYHtyIHZiX2Jhcl91aXRnZWJyZWlkfQ0KbXBnICU+JSAjIGRhdGFzZXQNCg0KZ2dwbG90KGFlcyh4ID0gZmwpKSArICAgICAgICAgIyBicmFuZHN0b2Z0eXBlIG9wIGRlIHgtYXMNCiAgICBnZW9tX2JhciggICAgICAgICAgICAgICAgICMgc3RhYWZkaWFncmFtDQogICAgICAgIGFlcyhmaWxsID0gZmwpICAgICAgICAjIGtsZXVydnVsbGluZyAtLT4gYnJhbmRzdG9mdHlwZQ0KICAgICAgICAsIGNvbG91ciA9ICJibGFjayIgICAgIyBrbGV1ciB2YW4gZGUgYnVpdGVucmFuZCB2YW4gZGUgc3RhdmVuDQogICAgICAgICAgICAgKSArICAgICAgICAgICAgICAgDQogICAgZ2VvbV90ZXh0KCAgICAgICAgICAgICAgICAjIGxhYmVscyB2b29yIGRlIHN0YXZlbg0KICAgICAgICBzdGF0PSdjb3VudCcsICAgICAgICAgIyBzdGF0PSdjb3VudCcgZ2VlZnQgaGV0IGFhbnRhbCB2YW4gZGUgeC12YXJpYWJlbGUNCiAgICAgICAgYWVzKGxhYmVsPS4uY291bnQuLiksICMgYWFudGFsIGFscyBsYWJlbA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBsYWJlbCA9IHZlcndpanN0IG5hYXIgJ2NvdW50JyBpbiBzdGF0PSdjb3VudCcNCiAgICAgICAgY29sb3VyID0gIndoaXRlIiwgICAgICMgdGVrc3RrbGV1ciB2b29yIGhldCBsYWJlbA0KICAgICAgICB2anVzdD0xICAgICAgICAgICAgICAgIyBhYW5wYXNzaW5nIHZhbiBoZXQgbGFiZWwgLS0+IGluIGRlIHN0YWFmDQogICAgICAgICkgKw0KICAgIGxhYnModGl0bGUgICAgPSAiTVBHIGRhdGFzZXQiLCAgICAgICAgICAgICAgICAgICAgICAgICAjIGdyYWZpZWt0aXRlbA0KICAgICAgICAgc3VidGl0bGUgPSAiQWFudGFsIG1vZGVsbGVuIG5hYXIgYnJhbmRzdG9mdHlwZSIsICAjIHN1YnRpdGVsDQogICAgICAgICB4ICAgICAgICA9ICJicmFuZHN0b2YgdHlwZSIsICAgICAgICAgICAgICAgICAgICAgICMgdGl0ZWwgeC1hcw0KICAgICAgICAgeSAgICAgICAgPSAiYWFudGFsIikgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRpdGVsIHktYXMNCiAgICB0aGVtZV9taW5pbWFsKCkgKyAgICAjIHRoZW1hIHZyaWp3ZWwgem9uZGVyIG9wc211aw0KICAgIHRoZW1lKA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBnZWVuIHByaW1haXJlIHJhc3Rlcmxpam5lbg0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBnZWVuIHNlY3VuZGFpcmUgcmFzdGVybGlqbmVuDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScgICAgICAgICAgICAgICAjIGdlZW4gbGVnZW5kYQ0KICAgICAgICApDQpgYGANCg0KVm9vciBlZW4gdWl0Z2VicmVpZCBzdGFhZmRpYWdyYW0gdm9lZyBqZSBlbGVtZW50ZW4gdG9lIGFhbiBkZSBlZXJkZXIgZ2Vub2VtZGUgYmFzaXMtZWxlbWVudGVuLiBCaWp2b29yYmVlbGQ6DQoNCjEuICBFZW4ga2xldXIgZGllIGFhbiBkZSBzdGFhZiB0b2VnZXdlemVuIHdvcmR0IG9wIGJhc2lzIHZhbiBoZXQgYnJhbmRzdG9mdHlwZSAoYGZsYCkuIERpdCBkb29yZGF0IGBmaWxsID0gZmxgIGlzIG9wZ2Vub21lbiBpbiBkZSBgYWVzYCB2YW4gYGdlb21fYmFyYDsNCg0KMi4gIEhldCBhYW50YWwgd2Fhcm5lbWluZ2VuIGluIGhldCB3aXQgaW4gZGUgc3RhYWYgd2VlcmdldmVuLg0KDQogICAgYS4gIGBnZW9tX3RleHRgICh0ZWtzdCk7DQogICAgYi4gIGBzdGF0PSdjb3VudCdgIChnZWVmdCBoZXQgYWFudGFsIHdhYXJuZW1pbmdlbikgZW4gYGxhYmVsPS4uY291bnQuLmAgKGdlZWZ0IGhldCBhYW50YWwgd2Fhcm5lbWluZ2VuIGRvb3IgYWFuIGhldCB0ZWtzdGxhYmVsKTsNCiAgICBjLiAgYGNvbG91ciA9ICJ3aGl0ZSJgIChsYWJlbCBpbiBoZXQgd2l0KTsNCiAgICBkLiAgYHZqdXN0PTFgIChrbGVpbmUgdmVydGljYWxlIGFhbnBhc3NpbmcsIHN0YW5kYWFyZCB3b3JkdCBoZXQgbGFiZWwgYm92ZW4gZGUgc3RhYWYgZ2Vwcm9qZWN0ZWVyZCkuDQoNCjMuICAoQWFuZ2VwYXN0ZSkgdGl0ZWwsIHN1YnRpdGVsIGVuIGFzLXRpdGVscyBkb29yIG1pZGRlbCB2YW4gYGxhYnMoKWA7DQoNCjQuICBFZW4gaGVsZSBiYXNpYyAvIHNjaG9uZSB3ZWVyZ2F2ZSBkb29yIGdlYnJ1aWsgdmFuIGB0aGVtZV9taW5pbWFsKClgOw0KDQo1LiAgSGV0IG5pZXQgd2VlcmdldmVuIHZhbiByYXN0ZXJsaWpuZW4gZW4gbGVnZW5kYSBtZXQgZGUgb3BkcmFjaHRlbiBiaW5uZW4gYHRoZW1lKClgLg0KDQojIyAyIGNvbnRpbnVlIHZhcmlhYmVsZW4NCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAhW10od3d3LzIuMyUyMHZpc3VhbGlzYXRpZWxhZ2VuJTIwLSUyMDIlMjBjb250aW51ZSUyMHZhcmlhYmVsZW4uSlBHKSB8IFZhbiBkZSB3ZWVyZ2F2ZWxhZ2VuIG9wIGhldCBzcGlla2JyaWVmamUgemFsIGlrIGFhbmRhY2h0IGJlc3RlZGVuIGFhbjogfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBIZXQgc3ByZWlkaW5nc2RpYWdyYW06IGBnZW9tX2ppdHRlcigpYCAmIGBnZW9tX3BvaW50KClgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBEZSB0cmVuZGxpam46IGBnZW9tX3Ntb290aCgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBUZWtzdDogYGdlb21fdGV4dCgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQojIyMgU3ByZWlkaW5nc2dyYWZpZWsgLSBnZW9tX2ppdHRlcigpICYgZ2VvbV9wb2ludCgpDQoNCmBnZW9tX2ppdHRlcigpYCBlbiBgZ2VvbV9wb2ludCgpYCBsaWprZW4gZXJnIG9wIGVsa2Fhci4gSGV0IHppam4gYmVpZGUgc3ByZWlkaW5nc2dyYWZpZWtlbi5cDQpEYWFyIHdhYXIgYGdlb21fcG9pbnQoKWAgZGUgcHVudGVuIGluIGRlIGdyYWZpZWsgZXhhY3Qgb3AgZGUgcGxlayBwbGFhdHMgZGllIGRlIGRhdGEgYWFuZ2VlZnQsIHZvZWd0IGBnZW9tX2ppdHRlcigpYCBlbiB3YXQgcnVpcyBhYW4gdG9lLCB6b2RhdCBkZSBwdW50ZW4gbmlldC9taW5kZXIgb3ZlcmxhcHBlbi4NCg0KQWxzIGplIHZlZWwgZGF0YXB1bnRlbiBoZWJ0LCBkYW4ga2FuIGBnZW9tX2ppdHRlcigpYCBlZW4gYmV0ZXJlIGtldXplIHppam4sIG1hYXIgb29rIGhldCBnZWJydWlrIHZhbiBkZSBkb29yemljaHRpZ2hlaWQgKGBhbHBoYWApIGthbiBoaWVyIGVyZyBudXR0aWcgYmlqIHppam4uDQoNCkJla2lqayBkZSBncmFmaWVrZW4gZW4gemllIGRlIHZlcnNjaGlsbGVuIGRpZSBleGFjdCBkZXplbGZkZSBjb2RlIG1ldCBhbGxlZW4gZWVuIGFuZGVyZSBnZW9tIHZlcm9vcnphYWt0Lg0KDQojIyMjIFZvb3JiZWVsZGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMjIyBFZW52b3VkaWcgKGppdHRlcikNCg0KYGBge3IgdmJfaml0dGVyX2VlbnZvdWRpZ18xfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHg9aHd5LCB5PWN0eSkpICsgICAgIyBkYXRhIHRvZXdpanplbiB4LSBlbiB5LWFzDQogICAgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgZ2VvbV9qaXR0ZXIoKQ0KICAgICAgICAgICAgICAgDQpgYGANCg0KSmUgemlldCBkdWlkZWxpamsgZGF0IG1ldCBgZ2VvbV9qaXR0ZXIoKWAgZGUgaW5kaXZpZHVlbGUgd2Fhcm5lbWluZ2VuIGJldGVyIHRlIHppZW4gemlqbiBkYW4gZGUgZ3JhZmllayBtZXQgYGdlb21fcG9pbnQoKWAuDQoNCiMjIyMjIEVlbnZvdWRpZyAocG9pbnQpDQoNCmBgYHtyIHZiX3BvaW50X2VlbnZvdWRpZ18xfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHg9aHd5LCB5PWN0eSkpICsgICAgIyBkYXRhIHRvZXdpanplbiB4LSBlbiB5LWFzDQogICAgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgZ2VvbV9wb2ludCgpDQogICAgICAgICAgICAgICANCmBgYA0KDQojIyMjIyBVaXRnZWJyZWlkIChqaXR0ZXIpDQoNCmBgYHtyIHZiX2ppdHRlcl91aXRnZWJyZWlkXzF9DQojIGRhdGENCm1wZyAlPiUgDQogICAgbXV0YXRlKGN5bCA9IGFzLmZhY3RvcihjeWwpKSAlPiUgIyBjYXRlZ29yaXNjaGUgdmFyaWFiZWxlIHZhbiBjeWwgbWFrZW4NCiAgICANCiAgICAjIGNhbnZhcyAvIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4PWh3eSwgeT1jdHkpKSArICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21faml0dGVyKGFlcyhjb2xvciA9IGN5bCksDQogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKG1wZyRjdHkpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihtcGckaHd5KSkgKw0KICAgIA0KICAgICMgb3BtYWFrDQogICAgbGFicygNCiAgICAgICAgdGl0bGUgICAgPSAiU3RhZCB2cyBzbmVsd2VnIHZlcmJydWlrIiwNCiAgICAgICAgc3VidGl0bGUgPSAiTW9kZWxsZW4gbWV0IDQgY2lsaW5kZXJzIGRvZW4gaGV0IGJvdmVuZ2VtaWRkZWxkIGdvZWQiLA0KICAgICAgICB4ICAgICAgICA9ICJNaWpsZW4gcGVyIGdhbGxvbiBvcCBkZSBzbmVsd2VnIiwgICAgICAgICAjIHRpdGVsIHgtYXMNCiAgICAgICAgeSAgICAgICAgPSAiTWlqbGVuIHBlciBnYWxsb24gaW4gZGUgc3RhZCIsICAgICAgICAgICAgIyB0aXRlbCB5LWFzDQogICAgICAgIGNhcHRpb24gID0gImRhdGEgc2V0OiBtcGciDQogICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpEZSB2b2xnZW5kZSBhYW5wYXNzaW5nZW4gbGF0ZW4gZGUgZ3JhZmlla2VuIGVyIGFhbnRyZWtrZWxpamtlciB1aXR6aWVuOg0KDQoxLiAgS2xldXIgZW4gZG9vcnppY2h0aWdoZWlkIHZhbiBkZSBkZSBwdW50ZW4gdmlhIGBhZXMoY29sb3IgPSBjeWwpLCBhbHBoYSA9IDAuNWAuDQoNCjIuICBFZW4gaG9yem9udGFsZSAoYGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4obXBnJGN0eSkpYCkgZW4gdmVydGljYWxlIHJlZmVyZW50aWVsaWpuIChgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihtcGckY3R5KSlgKSBvbSBoZXQgZ2VtaWRkZWxkZSBkYWFuIHRlIGdldmVuDQoNCjMuICBXaWp6aWdlbiB2YW4gZGUgdGl0ZWwsIGRlIGFzLXRpdGVscyBlbiB0b2V2b2VnZW4gdmFuIGRlIGJyb24gdmFuIGRlIGRhdGEgdmlhIGBsYWJzKClgOw0KDQo0LiAgRWVuIGhlbGUgYmFzaWMgLyBzY2hvbmUgd2VlcmdhdmUgZG9vciBnZWJydWlrIHZhbiBgdGhlbWVfbWluaW1hbCgpYC4NCg0KIyMjIyMgVWl0Z2VicmVpZCAocG9pbnQpDQoNCmBgYHtyIHZiX3BvaW50X3VpdGdlYnJlaWRfMX0NCiMgZGF0YQ0KbXBnICU+JSANCiAgICBtdXRhdGUoY3lsID0gYXMuZmFjdG9yKGN5bCkpICU+JSAjIGNhdGVnb3Jpc2NoZSB2YXJpYWJlbGUgdmFuIGN5bCBtYWtlbg0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHggPSBod3ksIHkgPSBjdHkpKSArICAjIGRhdGEgdG9ld2lqemVuIHgtIGVuIHktYXMNCiAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX3BvaW50KA0KICAgICAgICBhZXMoY29sb3IgPSBjeWwpLCAjIHNwcmVpZGluZ3NkaWFncmFtLCBrbGV1ciBwZXIgYWFudGFsIGN5bGluZGVycw0KICAgICAgICBhbHBoYSA9IDAuMjUgICAgICAjIG1hdGUgdmFuIG9uZG9vcnppY2h0aWdoZWlkDQogICAgICAgICkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4obXBnJGN0eSkpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKG1wZyRod3kpKSArDQogICAgDQogICAgIyBvcG1hYWsNCiAgICBsYWJzKA0KICAgICAgICB0aXRsZSAgICA9ICJWZXJicnVpayBvcCBzbmVsd2VnIHQuby52IGluIGRlIHN0YWQiLCAgICAgIyBncmFmaWVrdGl0ZWwNCiAgICAgICAgc3VidGl0bGUgPSAiTWVlciBjaWxpbmRlcnMgYmV0ZWtlbnQga2xlaW5lcmUgYWZzdGFuZCBwZXIgZ2FsbG9uIiwNCiAgICAgICAgY29sb3IgICAgPSAiYWFudGFsXG5jeWxpbmRlcnMiLCAgICAgICAgICAgICAgICAgICAgICAgICMgbGVnZW5kYSB0aXRlbA0KICAgICAgICB4ICAgICAgICA9ICJNaWpsZW4gcGVyIGdhbGxvbiBvcCBkZSBzbmVsd2VnIiwgICAgICAgICAjIHRpdGVsIHgtYXMNCiAgICAgICAgeSAgICAgICAgPSAiTWlqbGVuIHBlciBnYWxsb24gaW4gZGUgc3RhZCIsICAgICAgICAgICAgIyB0aXRlbCB5LWFzDQogICAgICAgIGNhcHRpb24gID0gImRhdGFzZXQ6IG1wZyIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGJpanNjaHJpZnQNCiAgICAgICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpICMgdGhlbWEgbWV0IG1pbmltYWxlIG9wbWFhaw0KDQpgYGANCg0KRGUgdm9sZ2VuZGUgYWFucGFzc2luZ2VuIGxhdGVuIGRlIGdyYWZpZWtlbiBlciBhYW50cmVra2VsaWprZXIgdWl0emllbjoNCg0KMS4gIEtsZXVyIGVuIGRvb3J6aWNodGlnaGVpZCB2YW4gZGUgZGUgcHVudGVuIHZpYSBgYWVzKGNvbG9yID0gY3lsKSwgYWxwaGEgPSAwLjVgLg0KDQoyLiAgRWVuIGhvcnpvbnRhbGUgKGBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKG1wZyRjdHkpKWApIGVuIHZlcnRpY2FsZSByZWZlcmVudGllbGlqbiAoYGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4obXBnJGN0eSkpYCkgb20gaGV0IGdlbWlkZGVsZGUgZGFhbiB0ZSBnZXZlbg0KDQozLiAgV2lqemlnZW4gdmFuIGRlIHRpdGVsLCBkZSBhcy10aXRlbHMgZW4gdG9ldm9lZ2VuIHZhbiBkZSBicm9uIHZhbiBkZSBkYXRhIHZpYSBgbGFicygpYDsNCg0KNC4gIEVlbiBoZWxlIGJhc2ljIC8gc2Nob25lIHdlZXJnYXZlIGRvb3IgZ2VicnVpayB2YW4gYHRoZW1lX21pbmltYWwoKWAuDQoNCk9vayBtZXQgZGV6ZSAyIGdyYWZpZWtlbiBpcyBoZXQgdmVyc2NoaWwgdWl0ZXJtYXRlIGR1aWRlbGlqay4gT29rIGluIGRpdCBnZXZhbCBnZWVmdCBgZ2VvbV9qaXR0ZXIoKWAgbWVlciBpbmZvcm1hdGllLiBCaWogdmVlbCBkYXRhcHVudGVuIGlzIGBnZW9tX2ppdHRlcigpYCBkdXMgZGUgYmV0ZXJlIGtldXplLg0KDQojIyMgVHJlbmRsaWpuIC0gZ2VvbV9zbW9vdGgoKQ0KDQpnZW9tX3Ntb290aCBnZWVmdCBlZW4gZ2Vtb2RlbGxlZXJkZSBsaWpuIHdlZXIgZG9vciBkZSBkYXRhc2V0IG1ldCBvbnpla2VyaGVpZHNtYXJnZXMgKHN0YW5kYWFyZCBlZW4gZ2ViaWVkIG9tIGRlIGxpam4gZGF0IGluZ2VrbGV1cmQgd29yZHQpLiBBbHMgamUgZ2VlbiBhcmd1bWVudGVuIG1lZWdlZWZ0LCB3b3JkdCBlciBlZW4gc3RhbmRhYXJkIG1ldGhvZGUgZW4gZm9ybXVsZSB2b29yIGRlIHNjaGF0dGluZ3NsaWpuIGdla296ZW4gb3AgYmFzaXMgdmFuIGRlIGRhdGEuDQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIEVlbnZvdWRpZw0KDQpgYGB7ciB2Yl9zbW9vdGhfZWVudm91ZGlnLCBtZXNzYWdlPUZBTFNFfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHg9aHd5LCB5PWN0eSkpICsgICAgIyBkYXRhIHRvZXdpanplbiB4LSBlbiB5LWFzDQogICAgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgIyB0cmVuZGxpam4gbGluZWFyIG1vZGVsICg9IGxpbmVhaXJlIHJlZ3Jlc3NpZSkNCiAgICAgICAgICAgICAgIA0KYGBgDQoNCiMjIyMjIFVpdGdlYnJlaWQNCg0KYGBge3IgdmJfc21vb3RoX3VpdGdlYnJlaWQsIG1lc3NhZ2U9RkFMU0V9DQojIGRhdGENCm1wZyAlPiUgDQogICAgbXV0YXRlKGN5bCA9IGFzLmZhY3RvcihjeWwpKSAlPiUgIyBjYXRlZ29yaXNjaGUgdmFyaWFiZWxlIHZhbiBjeWwgbWFrZW4NCiAgICANCiAgICAjIGNhbnZhcyAvIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4PWh3eSwgeT1jdHkpKSArICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21faml0dGVyKGFlcyhjb2xvciA9IGN5bCksDQogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKG1wZyRjdHkpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihtcGckaHd5KSkgKw0KICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsNCiAgICANCiAgICAjIG9wbWFhaw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlICAgID0gIlN0YWQgdnMgc25lbHdlZyB2ZXJicnVpayIsDQogICAgICAgIHN1YnRpdGxlID0gIk1vZGVsbGVuIG1ldCA0IGNpbGluZGVycyBkb2VuIGhldCBib3ZlbmdlbWlkZGVsZCBnb2VkIiwNCiAgICAgICAgeCAgICAgICAgPSAiTWlqbGVuIHBlciBnYWxsb24gb3AgZGUgc25lbHdlZyIsICAgICAgICAgIyB0aXRlbCB4LWFzDQogICAgICAgIHkgICAgICAgID0gIk1pamxlbiBwZXIgZ2FsbG9uIGluIGRlIHN0YWQiLCAgICAgICAgICAgICMgdGl0ZWwgeS1hcw0KICAgICAgICBjYXB0aW9uICA9ICJkYXRhIHNldDogbXBnIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkFscyB1aXRnZWJyZWlkIHZvb3JiZWVsZCBoaWVyIGBnZW9tX3Ntb290aCgpYCB0b2VnZXZvZWdkIGFhbiBoZXQgdWl0Z2VicmVpZGUgdm9vcmJlZWxkIHZvb3IgYGdlb21faml0dGVyKClgLiBWZWVsYWwgenVsIGplIGBnZW9tX3Ntb290aCgpYCBpbiBjb21iaW5hdGllIG1ldCBlZW4gc3ByZWlkaW5nc2dyYWZpZWsgZ2VicnVpa2VuLiBIZXQgYWFucm9lcGVuIHZhbiBgZ2VvbV9zbW9vdGgoKWAgaGllcmluIG5pZXQgYW5kZXJzIGRhbiBpbiBoZXQgZWVudm91ZGlnZSB2b29yYmVlbGQuIERlIG92ZXJpZ2UgZGVsZW4ga29tZW4gdWl0IGhldCB1aXRnZWJyZWlkZSB2b29yYmVlbGQgcm9uZG9tIGBnZW9tX2ppdHRlcigpYC4NCg0KIyMjIFRla3N0IC0gZ2VvbV90ZXh0KCkNCg0KVGVrc3QgaW4gZWVuIGdyYWZpZWsga2FuIGhlZWwgdmVyaGVsZGVyZW5kIHppam4uIEJpanZvb3JiZWVsZCBkb29yIHdhYXJkZXMgb2YgbGFiZWxzIGluIGVlbiBncmFmaWVrIHRvZSB0ZSB2b2VnZW4uIE1hYXIgb29rIGRvb3Igc3BlY2lmaWVrZSB3YWFyZGVzIHVpdCB0ZSBsaWNodGVuIG9mIGVlbiB6b2dlbmFhbWRlIHdvcmRjbG91ZCAoZGllIHdvcmR0IGhpZXIgbmlldCBiZWhhbmRlbGQpLg0KDQpUZWtzdCBpbiBlZW4gZ3JhZmllayB6dWwgamUgdmFhaywgbWFhciBuaWV0IGFsdGlqZCBjb21iaW5lcmVuIG1ldCBhbmRlcmUgd2VlcmdhdmVsYWdlbi4gRGF0IGhlYmJlbiB3ZSBiaWp2b29yYmVlbGQgYWwgZ2V6aWVuIGJpaiBoZXQgdWl0Z2VicmVpZGUgc3RhYWZkaWFncmFtLiBEZSB3YWFyZGVzIGluIGRlIGJhbGtlbi9zdGF2ZW4gemlqbiB0ZWtzdHdhYXJkZXMuIEVlbiBhbmRlciB2b29yYmVlbGQgd2FhcmluIGxhYmVscyB2YWFrIGdlYnJ1aWt0IHdvcmRlbiBpcyBlZW4gbG9sbGlwb3AtZGlhZ3JhbS4gRGFhcnZhbiBpcyBlZW4gdm9vcmJlZWxkIG9wZ2Vub21lbiBpbiBbTG9sbGlwb3AtZGlhZ3JhbV0uDQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIEVlbnZvdWRpZw0KDQpgYGB7ciB2Yl90ZXh0X2VlbnZvdWRpZ30NCiMgZGF0YQ0KbXBnICU+JSANCiAgICANCiAgICAjIGNhbnZhcyAvIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4PWh3eSwgeT1jdHkpKSArICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBjdHkpKQ0KDQpgYGANCg0KIyMjIyMgVWl0Z2VicmVpZA0KDQpgYGB7ciB2Yl90ZXh0X3VpdGdlYnJlaWR9DQojIGRhdGENCm1wZyAlPiUgDQogICAgIyBzYW1lbnZhdHRlbiBwZXIgdHlwZSBhYW5kcmlqdmluZw0KICAgIGdyb3VwX2J5KGRydikgJT4lICAgICAgICAgICAgICAgIA0KICAgIHN1bW1hcmlzZShjdHkgPSBtZWFuKGN0eSksIA0KICAgICAgICAgICAgICBod3kgPSBtZWFuKGh3eSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgICMgZHJ2IGluIGxlZXNiYXJlIHRlcm1lbg0KICAgIG11dGF0ZShkcnYgPSByZWNvZGUoZHJ2LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGYgPSAidm9vcndpZWxhYW5kcmlqdmluZyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgciA9ICJhY2h0ZXJ3aWVsYWFuZHJpanZpbmciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICI0IiA9ICJ2aWVyd2llbGFhbmRyaWp2aW5nIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAuZGVmYXVsdCA9IE5BX2NoYXJhY3Rlcl8pKSAlPiUgDQogICAgDQogICAgIyBjYW52YXMgLyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgIGdncGxvdCgNCiAgICAgICAgYWVzKHggPSBjdHksIHkgPSBod3ksIGNvbG9yID0gZHJ2KSAjIGRhdGEgdG9ld2lqemVuIHgtYXMsIHktYXMgZW4ga2xldXINCiAgICAgICAgKSArIA0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgICMgbGFhZyBtZXQgcHVudGVuIHZvb3Igd2FhcmRlcw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgIyBsYWFnIG1ldCB0ZWtzdA0KICAgIGdlb21fdGV4dCgNCiAgICAgICAgYWVzKGxhYmVsID0gZHJ2KSwgIyB0ZWtzdCB0b2V3aWp6ZW4gYWFuIGRydiB2YXJpYWJlbGUNCiAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgIyBob3Jpem9udGFsZSBhYW5wYXNzaW5nIHBvc2l0aWUgdGVrc3QNCiAgICAgICAgdmp1c3QgPSAtMC41ICAgICAgIyB2ZXJ0aWNhbGUgYWFucGFzc2luZyBwb3NpdGllIHRla3N0DQogICAgICAgICkgKw0KICAgIA0KICAgICMgb3BtYWFrDQogICAgbGFicygNCiAgICAgICAgdGl0bGUgICAgPSAiU3RhZCB2cyBzbmVsd2VnIHZlcmJydWlrIiwNCiAgICAgICAgc3VidGl0bGUgPSAiTW9kZWxsZW4gbWV0IFZvb3J3aWVsYWFuZHJpanZpbmcgemlqbiBoZXQgenVpbmlnc3QiLA0KICAgICAgICB4ICAgICAgICA9ICJNaWpsZW4gcGVyIGdhbGxvbiBvcCBkZSBzbmVsd2VnIiwgICAgICAgICAjIHRpdGVsIHgtYXMNCiAgICAgICAgeSAgICAgICAgPSAiTWlqbGVuIHBlciBnYWxsb24gaW4gZGUgc3RhZCIsICAgICAgICAgICAgIyB0aXRlbCB5LWFzDQogICAgICAgIGNhcHRpb24gID0gImRhdGEgc2V0OiBtcGciDQogICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBleHBhbmRfbGltaXRzKHggPSBjKDUsIDI1KSwgICAgIyB4LWFzIHVpdGJyZWlkZW4gb20gZGUgdGVrc3Qgdm9sbGVkaWcgdGUgdG9uZW4NCiAgICAgICAgICAgICAgICAgIHkgPSBjKDE1LCAzMCkpICsgIyB5LWFzIHVpdGJyZWlkZW4gb20gZGUgdGVrc3Qgdm9sbGVkaWcgdGUgdG9uZW4NCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KVm9vciBoZXQgdWl0Z2VicmVpZGUgdm9vcmJlZWxkIHppam4gZGUgdm9sZ2VuZGUgc3RhcHBlbiBnZW5vbWVuOg0KDQoxLiAgRGUgZGF0YSB2b29yYmVyZWlkZW4gdm9vciBkZSBncmFmaWVrOw0KDQoyLiAgYGN0eWAgYWFuIGRlIHgtYXMgZW4gYGh3eWAgYWFuIGRlIHktYXMgdG9ld2lqemVuLCBkZSBrbGV1ciB3b3JkdCBiZXBhYWxkIGRvb3IgZGUgd2FhcmRlIHZhbiBgZHJ2YDsNCg0KMy4gIFRla3N0IHRvZXZvZWdlbiBgZ2VvbV90ZXh0KClgLCB3YWFyYmlqIGRlIHRla3N0IGJlcGFhbGQgd29yZHQgZG9vciBkZSB2YXJpYWJlbGUgZHJ2IGVuIGRlIHBvc2l0aWUgem93ZWwgaG9yaXpvbnRhYWwgYWxzIHZlcnRpY2FhbCBhYW5nZXBhc3Qgd29yZHQgKGRhYXJvdmVyIGxhdGVyIG1lZXI7DQoNCjQuICBUaXRlbHMgZW4gZGVyZ2VsaWprZSB0b2V2b2VnZW4gYGxhYnMoKWA7DQoNCjUuICBNaW5pbWFsZSBvcG1hYWsgdm9vciBoZXQgdGhlbWEgYHRoZW1lX21pbmltYWwoKWA7DQoNCjYuICBEZSBib3Zlbi0gZW4gb25kZXJncmVucyB2YW4gZGUgeC0gZW4geS1hcyB2YXN0bGVnZ2VuIHZpYSBgZXhwYW5kX2xpbWl0cygpYDsNCg0KNy4gIERlIGxlZ2VuZGEgdmVyd2lqZGVyZW4gdmlhIGB0aGVtZSgpYDsNCg0KIyMgMSBjYXRlZ29yaXNjaGUgZW4gMSBjb250aW51ZSB2YXJpYWJlbGUNCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgIVtdKHd3dy8yLjQlMjB2aXN1YWxpc2F0aWVsYWdlbiUyMC0lMjAxJTIwY2F0Z29yaXNjaGUlMjBlbiUyMDElMjBjb250aW51ZSUyMHZhcmlhYmVsZS5KUEcpIHwgVmFuIGRlIHdlZXJnYXZlbGFnZW4gb3AgaGV0IHNwaWVrYnJpZWZqZSB6YWwgaWsgYWFuZGFjaHQgYmVzdGVkZW4gYWFuOiB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAtICAgSGV0IHN0YWFmZGlhZ3JhbTogYGdlb21fYmFyKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBEZSBib3hwbG90OiBgZ2VvbV9ib3hwbG90KClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgLSAgIEhldCB2aW9vbGRpYWdyYW06IGBnZW9tX3Zpb2xpbigpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQojIyMgU3RhYWZpYWdyYW0gLSBnZW9tX2JhcigpIC8gZ2VvbV9jb2woKQ0KDQpXZSBoZWJiZW4gaGV0IHN0YWFmZGlhZ3JhbSBhbCBlZXJkZXIgZ2V6aWVuLCBuYW1lbGlqayBpbiBkZSBwYXJhZ3JhYWYgWzEgY2F0ZWdvcmlzY2hlIHZhcmlhYmVsZV0uXA0KRGFhciB3ZXJkIGhldCBzdGFhZmRpYWdyYW0gZ2V0b29uZCBtZXQgMSBjYXRlZ29yaXNjaGUgdmFyaWFiZWxlIGVuIGVlbiB0ZWxsaW5nIHZhbiBoZXQgYWFudGFsIHdhYXJuZW1pbmdlbi4gTmV0IGFscyBlZW4gaGlzdG9ncmFtIGR1cy4NCg0KTnUgZHVzIGVlbiBzdGFhZmRpYWdyYW0gbWV0IDEgY2F0ZWdvcmlzY2hlIHZhcmlhYmVsZSBlbiAxIGNvbnRpbnVlLiBIZXQgdm9vcmJlZWxkIHZhbiBoZXQgc3BpZWticmllZmplIGlzIGVlbiB3ZWluaWcgYmV0ZWtlbmlzdm9sIHZvb3JiZWVsZC4gSGV0IHRvb250IGRlIHNvbSB2YW4gaGV0IGFhbnRhbCBtaWpsZW4gcGVyIGdhbGxvbiBhbHMgd2FhcmRlIHZhbiBkZSBjbGFzcyAodHlwZSkgYXV0by4gRGUgY29udGludWUgdmFyaWFiZWxlIGt1biBqZSBlY2h0ZXIgbmlldCB6byBvcCBlZW4gYmV0ZWtlbmlzdm9sbGUgbWFuaWVyIG9wdGVsbGVuLiBWb29yIGhldCB2b29yYmVlbGQgaGViIGlrIGR1cyBlZXJzdCBoZXQgZ2VtaWRkZWxkZSBiZXJla2VuZCwgem9kYXQgZGUgZ2VtaWRkZWxkZSBhZnN0YW5kIHBlciBtb2RlbCBwZXIgZ2FsbG9uIGdldG9vbmQgd29yZHQuDQoNCmBnZW9tX2JhcigpYCB2ZXJ3YWNodCBlaWdlbmxpamsgZ2VlbiB5LXdhYXJkZS4gSGV0IGdlZHJhYWd0IHppY2ggc3RhbmRhYXJkIGFscyBlZW4gaGlzdG9ncmFtICh0ZWx0IGhldCBhYW50YWwgd2Fhcm5lbWluZ2VuIGVuIHRvb250IGRpZSB3YWFyZGVzKS4gQWxzIGplIGRlIGdlZ2V2ZW5zIHdpbCB0b25lbiB6b2FscyB6ZSBpbiBkZSBkYXRhc2V0IHZvb3Jrb21lbiAob2YgaG9lIGppaiB6ZSB6ZWxmIHNhbWVudmF0KSwgZGFuIG1vZXQgamUgYHN0YXQgPSAiaWRlbnRpdHkiYCBnZWJydWlrZW4uXA0KSmUgem91IG9vayBgZ2VvbV9jb2woKWAga3VubmVuIGdlYnJ1aWtlbi4gU3RhbmRhYXJkIHRvb250IGRlemUgZGUgd2FhcmRlcyBkaWUgamUgYWFubGV2ZXJ0LiBIaWVyb25kZXIgemllIGplIGRhdCBiZWlkZSB2YXJpYW50ZW4gZXhhY3QgZGV6ZWxmZGUgZ3JhZmllayBvcGxldmVyZW4uXA0KSW4gZGV6ZSBjYXRlZ29yaWUgZ2FhdCBtaWpuIHZvb3JrZXVyIGRhYXJvbSB1aXQgbmFhciBgZ2VvbV9jb2woKWAuDQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIEVlbnZvdWRpZw0KDQpgYGB7ciB2Yl9zdGFhZmRpYWdyYW1fZWVudm91ZGlnfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgICMgMSBtb2RlbGphYXIgbGF0ZW4gemllbg0KICAgIGZpbHRlcih5ZWFyID09IDIwMDgpICU+JSANCiAgICAjIGdlbWlkZGVsZGUgcGVyIGBjbGFzc2AgdWl0cmVrZW5lbg0KICAgIGdyb3VwX2J5KGNsYXNzKSAlPiUgDQogICAgc3VtbWFyaXNlKGh3eSA9IG1lYW4oaHd5KSkgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHg9Y2xhc3MsIHk9aHd5KSkgKyAgICAjIGRhdGEgdG9ld2lqemVuIHgtIGVuIHktYXMNCiAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICAgIGxhYnModGl0bGUgPSAiZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpIikNCg0KIyBkYXRhDQptcGcgJT4lIA0KICAgICMgMSBtb2RlbGphYXIgbGF0ZW4gemllbg0KICAgIGZpbHRlcih5ZWFyID09IDIwMDgpICU+JSANCiAgICAjIGdlbWlkZGVsZGUgcGVyIGBjbGFzc2AgdWl0cmVrZW5lbg0KICAgIGdyb3VwX2J5KGNsYXNzKSAlPiUgDQogICAgc3VtbWFyaXNlKGh3eSA9IG1lYW4oaHd5KSkgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHg9Y2xhc3MsIHk9aHd5KSkgKyAgICAjIGRhdGEgdG9ld2lqemVuIHgtIGVuIHktYXMNCiAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX2NvbCgpICsNCiAgICBsYWJzKHRpdGxlID0gImdlb21fY29sKCkiKQ0KYGBgDQoNCiMjIyMjIFVpdGdlYnJlaWQNCg0KYGBge3IgdmJfc3RhYWZkaWFncmFtX3VpdGdlYnJlaWR9DQojIGRhdGENCm1wZyAlPiUgDQogICAgIyAxIG1vZGVsamFhciBsYXRlbiB6aWVuDQogICAgZmlsdGVyKHllYXIgPT0gMjAwOCkgJT4lIA0KICAgICMgZ2VtaWRkZWxkZSBwZXIgYGNsYXNzYCB1aXRyZWtlbmVuDQogICAgZ3JvdXBfYnkoY2xhc3MpICU+JSANCiAgICBzdW1tYXJpc2UoaHd5ID0gbWVhbihod3kpKSAlPiUgDQogICAgYXJyYW5nZShkZXNjKGh3eSkpICU+JSANCiAgICBtdXRhdGUoY2xhc3MgPSBhc19mYWN0b3IoY2xhc3MpICU+JSBmY3RfcmV2KCkpICU+JSANCiAgICANCiAgICAjIGNhbnZhcyAvIGdsb2JhbGUgdG9ld2lqemluZ2VuDQogICAgZ2dwbG90KGFlcyh4PWNsYXNzLCB5PWh3eSwgZmlsbCA9IGNsYXNzKSkgKyAgICAjIGRhdGEgdG9ld2lqemVuIHgtIGVuIHktYXMNCiAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX2NvbCgpICsNCiAgICANCiAgICAjIG9wbWFhaw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgbGFicygNCiAgICAgICAgdGl0bGUgICAgPSAiR2VtaWRkZWxkZSBhZnN0YW5kIG9wIGRlIHNuZWx3ZWciLCAgIyBncmFmaWVrIHRpdGVsDQogICAgICAgIHN1YnRpdGxlID0gIm5hYXIgdHlwZSBhdXRvIiwgICAgICAgICAgICAgICAgICAgICMgc3VidGl0ZWwNCiAgICAgICAgeCAgICAgICAgPSBOVUxMLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aXRlbCB4LWFzDQogICAgICAgIHkgICAgICAgID0gIk1pamxlbiBwZXIgZ2FsbG9uIG9wIGRlIHNuZWx3ZWciLCAgICMgdGl0ZWwgeS1hcw0KICAgICAgICBjYXB0aW9uICA9ICJkYXRhIHNldDogbXBnIiAgICAgICAgICAgICAgICAgICAgICAjIHRvZWxpY2h0aW5nDQogICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQogICAgDQpgYGANCg0KQWFucGFzc2luZ2VuIHZvb3IgaGV0IHVpdGdlYnJlaWRlIHZvb3JiZWVsZDoNCg0KMS4gIERlIGRhdGEgYmV3ZXJrZW4gem9kYXQgZXIgZ2Vzb3J0ZWVyZCB3b3JkdCBvcCBkZSB3YWFyZGUgaW4gYGh3eWA7DQoNCjIuICBPcHZ1bGtsZXVyIHBlciBjYXRlZ29yaWUgdmFuIGNsYXNzIHZpYSBgZmlsbCA9IGNsYXNzYCBpbiBgYWVzKClgOw0KDQozLiAgVWl0Z2VicmVpZGUgdGl0ZWxzIHZpYSBgbGFicygpYDsNCg0KNC4gIE1pbmltYWxlIHRoZW1hIHRvZXBhc3NlbiB2aWEgYHRoZW1lX21pbmltYWwoKWA7DQoNCjUuICBEZSBsZWdlbmRhIHZlcndpamRlcmVuIHZpYSBgdGhlbWUoKWAuDQoNCiMjIyBib3hwbG90IC0gZ2VvbV9ib3hwbG90KCkNCg0KRGUgYm94cGxvdCBnZWVmdCBpbnppY2h0IGluIGRlIHZlcmRlbGluZyB2YW4gZGUgd2Fhcm5lbWluZ2VuIHZhbiBkZSB2YXJpYWJlbGUuIFZvb3IgdWl0bGVnIHppZSBbYm94cGxvdCBvcCB3aWtpcGVkaWFdKGh0dHBzOi8vbmwud2lraXBlZGlhLm9yZy93aWtpL0JveHBsb3QpLg0KDQojIyMjIFZvb3JiZWVsZGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMjIyBFZW52b3VkaWcNCg0KYGBge3IgdmJfYm94cGxvdF9lZW52b3VkaWd9DQojIGRhdGENCm1wZyAlPiUgDQogICAgDQogICAgIyBjYW52YXMgLyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgIGdncGxvdChhZXMoeD1jbGFzcywgeT1od3kpKSArICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fYm94cGxvdCgpDQpgYGANCg0KIyMjIyMgVWl0Z2VicmVpZA0KDQpgYGB7ciB2Yl9ib3hwbG90X3VpdGdlYnJlaWQsIHdhcm5pbmc9RkFMU0V9DQojIGRhdGENCm1wZyAlPiUgDQogICAgIyBzb3J0ZXJlbiBvcCBtZWRpYWFuIHZhbiBod3kNCiAgICBncm91cF9ieShjbGFzcykgJT4lIA0KICAgIG11dGF0ZShod3lfbWVkaWFuID0gbWVkaWFuKGh3eSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIGFycmFuZ2UoZGVzYyhod3lfbWVkaWFuKSkgJT4lDQogICAgbXV0YXRlKGNsYXNzID0gYXNfZmFjdG9yKGNsYXNzKSAlPiUgZmN0X3JldigpKSAlPiUgDQogICAgDQogICAgIyBjYW52YXMgLyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcyAmIGtsZXVyIHRvZXdpanplbiBvYnYgY2xhc3MNCiAgICBnZ3Bsb3QoYWVzKHg9Y2xhc3MsIHk9aHd5LCBmaWxsID0gY2xhc3MpKSArDQogICAgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgc3RhdF9ib3hwbG90KGdlb20gPSAiZXJyb3JiYXIiKSArICMgdm9lZ3QgZGUgZHdhcnNsaWdnZW5kZSBsaWpuZW4gdG9lDQogICAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVFJVRSkgKyAgICAgICMgaW5rZXBpbmcgYmlqIGRlIG1lZGlhYW4gKDkwJSB2ZCB3YWFybi4pDQoNCiAgICAjIG9wbWFhaw0KICAgIGNvb3JkX2ZsaXAoKSArICAjIGdyYWZpZWsga2FudGVsZW4sIHpvZGF0IGNsYXNzIG9wIGRlIHktYXMga29tdA0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlICAgID0gIlZlcmRlbGluZyBhZnN0YW5kIHBlciBnYWxsb24iLCAgICAjIGdyYWZpZWt0aXRlbA0KICAgICAgICBzdWJ0aXRsZSA9ICJuYWFyIHR5cGUgYXV0byIsICAgICAgICAgICAgICAgICAgIyBzdWJ0aXRlbA0KICAgICAgICB4ICAgICAgICA9IE5VTEwsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aXRlbCB4LWFzDQogICAgICAgIHkgICAgICAgID0gIk1pamxlbiBwZXIgZ2FsbG9uIG9wIGRlIHNuZWx3ZWciLCAjIHRpdGVsIHktYXMNCiAgICAgICAgY2FwdGlvbiAgPSAiZGF0YSBzZXQ6IG1wZyIgICAgICAgICAgICAgICAgICAgICMgdG9lbGljaHRpbmcNCiAgICApICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpBYW5wYXNzaW5nZW4gdm9vciBkZSB1aXRnZWJyZWlkZSB2ZXJzaWU6DQoNCjEuICBEZSBkYXRhIGJld2Vya2VuIHpvZGF0IGVyIGdlc29ydGVlcmQgd29yZHQgb3AgZGUgbWVkaWFuZSB3YWFyZGUgaW4gYGh3eWA7DQoyLiAgT3B2dWxrbGV1ciBwZXIgY2F0ZWdvcmllIHZhbiBjbGFzcyB2aWEgYGZpbGwgPSBjbGFzc2AgaW4gYGFlcygpYDsNCjMuICBGb3V0YmFsa2VuIHRvZXZvZWdlbiB2aWEgYHN0YXRfYm94cGxvdChnZW9tID0gImVycm9yYmFyIilgOw0KNC4gIElua2VwaW5nIGJpaiBkZSBtZWRpYW5lIHdhYXJkZSBkb29yIGBub3RjaCA9IFRSVUVgIHRlIGdlYnJ1aWtlbjsNCjUuICBVaXRnZWJyZWlkZSB0aXRlbHMgdmlhIGBsYWJzKClgOw0KNi4gIE1pbmltYWxlIHRoZW1hIHRvZXBhc3NlbiB2aWEgYHRoZW1lX21pbmltYWwoKWA7DQo3LiAgRGUgbGVnZW5kYSB2ZXJ3aWpkZXJlbiB2aWEgYHRoZW1lKClgLg0KDQojIyMgdmlvb2xkaWFncmFtIC0gZ2VvbV92aW9saW4oKQ0KDQpIZXQgdmlvb2xkaWFncmFtIGlzIGVlbiBiZXdlcmtpbmcgdmFuIGRlIGJveHBsb3QsIGRpZSBkZSB2ZXJkZWxpbmcgd2F0IGJldGVyIGxhYXQgemllbi4gRGFhciB3YWFyIGhldCBkaWFncmFtIHNtYWwgaXMsIHppam4gd2VpbmlnIHdhYXJuZW1pbmdlbiBlbiB3YWFyIGhldCBicmVlZCBpcywgdmVlbC4NCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgRWVudm91ZGlnDQoNCmBgYHtyIHZiX3Zpb2xpbl9lZW52b3VkaWd9DQojIGRhdGENCm1wZyAlPiUgDQogICAgDQogICAgIyBjYW52YXMgLyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgIGdncGxvdChhZXMoeD1jbGFzcywgeT1od3kpKSArICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fdmlvbGluKCkNCmBgYA0KDQojIyMjIyBVaXRnZWJyZWlkDQoNCmBgYHtyIHZiX3Zpb2xpbl91aXRnZWJyZWlkfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgICMgc29ydGVyZW4gb3AgbWVkaWFhbiB2YW4gaHd5DQogICAgZ3JvdXBfYnkoY2xhc3MpICU+JSANCiAgICBtdXRhdGUoaHd5X21lZGlhbiA9IG1lZGlhbihod3kpKSAlPiUgDQogICAgdW5ncm91cCgpICU+JSANCiAgICBhcnJhbmdlKGRlc2MoaHd5X21lZGlhbikpICU+JQ0KICAgIG11dGF0ZShjbGFzcyA9IGFzX2ZhY3RvcihjbGFzcykgJT4lIGZjdF9yZXYoKSkgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICAjIGRhdGEgdG9ld2lqemVuIHgtIGVuIHktYXMgJiBrbGV1ciB0b2V3aWp6ZW4gb2J2IGNsYXNzDQogICAgZ2dwbG90KGFlcyh4PWNsYXNzLCB5PWh3eSwgZmlsbCA9IGNsYXNzLCBjb2xvciA9IGNsYXNzKSkgKyAgICANCiAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX2JveHBsb3Qobm90Y2ggPSBUUlVFLCBhbHBoYSA9IDAuNSkgKw0KICAgIGdlb21faml0dGVyKGFscGhhID0gMC4yNSkgKw0KICAgIGdlb21fdmlvbGluKGFscGhhID0gMC41KSArDQogICAgDQogICAgDQogICAgIyBvcG1hYWsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlICAgID0gIlZlcmRlbGluZyBhZnN0YW5kIHBlciBnYWxsb24iLA0KICAgICAgICBzdWJ0aXRsZSA9ICJuYWFyIHR5cGUgYXV0byIsDQogICAgICAgIHggICAgICAgID0gTlVMTCwgICAgICAgICAjIHRpdGVsIHgtYXMNCiAgICAgICAgeSAgICAgICAgPSAiTWlqbGVuIHBlciBnYWxsb24gb3AgZGUgc25lbHdlZyIsICAgICAgICAgICAgIyB0aXRlbCB5LWFzDQogICAgICAgIGNhcHRpb24gID0gImRhdGEgc2V0OiBtcGciDQogICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KQWFucGFzc2luZ2VuIHZvb3IgZGUgdWl0Z2VicmVpZGUgdmVyc2llOg0KDQoxLiAgRGUgZGF0YSBiZXdlcmtlbiB6b2RhdCBlciBnZXNvcnRlZXJkIHdvcmR0IG9wIGRlIG1lZGlhbmUgd2FhcmRlIGluIGBod3lgOw0KMi4gIE9wdnVsa2xldXIgZW4gcmFuZGtsZXVyIHBlciBjYXRlZ29yaWUgdmFuIGNsYXNzIHZpYSBgZmlsbCA9IGNsYXNzLCBjb2xvciA9IGNsYXNzYCBpbiBgYWVzKClgOw0KMy4gIEVlbiBoYWxmIGRvb3J6aWNodGlnZSBib3hwbG90IG1ldCBpbmtlcGluZyB0b2V2b2VnZW4gdmlhIGBnZW9tX2JveHBsb3QoKWA7DQo0LiAgV2Fhcm5lbWluZ2VuIHdlZXJnZXZlbiB2aWEgYGdlb21faml0dGVyKClgOw0KNS4gIGBnZW9tX3Zpb2xpbigpYCBnZWRlZWx0ZWxpamsgZG9vcnppY2h0aWcgbWFrZW4gdmlhIGBhbHBoYSA9IDAuNWA7DQo2LiAgVWl0Z2VicmVpZGUgdGl0ZWxzIHZpYSBgbGFicygpYDsNCjcuICBNaW5pbWFsZSB0aGVtYSB0b2VwYXNzZW4gdmlhIGB0aGVtZV9taW5pbWFsKClgOw0KOC4gIERlIGxlZ2VuZGEgdmVyd2lqZGVyZW4gdmlhIGB0aGVtZSgpYC4NCg0KIyMgMiBjYXRlZ29yaXNjaGUgdmFyaWFiZWxlbg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAhW10od3d3LzIuNSUyMHZpc3VhbGlzYXRpZWxhZ2VuJTIwLSUyMDIlMjBjYXRlZ29yaXNjaGUlMjB2YXJpYWJlbGVuLkpQRykgfCBWYW4gZGUgd2VlcmdhdmVsYWdlbiBvcCBoZXQgc3BpZWticmllZmplIHphbCBpayBhYW5kYWNodCBiZXN0ZWRlbiBhYW46IHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBEZSBzcHJlaWRpbmdzZ3JhZmllazogYGdlb21faml0dGVyKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMjIFNwcmVpZGluZ3NncmFmaWVrIC0gZ2VvbV9qaXR0ZXIoKQ0KDQojIyMjIFZvb3JiZWVsZGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMjIyBFZW52b3VkaWcNCg0KYGBge3IgdmJfaml0MmNhdF9lZW52b3VkaWdfMX0NCiMgZGF0YQ0KZGlhbW9uZHMgJT4lIA0KICAgIA0KICAgICMgY2FudmFzIC8gZ2xvYmFsZSB0b2V3aWp6aW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHg9Y3V0LCB5PWNvbG9yLCBjb2xvciA9IGNsYXJpdHkpKSArICAgICMgZGF0YSB0b2V3aWp6ZW4geC0gZW4geS1hcw0KICAgIA0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21faml0dGVyKCkNCmBgYA0KDQpQZXJzb29ubGlqayB2aW5kIGlrIGhldCBib3ZlbnN0YWFuZGUgZ2VlbiBnb2VkIHZvb3JiZWVsZC4gSWsgaGViIGFsIGRlIGBjbGFyaXR5YCB0b2VnZXZvZWdkIG9tIGhldCBhYW5nZW5hbWVyIHRlIG1ha2VuIHZvb3IgaGV0IG9vZy4gV2F0IGdvZWQgdGUgemllbiBpcywgaXMgd2FhciBlciB2ZWVsIGVuIHdhYXIgZXIgd2VpbmlnIHdhYXJuZW1pbmdlbiB6aWpuLlwNCg0KIyMjIyMgVWl0Z2VicmVpZA0KDQpgYGB7ciB2Yl9qaXQyY2F0X3VpdGdlYnJlaWRfMX0NCiMgZGF0YQ0KbXNsZWVwICU+JSANCiAgICBzZWxlY3QoY29uc2VydmF0aW9uLCBvcmRlciwgdm9yZSkgJT4lIA0KICAgIGRyb3BfbmEoKSAlPiUgDQogICAgYXJyYW5nZShvcmRlciwgY29uc2VydmF0aW9uKSAlPiUgDQogICAgbXV0YXRlKG9yZGVyID0gYXNfZmFjdG9yKG9yZGVyKSAlPiUgZmN0X3JldigpKSAlPiUgDQogICAgDQogICAgIyBjYW52YXMgLyBnbG9iYWxlIHRvZXdpanppbmdlbg0KICAgIGdncGxvdChhZXMoeD1jb25zZXJ2YXRpb24sIHk9b3JkZXIsIGNvbG9yID0gdm9yZSkpICsgICAgIyBkYXRhIHRvZXdpanplbiB4LSBlbiB5LWFzDQogICAgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgZ2VvbV9jb3VudCgpICsNCiAgICANCiAgICAjIG9wbWFhaw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gIk1hbW1hbHMgaW4gc2xlZXAgZGF0YXNldCIsDQogICAgICAgIHkgICAgID0gIk1hbW1hbCBvcmRlciIsDQogICAgICAgIGNvbG9yID0gImRpZXQiDQogICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KYGBgDQoNCkRpdCB2b29yYmVlbGQgaXMgZ2VtYWFrdCBtZXQgYGdlb21fY291bnQoKWAuDQoNCkVyIHppam4gbm9nIHdlbCBhbmRlcmUgbW9nZWxpamtoZWRlbiwgbWFhciBkaWUgd29yZGVuIGhpZXIgbmlldCBiZWhhbmRlbGQuDQoNCiMjIDMgdmFyaWFiZWxlbg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAhW10od3d3LzIuNiUyMHZpc3VhbGlzYXRpZWxhZ2VuJTIwLSUyMDMlMjB2YXJpYWJlbGVuLkpQRykgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IFZhbiBkZSB3ZWVyZ2F2ZWxhZ2VuIG9wIGhldCBzcGlla2JyaWVmamUgemFsIGlrIGFhbmRhY2h0IGJlc3RlZGVuIGFhbjogfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLSAgIEhldCB0ZWdlbGRpYWdyYW06IGBnZW9tX3RpbGUoKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQojIyMgVGVnZWxkaWFncmFtIC0gZ2VvbV90aWxlKCkNCg0KTWV0IGRlemUgZ2VvbSB0ZWtlbiBqZSByZWNodGhvZWtlbiwgZGllIG1ldCBkZSB6LXZhcmlhYmVsZSBlZW4ga2xldXIgb2YgdGludC4gRGl0IG1hYWt0IGRlemUgdmlzdWFsaXNhdGllIGVyZyBnZXNjaGlrdCBvbSBoZWF0bWFwcyB0ZSBtYWtlbi4NCg0KRGUgZGF0YXNldCBkaWUgaW4gaGV0IHNwaWVrYnJpZWZqZSBnZWJydWlrdCB3b3JkdCwgc3ByZWVrdCBuaWV0IGVyZyBhYW4uIEVyIGlzIHZvb3IgZGUgZGF0YXNldCBgZGlhbW9uZHNgIGdla296ZW4sIG9tZGF0IGlrIGRlemUgYmV0ZXIvZHVpZGVsaWprIHZpbmQgdm9vciBoZXQgdm9vcmJlZWxkLg0KDQojIyMjIFZvb3JiZWVsZGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMjIyBFZW52b3VkaWcNCg0KYGBge3IgdmJfdGlsZV9lZW52b3VkaWdfMSwgbWVzc2FnZT1GQUxTRX0NCiBkaWFtb25kcyAlPiUgDQogICAgIGdyb3VwX2J5KGNvbG9yLCBjdXQpICU+JSANCiAgICAgc3VtbWFyaXNlKHByaWNlID0gbWVkaWFuKHByaWNlKSkgJT4lIA0KICAgICB1bmdyb3VwKCkgJT4lIA0KICAgICBnZ3Bsb3QoYWVzKHggPSBjb2xvciwgeSA9IGN1dCwgeiA9IHByaWNlKSkgKyANCiAgICAgZ2VvbV90aWxlKGFlcyhmaWxsID0gcHJpY2UpKQ0KYGBgDQoNCiMjIyMjIFVpdGdlYnJlaWQNCg0KYGBge3IgdmJfdGlsZV91aXRnZWJyZWlkXzEsIG1lc3NhZ2U9RkFMU0V9DQojIGRhdGENCiMgd2Vsa2UgY2F0ZWdvcmllIGhlZWZ0IGRlIGhvb2dzdGUgcHJpanMNCmJlc3RfY2F0IDwtIA0KICAgIGRpYW1vbmRzICU+JSANCiAgICBncm91cF9ieShjb2xvciwgY3V0KSAlPiUgDQogICAgc3VtbWFyaXNlKHByaWNlID0gbWVkaWFuKHByaWNlKSkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gcHJpY2UsIG4gPSAxKQ0KIyBsZXZlbHMgdm9vciB2YXJpYWJlbGVuIG9tIGVlbiByYW5kIHRvZSB0ZSB2b2VnZW4gYWFuIGRlIGNlbCBtZXQgZGUgaG9vZ3N0ZSBwcmlqcw0KY29sb3JfbGlzdCA8LSBsZXZlbHMoZGlhbW9uZHMkY29sb3IpDQpjdXRfbGlzdCAgIDwtIGxldmVscyhkaWFtb25kcyRjdXQpDQoNCiMgZ3JhZmllayBvcGJvdXdlbg0KIyBkYXRhDQpkaWFtb25kcyAlPiUgDQogICAgZ3JvdXBfYnkoY29sb3IsIGN1dCkgJT4lIA0KICAgIHN1bW1hcmlzZShwcmljZSA9IG1lZGlhbihwcmljZSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIA0KICAgICMgZ2dwbG90DQogICAgZ2dwbG90KGFlcyh4ID0gY29sb3IsIHkgPSBjdXQsIHogPSBwcmljZSkpICsgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgIyAtIHRlZ2VsZGlhZ3JhbQ0KICAgIGdlb21fdGlsZShhZXMoZmlsbCA9IHByaWNlKSkgKw0KICAgICMgLSB3YWFyZGVuIGluIGNlbGxlbg0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBzY2FsZXM6OmRvbGxhcihwcmljZSwgYWNjdXJhY3kgPSAxMDApKSwgDQogICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKw0KICAgICMgLSByYW5kIHRvZXZvZWdlbiBhYW4gZGUgaG9vZ3N0ZSB3YWFyZGUNCiAgICBnZW9tX3JlY3Qoc2l6ZSA9IDIsIGZpbGwgPSBOQSwgY29sb3VyPSJ3aGl0ZSIsDQogICAgICAgICAgICBhZXMoeG1pbj13aGljaCgoY29sb3JfbGlzdCAlPiUgZmN0X3JldigpKSAlaW4lIGJlc3RfY2F0JGNvbG9yKSAtIDAuNSwgDQogICAgICAgICAgICAgICAgeG1heD13aGljaChjb2xvcl9saXN0ICVpbiUgYmVzdF9jYXQkY29sb3IpICsgMC41LCANCiAgICAgICAgICAgICAgICB5bWluPXdoaWNoKGN1dF9saXN0ICVpbiUgYmVzdF9jYXQkY3V0KSAtIDAuNSwgDQogICAgICAgICAgICAgICAgeW1heD13aGljaChjdXRfbGlzdCAlaW4lIGJlc3RfY2F0JGN1dCkgKyAwLjUpKSArDQogICAgDQogICAgIyBvcG1hYWsNCiAgICBsYWJzKA0KICAgICAgICB0aXRsZSAgICA9ICJQcmlqcyB2YW4gZGlhbWFudGVuIiwNCiAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuZSBwcmlqcyBwZXIgZ3JvZXAiLA0KICAgICAgICB4ICAgICAgICA9ICJrbGV1ciIsDQogICAgICAgIHkgICAgICAgID0gInNsaWpwdm9ybSIsDQogICAgICAgIGNvbG9yICAgID0gInByaWpzIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSANCmBgYA0KDQpWb29yIGhldCB1aXRnZWJyZWlkZSB2b29yYmVlbGQgemlqbiBkZSB2b2xnZW5kZSBzdGFwcGVuIGdlbm9tZW46DQoNCjEuICBEZSBkYXRhIHZvb3JiZXJlaWRlbiB2b29yIGRlIGdyYWZpZWs7DQoNCjIuICBWdWxrbGV1ciBvcCBiYXNpcyB2YW4gYHByaWNlYDsNCg0KMy4gIGRlIHdhYXJkZSB2YW4gYHByaWNlYCB3ZWVyZ2V2ZW4gaW4gZWxrZSAidGVnZWwiOw0KDQo0LiAgRWVuIHJhbmQgcm9uZG9tIGRlIHRlZ2VsIG1ldCBkZSBob29nc3RlIHByaWpzIG1ldCBgZ2VvbV9yZWN0KClgOw0KDQo1LiAgVGl0ZWxzIGVuIGRlcmdlbGlqa2UgdG9ldm9lZ2VuIGBsYWJzKClgOw0KDQo2LiAgTWluaW1hbGUgb3BtYWFrIHZvb3IgaGV0IHRoZW1hIGB0aGVtZV9taW5pbWFsKClgLg0KDQojIyBLYWFydGVuDQoNCkFscyBqZSBzZXJpZXVzIG1ldCBnZW9ncmFmaXNjaGUgaW5mb3JtYXRpZSBhYW4gZGUgc2xhZyBnYWF0LCBkYW4gcmFhZCBpayBqZSBhYW4gb20ga2FhcnRlbiB0ZSBtYWtlbiBtZXQgcGFja2FnZXMgZGllIGRhYXIgZXJnIGdlc2NoaWt0IHZvb3Igemlqbiwgem9hbHMgW2xlYWZsZXRdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIGVuIFttYXB2aWV3XShodHRwczovL3Itc3BhdGlhbC5naXRodWIuaW8vbWFwdmlldy9pbmRleC5odG1sKS4gRGV6ZSB0b29scyB2cmFnZW4gd2VsIG9tIGRhdGEgbWV0IGVlbiBnZW9ncmFmaXNjaGUgY29tcG9uZW50DQoNCkVlbnZvdWRpZ2Uga2FhcnRlbiBrdW5uZW4gb29rIGluIGdncGxvdCBnZW1hYWt0IHdvcmRlbiBtZXQgaGV0IGBtYXBzYCBwYWNrYWdlLg0KDQojIyMgZ2VvbV9tYXAoKQ0KDQohW10od3d3LzIuNyUyMHZpc3VhbGlzYXRpZWxhZ2VuJTIwLSUyMGthYXJ0ZW4uSlBHKXt3aWR0aD0iNjY3In0NCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgRWVudm91ZGlnDQoNCmBgYHtyIHZiX21hcHNfMX0NCmRhdGEgPC0gDQogICAgZGF0YS5mcmFtZShtdXJkZXIgPSBVU0FycmVzdHMkTXVyZGVyLCANCiAgICAgICAgICAgICAgIHN0YXRlICA9IHRvbG93ZXIocm93bmFtZXMoVVNBcnJlc3RzKSkpDQptYXAgPC0gbWFwX2RhdGEoInN0YXRlIikNCg0KZGF0YSAlPiUgDQogICAgZ2dwbG90KGFlcyhmaWxsID0gbXVyZGVyKSkgKyANCiAgICBnZW9tX21hcChhZXMobWFwX2lkID0gc3RhdGUpLCBtYXAgPSBtYXApICsNCiAgICBleHBhbmRfbGltaXRzKHggPSBtYXAkbG9uZywgeSA9IG1hcCRsYXQpDQpgYGANCg0KIyMjIyMgVWl0Z2VicmVpZA0KDQpgYGB7ciB2Yl9tYXBzXzJ9DQojIGRhdGENCmRhdGEgPC0gDQogICAgZGF0YS5mcmFtZShtdXJkZXIgPSBVU0FycmVzdHMkTXVyZGVyLCANCiAgICAgICAgICAgICAgIHN0YXRlICA9IHRvbG93ZXIocm93bmFtZXMoVVNBcnJlc3RzKSkpDQptYXAgPC0gbWFwX2RhdGEoInN0YXRlIikNCg0KZGF0YSAlPiUgDQogICAgZ2dwbG90KGFlcyhmaWxsID0gbXVyZGVyKSkgKyANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX21hcChhZXMobWFwX2lkID0gc3RhdGUpLCBtYXAgPSBtYXApICsNCiAgICBleHBhbmRfbGltaXRzKHggPSBtYXAkbG9uZywgeSA9IG1hcCRsYXQpICsgICMNCiAgICANCiAgICAjIG9wbWFhaw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlICAgID0gIkhvZSBnZXZhYXJsaWprIGlzIGVlbiAnVVMgc3RhdGU/JyIsDQogICAgICAgIHggICAgICAgID0gIiIsDQogICAgICAgIHkgICAgICAgID0gIiIsDQogICAgICAgIGZpbGwgICAgPSBwYXN0ZTAoImFhbnRhbFxubW9vcmRlbiIpDQogICAgKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZShheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpDQogICAgDQpgYGANCg0KVm9vciBoZXQgdWl0Z2VicmVpZGUgdm9vcmJlZWxkIHppam4gZGUgdm9sZ2VuZGUgc3RhcHBlbiBnZW5vbWVuOg0KDQoxLiAgVGl0ZWxzIGVuIGRlcmdlbGlqa2UgdG9ldm9lZ2VuIGBsYWJzKClgOw0KDQoyLiAgTWluaW1hbGUgb3BtYWFrIHZvb3IgaGV0IHRoZW1hIGB0aGVtZV9taW5pbWFsKClgOw0KDQozLiAgVmVyd2lqZGVyZW4gdmFuIGRlIHdhYXJkZXMgb3AgZGUgeC0gZW4geS1hcyBtZXQgYHRoZW1lKGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCkpYDsNCg0KNC4gIEhldCByYXN0ZXIgdmVyd2lqZGVyZW4gbWV0IGB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKWAuDQoNCiMjIFRpamRyZWVrc2VuDQoNClRpamRyZWVrc2VuIHppam4gdmFhayBsaWpuZGlhZ3JhbW1lbiwgbWFhciBrdW5uZW4gb29rIGJpanZvb3JiZWVsZCBib3hwbG90cyB6aWpuLiBEZSBvdmVyZWVua29tc3QgaXMgZGF0IGVyIGFsdGlqZCBlZW4gdGlqZCwgZGF0dW0gb2YgcGVyaW9kZSBvcCBkZSB4LWFzIHN0YWF0LiBKZSBrdW50IGRlemUgZ3JhZmlla2VuL2RpYWdyYW1tZW4gaGVsZW1hYWwgbWV0IGdncGxvdDIgbWFrZW4uIEFscyBqZSBoaWVyIHNlcmlldXMgbWVlIGFhbiBkZSBzbGFnIHdpbCwgZGFuIHpvdSBpayBqZSBhYW5yYWRlbiBvbSBoZXQgW3RpbWV0a10oaHR0cHM6Ly9idXNpbmVzcy1zY2llbmNlLmdpdGh1Yi5pby90aW1ldGsvcmVmZXJlbmNlL2luZGV4Lmh0bWwjdGltZS1zZXJpZXMtcGxvdHRpbmcpIHBhY2thZ2UgdGUgZ2VicnVpa2VuLCB3YWFyaW4gbW9vaWUgcGxvdC1mdW5jdGllcyBvcGdlbm9tZW4gemlqbi4NCg0KIVtdKHd3dy8yLjglMjB2aXN1YWxpc2F0aWVsYWdlbiUyMC0lMjB0aWpkcmVla3Nlbi5KUEcpe3dpZHRoPSI2NzAifQ0KDQojIyMjIFZvb3JiZWVsZGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMjIyBFZW52b3VkaWcNCg0KYGBge3IgdGlqZHJlZWtzX2VlbnZvdWRpZ30NCiMgZGF0YQ0KZWNvbm9taWNzICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKGRhdGUsIHVuZW1wbG95KSkgKw0KICAgIGdlb21fbGluZSgpDQpgYGANCg0KIyMjIyMgVWl0Z2VicmVpZA0KDQpgYGB7ciB0aWpkcmVla3NfdWl0Z2VicmVpZCwgbWVzc2FnZT1GQUxTRX0NCiMgZGF0YQ0KZWNvbm9taWNzICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKGRhdGUsIHVuZW1wbG95KSkgKw0KICAgICMgd2VlcmdhdmVsYWdlbg0KICAgIGdlb21fbGluZSgpICsNCiAgICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogICAgDQogICAgIyBvcG1hYWsNCiAgICBsYWJzKA0KICAgICAgICB0aXRsZSAgICA9ICJXZXJrbG9vc2hlaWQgaW4gZGUgVlMiLA0KICAgICAgICB4ICAgICAgICA9ICIiLA0KICAgICAgICB5ICAgICAgICA9ICJ4IDEwMDAgcGVyc29uZW4iLA0KICAgICAgICBjYXB0aW9uICA9ICJicm9uOiBodHRwczovL2ZyZWQuc3Rsb3Vpc2ZlZC5vcmcvc2VyaWVzL1VORU1QTE9ZIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpDQpgYGANCg0KVm9vciBoZXQgdWl0Z2VicmVpZGUgdm9vcmJlZWxkIHppam4gZGUgdm9sZ2VuZGUgc3RhcHBlbiBnZW5vbWVuOg0KDQoxLiAgVG9ldm9lZ2VuIHZhbiBlZW4gdHJlbmRsaWpuIG1pZGRlbHMgYGdlb21fc21vb3RoKHNlID0gRkFMU0UpYCAoem9uZGVyIGJhbmRicmVlZHRlKTsNCg0KMi4gIFRpdGVscyBlbiBkZXJnZWxpamtlIHRvZXZvZWdlbiBgbGFicygpYDsNCg0KMy4gIE1pbmltYWxlIG9wbWFhayB2b29yIGhldCB0aGVtYSBgdGhlbWVfbWluaW1hbCgpYDsNCg0KNC4gIFdhYXJkZXMgb3AgZGUgeS1hcyBuZXRqZXMgb3BtYWtlbiBtZXQgYHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKS5gDQoNCiMjIFZpc3VhbGlzZXJlbiB2YW4gZm91dGVuDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgIVtdKHd3dy8yLjklMjB2aXN1YWxpc2F0aWVsYWdlbiUyMC0lMjBmb3V0ZW4uSlBHKSB8IFplbGYgdmluZCBpayBkZSBkdWlkZWxpamtzdGUgdmFyaWFudCBvbSBlZW4gICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGZvdXRyYW5nZSBhYW4gdGUgZ2V2ZW4gZWVuIGNvbWJpbmF0aWUgdmFuICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGBnZW9tX2Vycm9yYmFyKClgIG1ldCBgZ2VvbV9wb2ludCgpYC4gICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IERlemUgemFsIGlrIGhpZXJvbmRlciBsYXRlbiB6aWVuLiAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KVm9vciBoZXQgdmlzdWFsaXNlcmVuIHZhbiBmb3V0ZW4gd29yZHQgZmljdGlldmUgZGF0YSBnZWdlbmVyZWVyZCBlbiBuaWV0IGVlbiBkYXRhc2V0IHVpdCBoZXQgZ2dwbG90MiBwYWNrYWdlIGdlYnJ1aWt0LiBIZXQgZWVudm91ZGlnZSB2b29yYmVlbGQgemlldCBlciBkYW4gYWxzIHZvbGd0IHVpdDoNCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgRWVudm91ZGlnDQoNCmBgYHtyIGZvdXRlbl9lZW52b3VkaWd9DQojIGRhdGENCmRmIDwtIGRhdGEuZnJhbWUoZ3JwID0gYygiQSIsICJCIiwgIkMiKSwgZml0ID0gYyg0LCA1LCA1LjUpLCBzZSA9IGMoMSwgMiwgMC41KSkNCiMgb20gZGUgZGF0YSB0ZSBiZWtpamtlbg0KZGYNCg0KZGYgJT4lIA0KICAgIA0KICAgICMgYmFzaXMgZ3JhZmllaw0KICAgIGdncGxvdCgNCiAgICAgICAgYWVzKHggPSBncnAsICAgICAgICMgdmFyaWFiZWxlIHZvb3IgZGUgeC1hcw0KICAgICAgICAgICAgeSA9IGZpdCwgICAgICAgIyBwdW50IHZhbiBkZSB3YWFybmVtaW5nDQogICAgICAgICAgICB5bWluID0gZml0LXNlLCAjIHktd2FhcmRlIHZvb3IgZGUgb25kZXJzdGUgZm91dGJhbGsNCiAgICAgICAgICAgIHltYXggPSBmaXQrc2UpICMgeS13YWFyZGUgdm9vciBkZSBib3ZlbnN0ZSBmb3V0YmFsaw0KICAgICAgICAgICApICsNCiAgICANCiAgICAjIHdlZXJnYXZlbGFnZW4NCiAgICBnZW9tX2Vycm9yYmFyKCkgKyAgICAgICAgIyBmb3V0YmFsa2VuDQogICAgZ2VvbV9wb2ludCgpICAgICAgICAgICAgICMgcHVudCB2YW4gZGUgd2Fhcm5lbWluZw0KYGBgDQoNCiMjIyMjIFVpdGdlYnJlaWQNCg0KYGBge3IgZm91dGVuX3VpdGdlYnJlaWR9DQojIGRhdGENCmRmIDwtIGRhdGEuZnJhbWUoZ3JwID0gYygiQSIsICJCIiwgIkMiKSwgZml0ID0gYyg0LCA1LCA1LjUpLCBzZSA9IGMoMSwgMiwgMC41KSkNCiMgb20gZGUgZGF0YSB0ZSBiZWtpamtlbg0KZGYNCg0KZGYgJT4lIA0KICAgIA0KICAgICMgYmFzaXMgZ3JhZmllaw0KICAgIGdncGxvdCgNCiAgICAgICAgYWVzKHggICAgID0gZ3JwLCAgICAjIHZhcmlhYmVsZSB2b29yIGRlIHgtYXMNCiAgICAgICAgICAgIGNvbG9yID0gZ3JwLCAgICAjIGtsZXVyIHZvb3Igd2Fhcm5lbWluZyBlbiBsaWpuZW4NCiAgICAgICAgICAgIHkgICAgID0gZml0LCAgICAjIHB1bnQgdmFuIGRlIHdhYXJuZW1pbmcNCiAgICAgICAgICAgIHltaW4gID0gZml0LXNlLCAjIHktd2FhcmRlIHZvb3IgZGUgb25kZXJzdGUgZm91dGJhbGsNCiAgICAgICAgICAgIHltYXggID0gZml0K3NlKSAjIHktd2FhcmRlIHZvb3IgZGUgYm92ZW5zdGUgZm91dGJhbGsNCiAgICAgICAgICAgKSArDQogICAgDQogICAgIyB3ZWVyZ2F2ZWxhZ2VuDQogICAgZ2VvbV9lcnJvcmJhcihzaXplID0gMSkgKyAgIyBmb3V0YmFsa2VuDQogICAgZ2VvbV9wb2ludChzaXplID0gMykgKyAgICAgIyBwdW50IHZhbiBkZSB3YWFybmVtaW5nICh3YXQgZ3JvdGVyIGdlbWFha3QpDQogICAgDQogICAgIyBvcG1hYWsNCiAgICBsYWJzKA0KICAgICAgICB0aXRsZSAgICA9ICJXYWFybmVtaW5nIGVuIGZvdXRtYXJnZSBwZXIgY2F0ZWdvcmllIiwNCiAgICAgICAgeCAgICAgICAgPSAiIiwNCiAgICAgICAgeSAgICAgICAgPSAid2FhcmRlIg0KICAgICkgKw0KICAgIGV4cGFuZF9saW1pdHMoeSA9IGMoMCwgbWF4KGRmJGZpdCkrbWF4KGRmJHNlKSkpICsgIyB5LWFzIGJlZ2lubmVuIGJpaiAwDQogICAgdGhlbWVfbWluaW1hbCgpICsgDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyB2ZXJ3aWpkZXJlbiB2YW4gZGUgbGVnZW5kYQ0KICAgIA0KYGBgDQoNClZvb3IgaGV0IHVpdGdlYnJlaWRlIHZvb3JiZWVsZCB6aWpuIGRlIHZvbGdlbmRlIHN0YXBwZW4gZ2Vub21lbjoNCg0KMS4gIEtsZXVyIG9wIGJhc2lzIHZhbiBkZSB2YXJpYWJlbGUgYGdycGAgdG9lZ2V2b2VnZCBpbiBkZSBgYWVzKClgOw0KDQoyLiAgRGUgZGlrdGUgdmFuIGRlIGVycm9yYmFyIGVuIHB1bnQgdmFuIGRlIHdhYXJuZW1pbmcgaWV0cyB2ZXJncm9vdCBkb29yIGhldCBhcmdldW1lbnQgYHNpemVgIHRlIGdlYnJ1aWtlbjsNCg0KMy4gIFRpdGVscyBlbiBkZXJnZWxpamtlIHRvZXZvZWdlbiBgbGFicygpYDsNCg0KNC4gIFktYXMgYmlqIDAgbGF0ZW4gYmVnaW5uZW4gbWV0DQoNCjUuICBNaW5pbWFsZSBvcG1hYWsgdm9vciBoZXQgdGhlbWEgYHRoZW1lX21pbmltYWwoKWA7DQoNCjYuICBMZWdlbmRhIHZlcndpamRlcmQgbWV0IGB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpYC4NCg0KIyBBYW5wYXNzaW5nZW4gaW4gcG9zaXRpZXMgKHBvc2l0aW9uKQ0KDQpQb3NpdGllIGFhbnBhc3NpbmdlbiBiZXBhbGVuIGhvZSBvdmVybGFwcGVuZGUgd2VlcmdhdmVzIGdldG9vbmQgd29yZGVuLg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICFbXSh3d3cvNCUyMHBvc2l0aW9ucy5KUEcpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRGUgb3B0aWVzIHppam46ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMS4gIGRvZGdlIC0tXD4gbmFhc3QgZWxrYWFyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMi4gIGZpbGwgLS1cPiBzdGFwZWx0IHRvdCAxMDAlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMy4gIHN0YWNrIC0tXD4gc3RhcGVsdCBvcCBiYXNpcyB2YW4gZGUgd2FhcmRlcyAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNC4gIGppdHRlciAtLVw+IHZvZWd0IHJ1aXMgdG9lLCB6b2RhdCB3YWFyZGVzIG5pZXQgb3ZlcmxhcHBlbiB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCkhldCBzcGlla2JyaWVmamUgbGVndCBoZXQgZWlnZW5saWprIGhlZWwgZ29lZCB1aXQuDQoNCkRlIGJhc2lzZ3JhZmllayBpcyBhbHMgdm9sZ3Q6DQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIGJhc2lzDQoNCmBgYHtyIHBvc2l0aWVzfQ0KbXBnICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKHggPSBmbCwgZmlsbCA9IGRydikpICsNCiAgICBnZW9tX2JhcigpDQpgYGANCg0KIyMjIyMgZG9kZ2UNCg0KQWxzIHdlIGBwb3NpdGlvbiA9ICJkb2RnZSJgIGdlYnJ1aWtlbiwgZGFuIHdvcmRlbiBkZSBiYWxrZW4gbmFhc3QgZWxrYWFyIGdlcGxhYXRzdC4NCg0KYGBge3IgcG9zaXRpZXNfZG9kZ2V9DQptcGcgJT4lIA0KICAgIA0KICAgICMgYmFzaXMgZ3JhZmllaw0KICAgIGdncGxvdChhZXMoeCA9IGZsLCBmaWxsID0gZHJ2KSkgKw0KICAgICMgcG9zaXRpZSB2YW4gZGUgYmFsa2VuDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQ0KYGBgDQoNCkplIGt1bnQgbWV0IGBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0geClgIG9vayBhYW5nZXZlbiBob2UgdmVyIGRlIHZlcnNjaHVpdmluZyBtb2V0IHppam4uIE1ldCBgeCA9IDAuNWAgenVsbGVuIGRlIGJhbGtlbiBlbGthYXIgb3ZlcmxhcHBlbiBlbiBtZXQgYHggPSAxYCB6dWxsZW4gZGUgYmFsa2VuIGlldHMgdWl0IGVsa2FhciBzdGFhbiAoemllIGhpZXJvbmRlcikuDQoNCkRlemUgaW5zdGVsbGluZyBrdW4gamUgb29rIHZvb3IgYW5kZXJlIHdlZXJnYXZlbGFnZW4gKGdlb21zKSBnZWJydWlrZW4uIFppZSBkYWFydm9vclwNCmA/cG9zdGlvbl9kb2RnZSgpYC4NCg0KYGBge3IgcG9zaXRpZXNfZG9kZ2UxfQ0KbXBnICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKHggPSBmbCwgZmlsbCA9IGRydikpICsNCiAgICAjIHBvc2l0aWUgdmFuIGRlIGJhbGtlbg0KICAgIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAxKSkNCmBgYA0KDQojIyMjIyBmaWxsDQoNCkFscyB3ZSBgcG9zaXRpb24gPSAiZmlsbCJgIGdlYnJ1aWtlbiwgZGFuIHdvcmRlbiBkZSBiYWxrZW4gZ2VzdGFwZWxkIGVuIGdlbm9ybWFsaXNlZXJkIChzdGFwZWx0IHRvdCAxMDAlKS4gRGl0IGlzIGVyZyBnZXNjaGlrdCBvbSBoZXQgYWFuZGVlbCBwZXIgY2F0ZWdvcmllIGFhbiB0ZSBnZXZlbi4NCg0KYGBge3IgcG9zaXRpZXNfZmlsbH0NCm1wZyAlPiUgDQogICAgDQogICAgIyBiYXNpcyBncmFmaWVrDQogICAgZ2dwbG90KGFlcyh4ID0gZmwsIGZpbGwgPSBkcnYpKSArDQogICAgIyBwb3NpdGllIHZhbiBkZSBiYWxrZW4NCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikNCmBgYA0KDQojIyMjIyBzdGFjaw0KDQpBbHMgd2UgYHBvc2l0aW9uID0gInN0YWNrImAgZ2VicnVpa2VuLCBkYW4gd29yZGVuIGRlIGJhbGtlbiBnZXN0YXBlbGQgem9uZGVyIHZlcmRlcmUgYmV3ZXJraW5nLiBEaXQgaXMgZGUgc3RhbmRhYXJkaW5zdGVsbGluZy4gRGFhcm1lZSBpcyBkZSBncmFmaWVrIGR1cyBoZXR6ZWxmZGUgYWxzIGRlIGJhc2lzIGdyYWZpZWsuDQoNCmBgYHtyIHBvc2l0aWVzX3N0YWNrfQ0KbXBnICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKHggPSBmbCwgZmlsbCA9IGRydikpICsNCiAgICAjIHBvc2l0aWUgdmFuIGRlIGJhbGtlbg0KICAgIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIikNCmBgYA0KDQojIyMjIyBqaXR0ZXINCg0KT20gZHVpZGVsaWprIHRlIG1ha2VuIHdhdCBgcG9zaXRpb24gPSAiaml0dGVyImAgKG5ldCBhbHMgYGdlb21faml0dGVyKClgKSBkb2V0LCBpcyBoaWVyb25kZXIgZWVuIGdyYWZpZWsgb3BnZW5vbWVuIG1ldCBkZSBvcmlnaW5lbGUgcHVudGVuIGluIHJvb2QgZW4gc3RlcmsgZG9vcnppY2h0aWcgZW4gZGUgYCJqaXR0ZXIiYCBpbiBoZXQgendhcnQgbWV0IGVuaWdlIGRvb3J6aWNodGlnaGVpZC4NCg0KRG9vcmRhdCBzb21taWdlIHB1bnRlbiByb2RlciB6aWpuIGRhbiBhbmRlcmVuLCBrdW5uZW4gd2UgemllbiBkYXQgZXIgb3ZlcmxhcCBpbiB3YWFybmVtaW5nZW4gaXMuIERlIHp3YXJ0ZSBwdW50ZW4ga2VubmVuIG5hdXdlbGlqa3Mgb3ZlcmxhcC4NCg0KYGBge3IgcG9zaXRpZXNfaml0dGVyfQ0KbXBnICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgIyBzcHJlaWRpbmdzZ3JhZmllayBtZXQgZG9vcnppY2h0aWdlIHB1bnRlbg0KICAgIGdlb21fcG9pbnQoY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjI1KSArIA0KICAgICMgaml0dGVyIG9tIGluZGl2aWR1ZWxlIHB1bnRlbiBiZXRlciB6aWNodGJhYXIgdGUgbWFrZW4NCiAgICBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIsIGFscGhhID0gMC41KQ0KYGBgDQoNCiMgU2NoYWxlbiAoc2NhbGUpDQoNClNjaGFsZW4ga3VubmVuIGluIGJlbGFuZ3JpamtlIG1hdGUgYmVwYWxlbiBob2UgamUgZ3JhZmllayBlcnVpdCB6aWV0LiBaZSBoZWJiZW4gaW52bG9lZCBvcDoNCg0KLSAgIERlIGRvb3J6aWNodGlnaGVpZCAoYHNjYWxlX2FscGhhXypgKQ0KDQotICAgS2xldXJlbiBpbiBqZSBncmFmaWVrIChgc2NhbGVfY29sb3JfKmAsIGBzY2FsZV9maWxsXypgKQ0KDQotICAgVm9ybWVuIChgc2NhbGVfc2hhcGVfKmAsIGBzY2FsZV9saW5ldHlwZV8qYCkNCg0KLSAgIERlIGFmbWV0aW5nZW4gdmFuIG8uYS4gbGlqbmVuIGVuIHB1bnRlbiAoYHNjYWxlX3NpemVfKmApDQoNCi0gICBEZSBhc3NlbiAoYHNjYWxlX3hfKmAsIGBzY2FsZV95XypgKQ0KDQpPcCBlbGsgdmFuIGRlIGAqYCAoYmVoYWx2ZSB2b29yIGRlIGFzc2VuKSBrdW5uZW4gdm9vcmtvbWVuOg0KDQotICAgY29udGludW91cywgYmlqdm9vcmJlZWxkIGBzY2FsZV9maWxsX2NvbnRpbm91c2ANCg0KLSAgIGRpc2NyZXRlLCBiaWp2b29yYmVlbGQ6IGBzY2FsZV9jb2xvcl9kaXNjcmV0ZWANCg0KLSAgIGlkZW50aXR5LCBiaWp2b29yYmVlbGQ6IGBzY2FsZV9jb2xvcl9pZGVudGl0eWANCg0KLSAgIG1hbnVhbCwgYmlqdm9vcmJlZWxkOiBgc2NhbGVfY29sb3JfbWFudWFsYA0KDQpIZXQgc3BpZWticmllZmplIGdhYXQgaW4gb3A6DQoNCjEuICBTY2hhbGVuIGVuIGtsZXVyZW4NCjIuICBTY2hhbGVuIGVuIHZvcm1lbg0KMy4gIFNjaGFsZW4gZW4gYWZtZXRpbmdlbg0KDQojIyBTY2hhbGVuIGVuIGtsZXVyZW4NCg0KSGllcm1lZSBoZWIgamUgY29udHJvbGUgb3ZlciBoZXQga2xldXJnZWJydWlrIGluIGRlIGdyYWZpZWsuDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgIVtdKHd3dy81LjElMjBzY2hhbGVuX2tsZXVyZW4uanBlZykgICAgICAgICAgICAgICB8IEluIGRlIHZvb3JiZWVsZGVuIHZhbiBoZXQgc3BpZWticmllZmplIHdvcmRlbiAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGFsbGVlbiB2b29yYmVlbGRlbiBtZXQgdnVsa2xldXJlbiBnZXRvb25kLiAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEplIHp1bHQgaGllcm9uZGVyIG9vayB2b29yYmVlbGRlbiB2YW4gYGNvbG9yYCAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHZpbmRlbiBpbiBwbGFhdHMgdmFuIGFsbGVlbiBtZXQgYGZpbGxgLiAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMjIERpc2NyZWV0LCB2dWxrbGV1cmVuDQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIEJhc2lzDQoNCmBgYHtyIGRpc2NyZWV0X3Z1bGtsZXVyX3NjaGFsZW5fYmFzaXN9DQoNCiMgem9uZGVyIGFhbnBhc3Npbmdlbg0KbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBmbCkpICsNCiAgICBnZW9tX2JhcigpDQpgYGANCg0KIyMjIyMgTWV0IGRpc2NyZXRlIHZ1bGtsZXVyDQoNCmBgYHtyIGRpc2NyZWV0X3Z1bGtsZXVyX3NjaGFsZW5faW5nZWtsZXVyZH0NCg0KIyBibGF1d2UgYmFsa2VuIHZvbGdlbnMgaGV0IGFhbmdlZ2V2ZW4gcGFsZXR0ZSBvcCBicmFuZHN0b2YgdHlwZQ0KbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBmbCkpICsNCiAgICBnZW9tX2JhcihhZXMoZmlsbCA9IGZsKSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQWNjZW50IikNCmBgYA0KDQojIyMgRGlzY3JlZXQga2xldXINCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgQmFzaXMNCg0KYGBge3IgZGlzY3JlZXRfa2xldXJfc2NoYWxlbl9iYXNpc30NCg0KIyB6b25kZXIgYWFucGFzc2luZ2VuDQptcGcgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQojIyMjIyBNZXQgZGlzY3JldGUga2xldXINCg0KYGBge3IgZGlzY3JlZXRfa2xldXJfc2NoYWxlbl9pbmdla2xldXJkfQ0KDQojIGtsZXVyZW4gb3AgYmFzaXMgdmFuIGRlIHR5cG9sb2dpZSB2YW4gZGUgYXV0bw0KbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpICsNCiAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJBY2NlbnQiKQ0KYGBgDQoNCiMjIyBDb250aW51ZSB2dWxrbGV1cmVuDQoNCiMjIyMgVm9vcmJlZWxkZW4gey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyMjIEJhc2lzDQoNCmBgYHtyIGNvbnRpbnVlX3Z1bGtsZXVyX3NjaGFsZW5fYmFzaXN9DQoNCiMgem9uZGVyIGFhbnBhc3Npbmdlbg0KbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBod3kpKSArDQogICAgZ2VvbV9iYXIoKQ0KDQpgYGANCg0KIyMjIyMgTWV0IGNvbnRpbnVlIHZ1bGtsZXVyDQoNCmBgYHtyIGNvbnRpbnVlX3Z1bGtsZXVyX3NjaGFsZW5faW5nZWtsZXVyZH0NCg0KIyBpbmdla2xldXJkIG9wIGJhc2lzIHZhbiBicmFuZHN0b2ZnZWJydWlrIG9wIGRlIHNuZWx3ZWcNCm1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gaHd5KSkgKw0KICAgIGdlb21fYmFyKGFlcyhmaWxsID0gLi54Li4pKSArICMgLi54Li4gdmVyd2lqc3QgbmFhciB4LXdhYXJkZSB2YW4gZGUgYmFsaw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZGF0IGlzIGR1cyBoZXQgcmVzdWx0YWF0IHZhbiBoZXQgZ3JvZXBlcmVuIHZhbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZGUgeC13YWFyZGUgaW4gYWVzKHggPSBod3kpDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudCgNCiAgICAgICAgbG93ID0gInJlZCIsDQogICAgICAgIGhpZ2ggPSAieWVsbG93Ig0KICAgICAgICApDQoNCmBgYA0KDQojIyMgQ29udGludWUga2xldXINCg0KIyMjIyBWb29yYmVlbGRlbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgQmFzaXMNCg0KYGBge3IgY29udGludWVfa2xldXJfc2NoYWxlbl9iYXNpc30NCg0KIyB6b25kZXIgYWFucGFzc2luZ2VuDQptcGcgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQojIyMjIyBNZXQgY29udGludWUga2xldXINCg0KYGBge3IgY29udGludWVfa2xldXJfc2NoYWxlbl9pbmdla2xldXJkfQ0KDQojIGtsZXVyZW4gb3AgYmFzaXMgdmFuIGRlIHR5cG9sb2dpZSB2YW4gZGUgYXV0bw0KbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkaXNwbCkpICsNCiAgICBzY2FsZV9jb2xvcl9ncmFkaWVudCgNCiAgICAgICAgbG93ID0gImJsdWUiLA0KICAgICAgICBoaWdoID0gInJlZCINCiAgICAgICAgKQ0KYGBgDQoNCiMjIFNjaGFsZW4gZW4gdm9ybWVuDQoNCkhpZXJtZWUgaGViIGplIGNvbnRyb2xlIG92ZXIgd2Vsa2Ugdm9ybWVuIGplIGluIGRlIGdyYWZpZWsgbGFhdCB6aWVuLiBCaWogYGdlb21fcG9pbnQoKWAgaXMgZGl0IHN0YW5kYWFyZCBlZW4gZ2V2dWxkIGJvbGxldGplLg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICFbXSh3d3cvNS4yJTIwc2NoYWxlbl92b3JtZW4uanBlZykgICAgICAgICAgICAgICAgfCBIZXQgc3BpZWticmllZmplIGJpZWR0IGhpZXIsIGJlaGFsdmUgZWVuIHZvb3JiZWVsZCB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBvb2sgZWVuIGhhbmRpZyBvdmVyemljaHQgdmFuIHdlbGsgY2lqZmVyIGJpaiB3ZWxrZSB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCB2b3JtIGhvb3J0LiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCmBgYHtyIHZvcm1fc2NoYWxlbn0NCg0KIyB6b25kZXIgYWFucGFzc2luZ2VuDQptcGcgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGZsKSkgIyBmb3JtZW4gb3AgYmFzaXMgdmFuIHR5cGUgYnJhbmRzdG9mDQoNCiMgdG9vbiBhbGxlZW4gZGUgcmFuZGVuL2xpam5lbiB2YW4gZGUgdm9ybQ0KbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBmbCkpICsgDQogICAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkNCg0KIyBoYW5kbWF0aWcgZ2Vrb3plbiB2b3JtZW4NCm1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gY3R5LCB5ID0gaHd5KSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gZmwpKSArDQogICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMzo3KSkNCg0KYGBgDQoNCiMjIFNjaGFsZW4gZW4gYWZtZXRpbmdlbg0KDQpIaWVybWVlIGhlYiBqZSBjb250cm9sZSBvdmVyIGJpanZvb3JiZWVsZCBkZSBncm9vdHRlIHZhbiBkZSBwdW50ZW4gaW4gZWVuIHNwcmVpZGluZ3NncmFmaWVrLg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICFbXSh3d3cvNS4zJTIwc2NoYWxlbl9hZm1ldGluZ2VuLmpwZWcpICAgICAgICAgICAgfCBMZXQgb3A6IGhldCB2b29yYmVlbGQgb3AgaGV0IHNwaWVrYnJpZWZqZSBrbG9wdCAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBoaWVyIG5pZXQuIEhldCBhcmd1bWVudCBgbWF4YCBpcyB2ZXJ2YW5nZW4gZG9vciAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgbWF4X3NpemVgLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCmBgYHtyIGFmbWV0aW5nZW5fc2NoYWxlbn0NCg0KIyB6b25kZXIgYWFucGFzc2luZ2VuLCBtZXQgZ3Jvb3R0ZSBvcCBiYXNpcyB2YW4gaGV0IGFhbnRhbCBjeWxpbmRlcnMNCm1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gY3R5LCB5ID0gaHd5KSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjeWwpKQ0KDQojIG1heGltYWxlIHB1bnRncm9vdHRlIGJlcGVya2VuDQptcGcgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gY3lsKSkgKw0KICAgIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDYpDQpgYGANCg0KIyBDb8O2cmRpbmF0ZW5zeXN0ZWVtIChjb29yZCkNCg0KSGV0IGNvw7ZyZGluYXRlbnN5c3RlZW0gaGVlZnQgaW52bG9lZCBvcCBkZSB4LSBlbiBkZSB5LWFzIHZhbiBkZSBncmFmaWVrLg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICFbXSh3d3cvNi4lMjBjb29yZGluYWF0X3N5c3RlbWVuLmpwZWcpICAgICAgICAgICAgfCBOaWV0IGFsbGUgbW9nZWxpamtoZWRlbiB3b3JkZW4gdmFhayAoZG9vciBtaWopICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBnZWJydWlrdC4gRGFhcm9tIHdvcmRlbiBvb2sgbmlldCBhbGxlIG1vZ2VsaWpraGVkZW58DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBiZWhhbmRlbGQuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBIaWVyb25kZXIgemFsIGdla2VrZW4gd29yZGVuIG5hYXI6ICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxLiAgY29vcmRfY2FydGVzaWFuKCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAyLiAgY29vcmRfZmxpcCgpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzLiAgY29vcmRfdHJhbnMoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCiMjIENhcnRlc2lhYW5zZSBjb8O2cmRpbmF0ZW4NCg0KRGl0IGlzIGhldCBub3JtYWxlIGNvw7ZyZGluYXRlbnN5c3RlZW0gdm9vciBncmFmaWVrZW4uIEplIGt1bnQgZGFhcmJpaiBhbHMgaGV0IHdhcmUgaW56b29tZW4gb3AgZGUgZ3JhZmllayBkb29yIGdyZW5zd2FhcmRlcyB2b29yIGRlIHgtIGVuL29mIHktYXMgb3AgdGUgZ2V2ZW4uDQoNCmBgYHtyIGNvb3JkX2NhcnRlc2lhbn0NCiMgem9uZGVyIGFhbnBhc3Npbmdlbg0KZzEgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBod3kpKSArDQogICAgZ2VvbV9iYXIoKSArDQogICAgbGFicyh0aXRsZSA9ICJ6b25kZXIgYWFucGFzc2luZ2VuIikNCg0KIyBhbGxlZW4gZGUgd2FhcmRlcyAob3AgZGUgeC1hcykgdHVzc2VuIDEwIGVuIDIwIHRvbmVuDQpnMiA8LSAgbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBod3kpKSArDQogICAgZ2VvbV9iYXIoKSArDQogICAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEwLCAyMCkpICsNCiAgICBsYWJzKHRpdGxlID0gIngtYXMgYmVwZXJrdCB0b3QgdHVzc2VuIDEwIGVuIDIwIikNCg0KZzEgKyBnMiAjIGdyZWZpZWtlbiBuYWFzdCBlbGthYXIgdG9uZW4NCmBgYA0KDQojIyBBc3NlbiB3aXNzZWxlbg0KDQpEb29yIGBjb29yZF9mbGlwKClgIHRlIGdlYnJ1aWtlbiwga3VuIGplIGRlIHgtIGVuIHktYXMgb213aXNzZWxlbi4gSmUga3VudCBkYW4gYmlqdm9vcmJlZWxkIGVlbiBsaWdnZW5kIHN0YWFmZGlhZ3JhbSBtYWtlbi4NCg0KYGBge3IgY29vcmRfZmxpcH0NCiMgem9uZGVyIGFhbnBhc3Npbmdlbg0KZzEgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBmbCkpICsNCiAgICBnZW9tX2JhcigpICsNCiAgICBsYWJzKHRpdGxlID0gInpvbmRlciBhYW5wYXNzaW5nZW4iKQ0KDQojIGFsbGVlbiBkZSB3YWFyZGVzIChvcCBkZSB4LWFzKSB0dXNzZW4gMTAgZW4gMjAgdG9uZW4NCmcyIDwtIG1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gZmwpKSArDQogICAgZ2VvbV9iYXIoKSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICBsYWJzKHRpdGxlID0gIngtIGVuIHktYXMgb21nZXdpc3NlbGQiKQ0KDQpnMSArIGcyICMgZ3JlZmlla2VuIG5hYXN0IGVsa2FhciB0b25lbg0KYGBgDQoNCiMjIFNjaGFhbCBhcyBvbXJla2VuZW4NCg0KYGBge3IgY29vcmRfdHJhbnN9DQojIHpvbmRlciBhYW5wYXNzaW5nZW4NCmcxIDwtIG1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gaHd5KSkgKw0KICAgIGdlb21fYmFyKCkgKw0KICAgIGxhYnModGl0bGUgPSAiem9uZGVyIGFhbnBhc3NpbmdlbiIpDQoNCiMgYWxsZWVuIGRlIHdhYXJkZXMgKG9wIGRlIHgtYXMpIHR1c3NlbiAxMCBlbiAyMCB0b25lbg0KZzIgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBod3kpKSArDQogICAgZ2VvbV9iYXIoKSArDQogICAgY29vcmRfdHJhbnMoeCA9ICJsb2cxMCIsIHkgPSAic3FydCIpICsgIyBvbXJla2VuZW4gdmFuIGRlIGFzc2VuDQogICAgbGFicyh0aXRsZSA9ICJ4LWFzIGluIGxvZyBzY2hhYWwsIHktYXMgd29ydGVsIikNCg0KZzEgKyBnMiAjIGdyYWZpZWtlbiBuYWFzdCBlbGthYXIgdG9uZW4NCmBgYA0KDQojIE9yZGVuaW5nIHZhbiBtZWVyZGVyZSBncmFmaWVrZW4gKGZhY2V0KQ0KDQpHZ3Bsb3QyIGtlbnQgb29rIGhhbmRpZ2UgZnVuY3RpZXMgb20gamUgZ3JhZmllayB2b2xnZW5zIGVlbiBkaXNjcmV0ZSB2YXJpYWJlbGUgb3AgdGUgZGVsZW4gaW4gbWVlcmRlcmUgZ3JhZmlla2VuLiBEaXQgd29yZHQgdmFhayBvb2sgInNtYWxsIG11bHRpcGxlcyIgZ2Vub2VtZCAoYmlqdm9vcmJlZWxkIGluIFBvd2VyIEJJKS4NCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAhW10od3d3LzcuJTIwZmFjZXRzLmpwZWcpICAgICAgICAgICAgICAgICAgICAgICAgIHwgTWV0IGBmYWNldF9ncmlkYCBiZXBhYWwgamUgd2Vsa2UgdmFyaWFiZWxlIG9wIGRlICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgeC1hcyBlbi9vZiB5LWFzIHVpdGdlc3BsaXRzdCB6YWwgd29yZGVuLlwgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQWxzIGplIDIgdmFyaWFiZWxlbiBsZXZlcnQgZGF0IGVlbiBtYXRyaXggb3AgbWV0ICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYWxsZSBjYXRlZ29yaWXDq24gdmFuIGRlIGVuZSB2YXJpYWJlbGUgb3AgZGUgeC1hcyAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGVuIGFsbGUgY2F0ZWdvcmllw6tuIHZhbiBkZSBhbmRlcmUgdmFyaWFiZWxlIG9wIGRlICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCB5LWFzLiBEaXQga2FuIGxlZ2UgZ3JhZmlla2VuIG9wbGV2ZXJlbiAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBNZXQgYGZhY2V0X3dyYXBgIHdvcmRlbiBhbGxlIGdyYWZpZWtlbiBtZXQgZ2VnZXZlbnN8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBnZW1hYWt0IGVuIHZlcnZvbGdlbnMgZ2V0b29uZCBpbiBoZXQgYWFudGFsICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCByaWplbi9rb2xvbW1lbiBkYXQgamUgYWFuZ2VlZnQuICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNClZvb3JiZWVsZGVuIHZhbiBgZmFjZXRfZ3JpZCgpYCB3b3JkZW4gaGllcm9uZGVyIHVpdGdld2Vya3QuIERhYXJiaWogaGViIGlrIGVlbg0KYmV0ZXIgbGVlc2JhcmUgY29kZSBnZWJydWlrdCBkYW4gb3AgaGV0IHNwaWVrYnJpZWZqZS4gSW4gcGxhYXRzIHZhbiBgLn52YXJpYWJlbGVgDQppcyBkaXQgdWl0Z2VzY2hyZXZlbiBhbHMgYGNvbHMgPSB2YXJzKHZhcmlhYmVsZSlgIGVuIGB2YXJpYWJlbGV+LmAgaXMgdWl0Z2VzY2hyZXZlbiBhbHMgYHJvd3MgPSB2YXJzKHZhcmlhYmVsZSlgLiAgDQpEaXQgaXMgdWl0ZXJhYXJkIG9vayB0ZSBjb21iaW5lcmVuIGFscyANCmBjb2xzID0gdmFycyhrb2xvbS12YXJpYWJlbGUpLCByb3dzID0gdmFycyhyaWotdmFyaWFiZWxlKWAuICANCg0KT20gdGUgYmVnaW5uZW4gZWVuIGVlbnZvdWRpZyB2b29yYmVlbGQgd2FhcmJpaiBkZSBncmFmaWVrZW4gYXV0b21hdGlzY2ggdWl0Z2VzcGxpdHMgd29yZGVuIGluIGVlbiBhYW50YWwga29sb21tZW4gdWl0Z2VzcGxpdHMgd29yZHQgb3AgYmFzaXMgdmFuIGRlIGZsIHZhcmlhYmVsZSAoPSBoZXQgYnJhbmRzdG9mIHR5cGUpLg0KDQoNCmBgYHtyIGZhY2V0X2dyaWRfMX0NCiMgYmFzaXMgZ3JhZmllaw0KdCA8LSBtcGcgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGxhYnModGl0bGUgPSAidWl0Z2VzcGxpdHMgcGVyIGJyYW5kc3RvZiB0eXBlIikNCg0KIyB1aXRzcGxpdHNlbiBpbiBncmFmaWVrZW4gb3AgYmFzaXMgdmFuIGZsDQp0ICsgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhmbCkpDQpgYGANCg0KT3AgZGV6ZWxmZGUgbWFuaWVyIGt1biBqZSBvb2sgdWl0c3BsaXRzZW4gcGVyIHJpai4gSW4gb25kZXJzdGFhbmQgdm9vcmJlZWxkIGdlYmV1cnQgZGl0IG9wIGJhc2lzIHZhbiB2YW4gaGV0IGJvdXdqYWFyLg0KDQpgYGB7ciBmYWNldF9ncmlkXzJ9DQojIGJhc2lzIGdyYWZpZWsNCnQgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBsYWJzKHRpdGxlID0gInVpdGdlc3BsaXRzdCBuYWFyIGJvdXdqYWFyIikNCg0KIyB1aXRzcGxpdHNlbiBpbiBncmFmaWVrZW4gb3AgYmFzaXMgdmFuIHllYXINCnQgKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKHllYXIpKQ0KYGBgDQoNClZlcnZvbGdlbnMgZWVuIGNvbWJpbmF0aWUgbWV0IHVpdHNwbGl0c2luZyBpbiBrb2xvbW1lbiBvcCBiYXNpcyB2YW4gYnJhbmRzdG9mIHR5cGUgZW4gcmlqZW4gaW8gYmFzaXMgdmFuIGJvdXdqYWFyLg0KDQpgYGB7ciBmYWNldF9ncmlkXzN9DQojIGJhc2lzIGdyYWZpZWsNCnQgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBsYWJzKHRpdGxlID0gInVpdGdlc3BsaXRzdCBuYWFyIGJyYW5kc3RvZiB0eXBlIMOpbiBib3V3amFhciIpDQoNCiMgdWl0c3BsaXRzZW4gaW4gZ3JhZmlla2VuIG9wIGJhc2lzIHZhbiB5ZWFyICsgYnJhbmRzdG9mIHR5cGUNCnQgKyBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGZsKSwgcm93cyA9IHZhcnMoeWVhcikpDQpgYGANCg0KSW4gZGV6ZSBncmFmaWVrIHZhbHQgb3AgZGF0IGRhdCBzb21taWdlIGNvbWJpbmF0aWVzIGxlZWcgYmxpanZlbi4gQmlqIGBmYWNldF9ncmlkYCB3b3JkdCBlbGtlIG1vZ2VsaWprZSBjb21iaW5hdGllIGdldG9vbmQuIE9vayBhbHMgZXIgZ2VlbiBnZWdldmVucyB6aWpuIG9tIGluIGRlemUgY29tYmluYXRpZSB2YW4gdmFyaWFiZWxlbi4gIA0KQWxzIGplIGRhdCB3aWwgdm9vcmtvbWVuLCBrdW4gamUgYGZhY2V0X3dyYXBgIGdlYnJ1aWtlbi4gRGV6ZWxkZSBncmFmaWVrIGFscyBoaWVyYm92ZW4gbWFhayBqZSBkYW4gYWxzIHZvbGd0OiAgDQoNCmBgYHtyIGZhY2V0X3dyYXB9DQojIGJhc2lzIGdyYWZpZWsNCnQgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBsYWJzKHRpdGxlID0gInVpdGdlc3BsaXRzdCBuYWFyIGJyYW5kc3RvZiB0eXBlIMOpbiBib3V3amFhciIpDQoNCiMgdWl0c3BsaXRzZW4gaW4gZ3JhZmlla2VuIG9wIGJhc2lzIHZhbiB5ZWFyICsgYnJhbmRzdG9mIHR5cGUNCnQgKyBmYWNldF93cmFwKGZhY2V0cyA9IHZhcnMoZmwsIHllYXIpKQ0KYGBgDQoNClZvb3JkZWVsIGlzIGRhdCBhbGxlZW4gY29tYmluYXRpZXMgbWV0IGdlZ2V2ZW5zIGdldG9vbmQgd29yZGVuLiBEZSB2YXJpYW50IG1ldCBgZmFjZXRfZ3JpZGAga2FuIHRvY2ggZGUgdm9vcmtldXIgaGViYmVuLCBvbWRhdCBjYXRlZ29yacOrbiBtYWtrZWxpamtlciB0ZSB2ZXJnZWxpamtlbiB6aWpuLiAgDQoNCldlbGxpY2h0IGlzIGhldCBvcGdldmFsbGVuIGRhdCBkZSBhc3NlbiB2YW4gYWxsZSBncmFmaWVrZW4gbWV0IGRlemVsZmRlIHJlZWtzIGFhbiB3YWFyZGVuIHdlcmtlbi4gRGl0IGt1biBqZSBhYW5wYXNzZW4gZG9vciBoZXQgYXJndW1lbnQgYHNjYWxlc2AgdGUgZ2VicnVpa2VuLiBKZSBoZWJ0IGRhYXJiaWogZGUga2V1emUgdWl0OiAgDQogIA0KLSAiZnJlZV94Ijogd2FhcmRlcyBvcCBkZSB4LWFzIGt1bm5lbiB2YXJpw6tyZW47ICANCi0gImZyZWVfeSI6IHdhYXJkZXMgb3AgZGUgeS1hcyBrdW5uZW4gdmFyacOrcmVuOyAgDQotICJmcmVlIjogd2FhcmRlcyBrdW5uZW4gdmFyacOrcmVuIG9wIHpvd2VsIGRlIHgtIGFscyBkZSB5LWFzLg0KDQpgYGB7ciBmYWNldF93cmFwX2ZyZWV9DQojIGJhc2lzIGdyYWZpZWsNCnQgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3kpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBsYWJzKHRpdGxlID0gInVpdGdlc3BsaXRzdCBuYWFyIGJyYW5kc3RvZiB0eXBlIMOpbiBib3V3amFhciIpDQoNCiMgdWl0c3BsaXRzZW4gaW4gZ3JhZmlla2VuIG9wIGJhc2lzIHZhbiB5ZWFyICsgYnJhbmRzdG9mIHR5cGUNCiMgJiAidnJpamUiIHgtIGVuIHktYXMNCnQgKyBmYWNldF93cmFwKGZhY2V0cyA9IHZhcnMoZmwsIHllYXIpLCBzY2FsZXMgPSAiZnJlZSIpDQpgYGANCg0KT29rIGhpZXIgaGViIGplIHRlIG1ha2VuIG1ldCBlZW4gYWZ3ZWdpbmcgdHVzc2VuIGR1aWRlbGlqa2UgaW5kaXZpZHVlbGUgZ3JhZmlla2VuIGVuIHZlcmdlbGlqa2JhYXJoZWlkLiAgDQoNCiMgVGl0ZWxzL2xhYmVscyBlbiBsZWdlbmRhDQpUaXRlbCwgbGFiZWxzIHZhbiBhc3NlbiwgbGVnZW5kYSBlbiBkZXJnZWxpamtlIGt1bm5lbiBlcmcgYmVsYW5ncmlqa2UgZWxlbWVudGVuIHppam4gdm9vciBkZSBsZWVzYmFhcmhlaWQgdmFuIGRlIGdyYWZpZWsvdmlzdWFsIGRpZSBqZSBtYWFrdC4gIA0KSGV0IGlzIGFhbiB0ZSByYWRlbiBvbSBoaWVyIGdvZWQgb3ZlciBuYSB0ZSBkZW5rZW4sIGVuIGdlZW4gb3ZlcmJvZGlnZSBpbmZvcm1hdGllIHRlIHRvbmVuLiBXYXQgZWVuIGdvZWRlIHRpdGVsLCBsZWdlbmRhLCBlbnpvdm9vcnQgaXMsIGlzIGdlZW4gb25kZXJkZWVsIHZhbiBkZXplIHVpdGxlZy4gTWFhciBoZXQgbG9vbnQgYmVzbGlzdCBkZSBtb2VpdGUgb20gZ29lZCBuYSB0ZSBkZW5rZW4gb3ZlciBkZXplIG9uZGVyZGVsZW4uDQoNCiMjIFRpdGVscyBlbiBsYWJlbHMNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwhW10od3d3LzguJTIwbGFiZWxzLmpwZyl7d2lkdGg9IjY2MiJ9ICAgICAgICAgICAgICB8IFRpdGVsIGVuIGxhYmVscyB6aWpuIG9wIG1lZXJkZXJlIG1hbmllcmVuIHRvZSB0ZSAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHZvZWdlbi4gRGUgZWVudm91ZGlnc3RlIG1hbmllciBpcyBvbSBkZSBgbGFic2AgdGUgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGdlYnJ1aWtlbiBpbiBwbGFhdHMgdmFuIGVlbiBmdW5jdGllIHZvb3IgZWxrIHZhbiBkZXwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IG9uZGVyZGVsZW4gZGllIGplIHRvZSB3aWwgdm9lZ2VuLiAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KSW4gaGV0IG9uZGVyc3RhYW5kZSB2b29yYmVlbGQgemlqbiBkZSB2ZWVsIGdlYnJ1aWt0ZSBvcHRpZXMgdmFuIGBsYWJzYCBtZWVnZW5vbWVuLg0KYGBge3IgbGFic30NCiMgYmFzaXMgZ3JhZmllaw0KdCA8LSBtcGcgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgICBnZW9tX3BvaW50KCkNCg0KIyB0b2V2b2VnZW4gdmFuIGxhYmVsIG9wdGllcw0KdCAgKw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlICAgID0gIkRpdCBpcyBkZSB0aXRlbCIsDQogICAgICAgIHN1YnRpdGxlID0gIkRpdCBpcyBkZSBzdWJ0aXRlbCIsDQogICAgICAgIGNhcHRpb24gID0gIkRpdCBpcyBoZXQgYmlqc2NocmlmdCAoY2FwdGlvbikiLCANCiAgICAgICAgeCAgICAgICAgPSAieC1hcyBsYWJlbCIsDQogICAgICAgIHkgICAgICAgID0gInktYXMgbGFiZWwiDQogICAgICAgICkNCmBgYA0KDQojIyBMZWdlbmRhICANCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwhW10od3d3LzguJTIwbGVnZW5kYS5qcGcpe3dpZHRoPSI2NTYifSAgICAgICAgICAgICB8IEVlbiBsZWdlbmRhIGt1biBqZSBvcCB2ZXJzY2hpbGxlbmRlIG1hbmllcmVuIHRvZS0gIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHZvZWdlbiBjLnEuIGJld2Vya2VuLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEVyIHppam4gYmVob29ybGlqayB3YXQgbW9nZWxpamtoZWRlbiB2b29yIGFhbnBhcy0gIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHNpbmdlbi4gQnVpdGVuIGRlIG1vZ2VsaWpraGVkZW4gb3AgaGV0IHNwaWVrLSAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGJyaWVmamUsIHZpbmQgamUgaGllcm9uZGVyIG9vayBub2cgZW5rZWxlIGFuZGVyZSAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IG1vZ2VsaWpraGVkZW4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KQmVkZW5rIGJpaiBsZWdlbmRhJ3MgYWx0aWpkIGRhdCBkZXplIGFmaGFua2VsaWprIHppam4gdmFuIGRlIGFlc3RoZXRpY3MgKG9mIGFlcykgZGllIGplIHRvZXZvZWd0LiBKZSBrcmlqZ3QgYmlqdm9vcmJlZWxkIGVlbiBsZWdlbmRhIG9wIGJhc2lzIHZhbiBgY29sb3JgLCBvcCBiYXNpcyB2YW4gYGZpbGxgLCBvcCBiYXNpcyB2YW4gYHNpemVgIGVuIG9wIGJhc2lzIHZhbiBgc2hhcGVgLiAgDQpCaWogZWVuIGNvbWJpbmF0aWUgdmFuIGRlemUgYWVzdGhldGljcywgenVsbGVuIGVyIG1lZXJkZXJlIGxlZ2VuZGEncyBhYW4gZGUgZ3JhZmllayB0b2VnZXZvZWdkIHdvcmRlbg0KDQojIyMgbGVnZW5kYSBwb3NpdGllDQpgYGB7ciBsZWdlbmRhX3Bvc2l0aWV9DQojIGJhc2lzIGdyYWZpZWsNCnQgPC0gbXBnICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBjdHksIHkgPSBod3ksIGNvbG9yID0gZmwpKSArDQogICAgZ2VvbV9wb2ludCgpDQoNCnQgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgICAgICAjIG1ldCBlZW4gc3RlZWt3b29yZCwgem9hbHMgb3AgaGV0IHNwaWVrYnJpZWZqZQ0KdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43LCAwLjIpLCAgICMgZXhhY3RlIHBvc2l0aWUNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKQ0KIyBsZWdlbmRhIG5pZXQgdG9uZW4NCnQgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgDQogICAgbGFicyhzdWJ0aXRsZSA9ICJnZWVuIGxlZ2VuZGEgZG9vcjogdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSIpDQp0ICsgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArDQogICAgbGFicyhzdWJ0aXRsZSA9ICJnZWVuIGxlZ2VuZGEgdm9vciAnY29sb3InIGRvb3I6IGd1aWRlcyhjb2xvciA9ICdub25lJykiKQ0KYGBgDQojIyMgbGVnZW5kYSB0aXRlbCAgDQpgYGB7ciBsZWdlbmRhX3RpdGVsfQ0KIyBiYXNpcyBncmFmaWVrDQp0IDwtIG1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gY3R5LCB5ID0gaHd5LCBjb2xvciA9IGZsKSkgKw0KICAgIGdlb21fcG9pbnQoKQ0KDQojIGxlZ2VuZGEgdGl0ZWwNCnQgKyB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogICAgbGFicyhzdWJ0aXRsZSA9ICJnZWVuIGxlZ2VuZGEgdGl0ZWwgZG9vcjogdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpIikNCnQgKyBsYWJzKGNvbG9yID0gIkxlZ2VuZGEgdGl0ZWwgdmlhIGxhYnMiKQ0KdCArIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiTGVnZW5kYSB0aXRhbCB2aWEgc2NhbGVfY29sb3IiLCBsYWJlbHMgPSBjKCJBIiwgIkIiLCAiQyIsICJEIiwgIkUiKSkNCmBgYA0KDQojIEtsZXVyKGVuKQ0KDQpIb2V3ZWwga2xldXJlbiBhbCBpbiBbNSBTY2hhbGVuIChzY2FsZSldKCNTY2hhbGVuIGVuIGtsZXVyZW4pIGJlaGFuZGVsZCB6aWpuLCBpcyBoZXQgZ29lZCBvbSBlciBlZW4gZXh0cmEgb25kZXJkZWVsIGFhbiB0ZSB3aWpkZW4uDQoNCktsZXVyZW4gemlqbiBpbW1lcnMgZXJnIGJlbGFuZ3JpamsgaW4gdmlzdWFscy4gWmlqIGJlcGFsZW4gdmFhayB2b29yIGVlbiBncm9vdCBkZWVsIGhvZSBlZW4gdmlzdWFsIGdlemllbi9nZcOvbnRlcnByZXRlZXJkIHdvcmR0IChlbiBob2UgYWFudHJla2tlbGlqayBkZSB2aXN1YWwgZ2V2b25kZW4gd29yZHQpLiBJbiBnZ3Bsb3QyIGt1biBqZSBlbGtlIGtsZXVyIGdlYnJ1aWtlbiBkaWUgamUga3VudCBiZWRlbmtlbi4gSGV0IGdhYXQgZWNodGVyIHZhYWsgb20gZGUgY29tYmluYXRpZXMgdmFuIGtsZXVyZW4gZW4gaG9lIHplIGluIGNvbWJpbmF0aWUgbWV0IGVsa2FhciBnZXppZW4gd29yZGVuLiAgDQpIb2V3ZWwgZGl0IG5pZXQgaW4gaGV0IHNwaWVrYnJpZWZqZSB0ZSB2aW5kZW4gaXMsIGlzIGhldCBlZW4gZHVzZGFuaWcgYmVsYW5ncmlqayBvbmRlcndlcnAgZGF0IGhldCBoaWVyIHRvY2ggYmVoYW5kZWxkIHdvcmR0LiAgDQpabyBrdW4gamUgY29uc2VxdWVudCBrbGV1cmVuIGdlYnJ1aWtlbiB2b29yIGJlcGFhbGRlIGdyb2VwZW4sIG1hYXIgb29rIGRlIGFhbmRhY2h0IG5hYXIgZWVuIGJlcGFhbGQgb25kZXJkZWVsIHRyZWtrZW4gZG9vciBrbGV1cmdlYnJ1aWsgKGJpanZvb3JiZWVsZCBkb29yIGRlIGdyb2VwZW4gZGllIG5pZXQgYmVsYW5ncmlqayB6aWpuIGdyaWpzIHRlIG1ha2VuKS4NCg0KRGUgaW5zcGlyYXRpZSB2b29yIGRpdCBvbmRlcmRlZWwga29tdCB2YW4gZGUgdm9sZ2VuZGUgYmxvZzogPGh0dHBzOi8vcGF1bHZhbmRlcmxha2VuLmNvbS8yMDIwLzAzLzIwL2hvdy10by1zdGFuZGFyZGl6ZS1ncm91cC1jb2xvcnMtaW4tZGF0YS12aXN1YWxpemF0aW9ucy1pbi1yLz4gIA0KDQpIZXQga2xldXJlbnBhbGxldCBkYXQgamUgZ2VicnVpa3QsIGlzIHVpdGVyYWFyZCBvb2sgZXJnIGJlbGFuZ3JpamsuIERhdCB6YWwgaGllciBlY2h0ZXIgbmlldCBzcGVjaWZpZWsgYmVoYW5kZWxkIHdvcmRlbi4gSmUga3VudCB1aXQgaGV0IG9uZGVyc3RhYW5kZSB3ZWwgZWVuIGlkZWUga3JpamdlbiBob2UgamUgem8nbiBrbGV1cmVucGFsbGV0IG1hYWt0LiBBbHMgamUgZWNodCB2aXN1YWxzIG9wIG1hYXQgd2lsIG1ha2VuLCBtZXQgZWVuIGNvbnNlcXVlbnRlIG9wbWFhaywgYmVraWprIGRhbiB2b29yYWwgb29rIGhldCB2b2xnZW5kZSBob29mZHN0dWsgJ1RoZW1hJ3MnLiANCg0KIyMgS2xldXJlbiBnZWxpamsgaG91ZGVuIHZvb3IgY2F0ZWdvcmllw6tuICANCkxhdGVuIHdlIGFscyBlZXJzdGUgbmFhciBlZW4gdmlzYXVsaXNhdGllIHZvb3IgZGUgbXBnIGRhdGFzZXQga2lqa2VuLiBIaWVyb25kZXIgd29yZHQgaGV0IHZlcmJydWlrIChtaWxlcyBwZXIgZ2Fsb24pIGluIGRlIHN0YWQgdmVyZ2VsZWtlbiBuYWFyIGtsYXNzZS4gRGFhcmJpaiB3b3JkZW4gZGUga2xldXJlbiB1aXQgaGV0IGB0aWR5cXVhbnRgIHBha2tldCBnZWJydWlrdCwgb21kYXQgZGV6ZSBlciBzdGFuZGFhcmQgYWFudHJla2tlbGlqa2VyIHVpdHppZW4gZGFuIGRlIGtsZXVyZW4gZGllIGdncGxvdCB0b29udC4NCmBgYHtyIGtsZXVyX2NvbnN0YW50XzF9DQojIGRhdGENCm1wZyAlPiUgDQogICAgZ3JvdXBfYnkoY2xhc3MpICU+JSANCiAgICBzdW1tYXJpc2UoYXZnX2N0eSA9IG1lYW4oY3R5KSkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgDQogICAgIyBncmFmaWVrIG1ldCBzdGFuZGFhcmRpbnN0ZWxsaW5nZW4NCiAgICBnZ3Bsb3QoYWVzKHggPSBjbGFzcywgeSA9IGF2Z19jdHksIGZpbGwgPSBjbGFzcykpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICB0aWR5cXVhbnQ6OnNjYWxlX2ZpbGxfdHEoKQ0KDQoNCmBgYA0KQWxzIGplIGluIGRlIGFuYWx5c2UgZGFuICJpbiB3aWwgem9vbWVuIiBvcCBiZXBhYWxkZSBrbGFzc2VuLCBiaWp2b29yYmVlbGQgbWV0IGVlbiB2ZXJicnVpayBpbiBkZSBzdGFkIChjdHkpID4gMTggbWlsZXMgcGVyIGdhbG9uLCBkYW4gZ2ViZXVydCBlciBoZXQgdm9sZ2VuZGU6DQpgYGB7ciBrbGV1cl9jb25zdGFudF8yfQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIGdyb3VwX2J5KGNsYXNzKSAlPiUgDQogICAgc3VtbWFyaXNlKGF2Z19jdHkgPSBtZWFuKGN0eSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIGZpbHRlcihhdmdfY3R5ID4gMTgpICU+JSANCiAgICANCiAgICAjIGdyYWZpZWsgbWV0IHN0YW5kYWFyZGluc3RlbGxpbmdlbg0KICAgIGdncGxvdChhZXMoeCA9IGNsYXNzLCB5ID0gYXZnX2N0eSwgZmlsbCA9IGNsYXNzKSkgKw0KICAgIGdlb21fY29sKCkgKw0KICAgIHRpZHlxdWFudDo6c2NhbGVfZmlsbF90cSgpDQpgYGANCkRlIGtsZXVyZW4gInZlcnNwcmluZ2VuIi4gRGl0IGlzIGVyZyB2ZXJ3YXJyZW5kIHZvb3IgZGUgbGV6ZXIgb2YgaW4gZWVuIHByZXNlbnRhdGllLiBKZSB6b3UgZGUga2xldXJlbiBncmFhZyBjb25zdGFudCBob3VkZW4uICANCiAgDQpFZW4gb3Bsb3NzaW5nIHZvb3IgZGl0IHByb2JsZWVtIGlzIG5hbWVuIGFhbiBoZXQga2xldXJlbnBhbGV0IHRvZSB0ZSBrZW5uZW4gZGllIG92ZXJlZW5rb21lbiBtZXQgZGUgY2F0ZWdvcmllw6tuLiBgdGlkeXF1YW50OjpzY2FsZV9maWxsX3RxKClgIGdlYnJ1aWt0IGRlIGtsZXVyZW4gdWl0IGRlIGZ1bmN0aWUgYHRpZHlxdWFudDo6cGFsZXR0ZV9saWdodCgpYC4gDQpBbHMgd2UgaGllciBlZW4gZWlnZW4ga2xldXJlbnBhbGV0IHZhbiBtYWtlbiBkaWUgYmVzdGFhdCB1aXQgaGV0IGFhbnRhbCBrbGV1cmVuIGRhdCBvdmVyZWVua29tdCBtZXQgaGV0IGFhbnRhbCBrbGFzc2VuIChjbGFzcykgaW4gZGUgZGF0YXNldCBlbiBkZXplIGRlIG5hbWVuIHZhbiBkZXplIGtsYXNzZW4gbWVlZ2V2ZW4sIGRhbiBrcmlqZ2VuIHdlIGhldCB2b2xnZW5kZSBrbGV1cmVucGFsZXQuDQpgYGB7ciBrbGV1cl9jb25zdGFudF9zdGFwMX0NCiMga2xldXJlbiBpbiB0aWR5cXVhbnQ6OnNjYWxlX2ZpbGxfdHEoKQ0KcHJpbnQodGlkeXF1YW50OjpwYWxldHRlX2xpZ2h0KCkpDQoNCiMgbWFhayBlciBlZW4gZWlnZW4gcGFsZXR0ZSB2YW4gKHdlIGhlYmJlbiBtYWFyIDcga2xldXJlbiBub2RpZyBlbiBkYWFyIG1vZXRlbiB3ZSBuYW1lbiBhYW4gZ2V2ZW4pDQpjdXN0b21fcGFsIDwtIA0KICAgIHRpZHlxdWFudDo6cGFsZXR0ZV9saWdodCgpWzE6bGVuZ3RoKHVuaXF1ZShtcGckY2xhc3MpKV0gJT4lIA0KICAgIHNldE5hbWVzKHNvcnQodW5pcXVlKG1wZyRjbGFzcykpKQ0KcHJpbnQoY3VzdG9tX3BhbCkNCg0KYGBgDQpEYWFybWVlIGt1biBqZSBkZSBrbGV1cmVuIGNvbnN0YW50IGhvdWRlbiBpbiB2ZXJzY2hpbGxlbmRlIGdyYWZpZWtlbiBtZXQgZW4gem9uZGVyIHNlbGVjdGllcyAvIGZpbHRlciBvcCBkZSBkYXRhLiBIZXQga2FuIHppam4gZGF0IGplIGVlbiBrbGVpbmUgYWFucGFzc2luZyBpbiBkZSBsZWdlbmRhIG5vZGlnIGhlYnQsIG9tZGF0IGplIG5pZXQgYWxsZSBtb2dlbGlqa2Uga2xldXJlbiB3aWwgdG9uZW4sIG1hYXIgYWxsZWVuIGRlIGtsZXVyZW4gZGllIGluIGRlIGdyYWZpZWsgdGUgemllbiB6aWpuLiAgDQpEaXQgZG9lIGplIGRvb3IgaGV0IGBicmVha3NgIGFyZ3VtZW50IHRlIGdlYnJ1aWtlbiBpbiBkZSBgc2NhbGVfKl9tYW51YWxgIGZ1bmN0aWUgdGUgZ2VicnVpa2VuLg0KDQpgYGB7ciBrbGV1cl9jb25zdGFudF9vcGxvc3Npbmd9DQojIG9vcnNwcm9ua2VsaWprZSBncmFmaWVrDQptcGcgJT4lIA0KICAgIGdyb3VwX2J5KGNsYXNzKSAlPiUgDQogICAgc3VtbWFyaXNlKGF2Z19jdHkgPSBtZWFuKGN0eSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIA0KICAgICMgZ3JhZmllayBtZXQgc3RhbmRhYXJkaW5zdGVsbGluZ2VuDQogICAgZ2dwbG90KGFlcyh4ID0gY2xhc3MsIHkgPSBhdmdfY3R5LCBmaWxsID0gY2xhc3MpKSArDQogICAgZ2VvbV9jb2woKSAgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWwpDQoNCiMgZ3JhZmllayB2b29yIGdlZmlsdGVyZGUgZGF0YQ0KIyBkYXRhDQptcGcgJT4lIA0KICAgIGdyb3VwX2J5KGNsYXNzKSAlPiUgDQogICAgc3VtbWFyaXNlKGF2Z19jdHkgPSBtZWFuKGN0eSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIGZpbHRlcihhdmdfY3R5ID4gMTgpICU+JSANCiAgICANCiAgICAjIGdyYWZpZWsgDQogICAgIyAoZ2VicnVpayBoZXQgZGF0YS1hcmd1bWVudCBvbSBpbiBoZXQgYnJlYWtzLWFyZ3VtZW50IGFhbiB0ZSBrdW5uZW4gcm9lcGVuKQ0KICAgIGdncGxvdChhZXMoeCA9IGNsYXNzLCB5ID0gYXZnX2N0eSwgZmlsbCA9IGNsYXNzKSwgZGF0YSA9IC4pICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsLCBicmVha3MgPSBkYXRhJGNsYXNzKQ0KYGBgDQoNCiMjIE5hZHJ1ayBsZWdnZW4gbWV0IGtsZXVyDQpJbiBlZW4gYW5hbHlzZSBrYW4gaGV0IGhlZWwgZ29lZCB6aWpuIG9tIGRlIGFhbmRhY2h0IG5hYXIgZWVuIGJlcGFhbGRlIGNhdGVnb3JpZSB0ZSB0cmVra2VuLiBEaXQga2FuIGhlZWwgZ29lZCBtZXQga2xldXJnZWJydWlrIGJpbm5lbiBlZW4gZ3JhZmllay4gQWFudnVsbGVuZCBvcCBoZXQgdm9vcmdhYW5kZSwga3VuIGplIGRhbiBiaWp2b29yYmVlbGQgZWVuIGtsZXVyZW5wYWxldCBtYWtlbiBkYXQgZWVuIGdyaWp6ZSBrbGV1ciBiZXZhdCB2b29yIGFsbGUgd2Fhcm5lbWluZ2VuIGRpZSBuaWV0IHZhbiBiZWxhbmcgemlqbiBlbiB3ZWwgZGUgZWlnZW4ga2xldXIgdGUgZ2VicnVpa2VuIHZvb3IgaGV0IG9uZGVyZGVlbCB3YWFyIGplIGRlIG5hZHJ1ayBvcCB3aWwgbGVnZ2VuLiAgDQpEYXQga2FuIGRvb3IgZWVyc3QgZWVuIGtsZXVyZW5wYWxldCB0ZSBtYWtlbiBtZXQgbmFtZW4sIHpvYWxzIHdlIGhpZXJib3ZlbiBoZWJiZW4gZ2VkYWFuLiBEYWFyaW4ga2VuIGplIGFhbiBlbGtlIG5hYW0gZGV6ZWxmZGUga2xldXIgdG9lLCBkaWUgZGUgYWFuZGFjaHQgTklFVCB0cmVrdCwgem9hbHMgbGljaHRncmlqcy4gRGFhcm5hIGtpZXMgamUgZGUgdmFyaWFiZWxlIHdhYXIgamUgbmFkcnVrIG9wIHdpbCBsZWdnZW4gZW4gd2lqcyBqZSBkaWUgdG9lIGFhbiBlZW4gci1vYmplY3QuIERpdCBvYmplY3Qga3VuIGplIHZlcnZvbGdlbnMgZ2VicnVpa2VuIG9tIGRlIGtsZXVyIHVpdCBoZXQga2xldXJlbnBhbGV0IG1ldCBuYW1lbiB1aXQgaGV0IHZvcmlnZSB2b29yYmVlbGQgdG9lIHRlIHdpanplbiBhYW4gZGV6ZWxmZGUgd2FhcmRlIGluIGhldCBrbGV1cmVucGFsZXQgZGF0IGplIGluIGRlIGdyYWZpZWsgZ2VicnVpa3QuIERpdCBsZXZlcnQgaGV0IGdld2Vuc3RlIHJlc3VsdGFhdCBvcC4gIA0KSW4gZGUgY29kZSB6aWUgamUgaGV0IHN0YXAgdm9vciBzdGFwIGdlYmV1cmVuLg0KDQpgYGAge3Iga2xldXJfbmFkcnVrfQ0KIyBiZWdpbiBtZXQgZWVuIGJhc2lzIG1ldCBhbGxlZW4gZ3JpanMgYWxzIGtsZXVyDQpuYWRydWtfcGFsX2Jhc2lzIDwtIA0KICAgIHJlcCgibGlnaHQgZ3JleSIsIGxlbmd0aCh1bmlxdWUobXBnJGNsYXNzKSkpJT4lIA0KICAgIHNldE5hbWVzKHNvcnQodW5pcXVlKG1wZyRjbGFzcykpKQ0KcHJpbnQobmFkcnVrX3BhbF9iYXNpcykNCg0KIyB3aWpzIGRlIHZhcmlhYmVsZSB0b2Ugd2FhciBkZSBuYWRydWsgb3AgbW9ldCBrb21lbiB0ZSBsaWdnZW4NCm5hZHJ1a192YXIgPC0gInN1YmNvbXBhY3QiDQpwcmludChuYWRydWtfdmFyKQ0KDQojIG1hYWsgZWVuIGtvcGllIHZhbiBoZXQgcGFsZXQsIHpvZGF0IGplIGRpdCB0ZWxrZW5zIG9wbmlldXcgYWxzIGJhc2lzIGt1bnQgZ2VicnVpa2VuDQpuYWRydWtfcGFsIDwtIG5hZHJ1a19wYWxfYmFzaXMNCg0KIyB3aWpzIGRlIGtsZXVyIHZvb3IgZGUgbmFkcnVrIHZhcmlhYmVsZSB1aXQgaGV0IGN1c3RvbV9wYWwgdG9lDQpuYWRydWtfcGFsW25hZHJ1a192YXJdIDwtIGN1c3RvbV9wYWxbbmFkcnVrX3Zhcl0NCnByaW50KG5hZHJ1a19wYWwpDQoNCiMgbWFhayBkZSBncmFmaWVrDQptcGcgJT4lIA0KICAgIGdyb3VwX2J5KGNsYXNzKSAlPiUgDQogICAgc3VtbWFyaXNlKGF2Z19jdHkgPSBtZWFuKGN0eSkpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIA0KICAgICMgZ3JhZmllayANCiAgICBnZ3Bsb3QoYWVzKHggPSBjbGFzcywgeSA9IGF2Z19jdHksIGZpbGwgPSBjbGFzcykpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBuYWRydWtfcGFsKSArIA0KICAgIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkplIGt1bnQgZXIgdWl0ZXJhYXJkIG9vayB2b29yIGtpZXplbiBlZW4gYW5kZXJlIGtsZXVyIHRlIGdlYnJ1aWtlbiBvbSBkZSBhYW5kYWNodCB0ZSB0cmVra2VuLiBEZSB3ZXJrd2lqemUgaXMgZGFuIHZlcmdlbGlqa2JhYXIuICANCg0KIyBUaGVtYSdzDQpUaGVtYSdzIHppam4gZWVuIHNldCBhYW4gaW5zdGVsbGluZ2VuIGRpZSBqZSB2aXN1YWwgaGVlbCBzbmVsIGVlbiBhbmRlciB1aXRlcmxpamsga3VubmVuIGdldmVuLiBNZWVzdGFsIGt1biBqZSBnb2VkIHVpdCBkZSB2b2V0ZW4gbWV0IDEgdmFuIGRlIGJlc2NoaWtiYXJlIHRoZW1hJ3MgaW4gYGdncGxvdDJgIG9mIGVlbiBhbmRlciBwYWNrYWdlLCB6b2FscyBgZ2d0aGVtZXNgLiBIaWVyYmlqIGdhYXQgaGV0IHZvb3JhbCBvbSB6YWtlbiBhbHMgaGV0IHVpdGVybGlqayB2YW4gaGV0IGNhbnZhcywgZGUgYXNzZW4sIGxlZ2VuZGEgZW4gZGVyZ2VsaWprZS4gIA0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IVtdKHd3dy8xMC4lMjB0aGVtYSdzLmpwZyl7d2lkdGg9IjcyMCJ9ICAgICAgICAgICAgfCBFciB6aWpuIHZlcnNjaGlsbGVuZGUgc3RhbmRhYXJkIHRoZW1hJ3MgaW4gZ2dwbG90Mi58DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBIZXQgZ2d0aGVtZXMgcGFja2FnZSBiaWVkdCBlZW4gdWl0Z2VicmVpZGVyIGFhbmJvZC58DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBIaWVyb25kZXIga3VuIGplIGRlIHZvb3JiZWVsZGVuIHZhbiBoZXQgc3BpZWstICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBicmllZmplIHppZW4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCiMjIFZvb3JiZWVsZGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgU3RhbmRhYXJkIHRoZW1hDQpgYGB7ciBzdGFuZGFyZF90aGVtZX0NCiMgbWFhayBkZSBiYXNpcyBncmFmaWVrDQp0IDwtIG1wZyAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gZmwsIGZpbGwgPSBmbCkpICsNCiAgICBnZW9tX2JhcigpICsNCiAgICBsYWJzKA0KICAgICAgICB5ID0gImFhbnRhbCIsDQogICAgICAgIHggPSAiYnJhbmRzdG9mIHR5cGUiLA0KICAgICAgICBmaWxsID0gImJyYW5kc3RvZiB0eXBlIg0KICAgICkNCg0KIyBwYXMgdGhlbWEgdG9lIGVuIHRvb24gZGl0IGluIGRlIHRpdGVsIGVuIHN1YnRpdGVsDQp0ICsgDQogICAgdGhlbWVfZ3JleSgpICsNCiAgICBsYWJzKA0KICAgICAgICB0aXRsZSA9ICJTdGFuZGFhcmQgdGhlbWEiLA0KICAgICAgICBzdWJ0aXRsZSA9ICJ0aGVtZV9ncmV5KCkiDQogICAgKQ0KYGBgDQoNCiMjIyB0aGVtZV9idygpDQpgYGB7ciBid190aGVtZX0NCiMgcGFzIHRoZW1hIHRvZSBlbiB0b29uIGRpdCBpbiBkZSB0aXRlbCBlbiBzdWJ0aXRlbA0KdCArIA0KICAgIHRoZW1lX2J3KCkgKw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gIlp3YXJ0LXdpdCB0aGVtYSIsDQogICAgICAgIHN1YnRpdGxlID0gInRoZW1lX2J3KCkiDQogICAgKQ0KYGBgDQoNCiMjIyB0aGVtZV9jbGFzc2ljKCkNCmBgYHtyIGNsYXNzaWNfdGhlbWV9DQojIHBhcyB0aGVtYSB0b2UgZW4gdG9vbiBkaXQgaW4gZGUgdGl0ZWwgZW4gc3VidGl0ZWwNCnQgKyANCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gIktsYXNzaWVrIHRoZW1hIiwNCiAgICAgICAgc3VidGl0bGUgPSAidGhlbWVfY2xhc3NpYygpIg0KICAgICkNCmBgYA0KDQojIyMgdGhlbWVfbWluaW1hbCgpDQpgYGB7ciBtaW5pbWFhbF90aGVtZX0NCiMgcGFzIHRoZW1hIHRvZSBlbiB0b29uIGRpdCBpbiBkZSB0aXRlbCBlbiBzdWJ0aXRlbA0KdCArIA0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicygNCiAgICAgICAgdGl0bGUgPSAiTWluaW1hbCB0aGVtYSIsDQogICAgICAgIHN1YnRpdGxlID0gInRoZW1lX21pbmltYWwoKSINCiAgICApDQpgYGANCg0KIyBWb29yYmVlbGRlbiB2YW4gdWl0Z2VicmVpZGUgdmlzdWFsaXNhdGllcw0KRGUgdmlzdWFscyBkaWUgaGllciB0ZSB6aWVuIHppam4sIG1vZXRlbiBkdWlkZWxpamsgbWFrZW4gZGF0IGplIGhlZWwgcHJvZmVzc2lvbmVsZSB2aXN1YWxzIGt1bnQgbWFrZW4gbWV0IGdncGxvdDIuIERhYXIgd2FhciBkZSB2b29yZ2FhbmRlIGhvb2Zkc3R1a2tlbiB3ZWxsaWNodCB3YXQgc2FhaWUgZ3JhZmlla2VuIGxpZXRlbiB6aWVuLCBpcyBkYXQgaGllciBhbmRlcnMuICANCiAgDQpEZSB2aXN1YWxzIGluIGRpdCBob29mZHN0dWsga3VubmVuIGVyIG5vZyBtb29pZXIgdWl0emllbi4gT21kYXQgcm1hcmtkb3duIHpha2VuIHNjaGFhbHQgZW4gb216ZXQgbmFhciBodG1sLCB3b3JkZW4gZXIgKGtsZWluZSkgYWFucGFzc2luZ2VuIGdlZGFhbi4gTm9ybWFhbCBnZXNwcm9rZW4gem91IGplIGRlIHZpc3VhbCBuYWFyIGJlc3RhbmQgb3BzbGFhbiBlbiBoZXQgZGFuIGRlbGVuLiBEYXQgaGVlZnQgYWxzIGJlbGFuZ3JpamsgbmFkZWVsIGRhdCBqZSBkZSBjb2RlIGRhbiBuaWV0IGt1bnQgemllbi4gRGV6ZSBzY2hvb25oZWlkc2ZvdXRqZXMgbmVlbSBpayBkdXMgdm9vciBsaWVmLg0KV2FhciBub2RpZyBoZWIgaWsgZGUgb3BnZXNsYWdlbiB2ZXJzaWUgdmFuIGRlIHZpc3VhbCBvcGdlbm9tZW4sIHpvZGF0IGplIGRlIGt3YWxpdGVpdCBrdW50IHZlcmdlbGlqa2VuLg0KDQojIyBTYW1lbnZhdHRpbmcgcHJvamVjdGVuIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfSANCg0KRGUgZWVyc3RlIHZpc3VhbCBpcyBlZW4gY29tYmluYXRpZSB2YW4gMyBncmFmaWVrZW4uIERlIGluc3BpcmF0aWUgaGViIGlrIGdldm9uZGVuIG9wOiBodHRwczovL3d3dy5zdG9yeXRlbGxpbmd3aXRoZGF0YS5jb20vYmxvZy8yMDE2LzEvMjYvY29ubmVjdGluZy10aGUtZG90cy4gSGV0IGlzIHRldmVucyBlZW4gbW9vaSB2b29yYmVlbGQgdmFuIGhvZSBnZWdldmVucyBhbmRlcnMgZ2V0b29uZCBrdW5uZW4gd29yZGVuIGRhbiBpbiBkZSBvb3JzcHJvbmtlbGlqa2UgZ3JhZmlla2VuIGRpZSBvb2sgb3AgZGV6ZSB3ZWJwYWdpbmEgZ2V0b29uZCB3b3JkZW4uICANCkhpZXJvbmRlciB6b3dlbCBkZSBvcGdlc2xhZ2VuIHZlcnNpZSAod2FhcnZhbiBqZSBkZSBjb2RlIG5pZXQga3VudCBiZWtpamtlbikgw6luIGRlIGdlZ2VuZXJlZXJkZSB2ZXJzaWUgdmFuIGRlemUgdmlzdWFsLiAgDQoNCiMjIyBvcGdlc2xhZ2VuIHZlcnNpZQ0KYGBge3IsIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygid3d3L3Zpc3VhbF8xLmpwZyIpDQpgYGANCg0KIyMjIGdlZ2VuZXJlZXJkDQoNCmBgYCAge3IgcHJvamVjdHNhbWVudmF0dGluZywgZmlnLmhlaWdodD11bml0KDUuMjUsImNtIil9DQojIDEuMSBwcm9qZWN0ZW4gb3ZlcnppY2h0IC0tLS0NCiMgKiBkYXRhIC0tLS0NCnByb2plY3RfcGVyX3N0YXR1c190YmwgPC0gDQogICAgdGliYmxlKA0KICAgICAgICBzdGF0dXMgPSBjKCJUb3RhYWwgUHJvamVjdGVuIiwgIlJlbGV2YW50IiwgIlZvb3JzdGVsbGVuIiwgDQogICAgICAgICAgICAgICAgICAgIkJpbm5lbmdlaGFhbGQiKSAlPiUgYXNfZmFjdG9yKCkgJT4lIGZjdF9yZXYoKSwNCiAgICAgICAgYWFudGFsID0gYygzNDAsIDI2NCwgOTIsIDM3KSwNCiAgICAgICAgdG90YWFsID0gYygzNDAsIDI2NCwgMjY0LCA5MiksDQogICAgICAgIGtsZXVyX3RvdGFhbCA9IGMoImdyZXkiLCAiZ3JleSIsICJvcmFuZ2UiLCAiZGFya3JlZCIpLA0KICAgICAgICB2ZXJzY2hpbCA9IGMoMCwgMCwgMTcyLCA1NSksDQogICAgICAgIGxhYmVsX3ZlcnNjaGlsID0gYyhOQV9jaGFyYWN0ZXJfLCBOQV9jaGFyYWN0ZXJfLCAiTmlldCBpbmdlZGllbmQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJWZXJsb3JlbiIpDQogICAgKQ0KDQojICogdmlzdWFsIC0tLS0NCnAxIDwtIHByb2plY3RfcGVyX3N0YXR1c190YmwgJT4lIA0KICAgIA0KICAgICMgYmFzaXMgZ3JhZmllaw0KICAgIGdncGxvdChhZXMoeCA9IHN0YXR1cywgeSA9IGFhbnRhbCkpICsNCiAgICAjIHRvdGFhbCBhbHMga29sb20gbWV0IGtsZXVyIHZhbiBrbGV1cl90b3RhYWwgZW4gZ2VlbiBvcHZ1bGxpbmcNCiAgICBnZW9tX2NvbChhZXMoeSA9IHRvdGFhbCwgY29sb3IgPSBrbGV1cl90b3RhYWwpLCANCiAgICAgICAgICAgICBmaWxsID0gTkEsIA0KICAgICAgICAgICAgIHdpZHRoID0gMC43LCANCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCANCiAgICAgICAgICAgICBzaXplID0gMSkgKw0KICAgIGdlb21fY29sKGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImdyZXkiLCB3aWR0aCA9IDAuNywgc2l6ZSA9IDEpICsNCiAgICAjIHdhYXJkZXMgdG9ldm9lZ2VuIG9wIGRlIGtvbG9tbWVuDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGFhbnRhbCksIGNvbG9yID0gIndoaXRlIiwgaGp1c3QgPSAxLjIsIHNpemUgPSA1KSArDQogICAgIyB0ZWtzdCB2b29yIGRlIHZlcnNjaGlsbGVuDQogICAgZ2VvbV90ZXh0KGFlcyh5ID0gdG90YWFsLCBsYWJlbCA9IGxhYmVsX3ZlcnNjaGlsLCBjb2xvciA9IGtsZXVyX3RvdGFhbCksIA0KICAgICAgICAgICAgICBoanVzdCA9IDEuMSwgc2l6ZSA9IDQpICsNCiAgICANCiAgICAjIG9wbWFhaw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gIlByb2plY3Qgc2FtZW52YXR0aW5nIiwNCiAgICAgICAgc3VidGl0bGUgPSAiSmFudWFyaSAtIERlY2VtYmVyIiwNCiAgICAgICAgeCA9ICIiLA0KICAgICAgICB5ID0gIiINCiAgICApICsNCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSA0LjUyNSwgeSA9IDE1MCwgDQogICAgICAgICAgICAgbGFiZWwgPSAiIyBwcm9qZWN0ZW4gcGVyIHN0YXAgaW4gaGV0IHByb2NlcyIsIA0KICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLCBzaXplID0gNSkgKw0KICAgICMgZ3JhZmllayBkcmFhaWVuDQogICAgY29vcmRfZmxpcCgpICsNCiAgICAjIHRoZW1hIHdpanppZ2VuDQogICAgdGhlbWVfbWluaW1hbCgpICsgDQogICAgIyBvcG1hYWsgdmFuIHRpdGVsLCBzdWJ0aXRlbCBlbiBncmlkDQogICAgdGhlbWUoDQogICAgICAgIHBsb3QudGl0bGUgICAgICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsDQogICAgICAgIHBsb3Quc3VidGl0bGUgICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwNCiAgICAgICAgYXhpcy50ZXh0LnggICAgICAgICA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgICAgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgICAgPSBlbGVtZW50X2JsYW5rKCkNCiAgICAgICAgKSArDQogICAgIyByYW5ka2xldXIgb3AgYmFzaXMgdmFuIHN0YXR1cy12YXJpYWJlbGUgKHZvb3IgdG90YWFsKQ0KICAgIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkNCg0KIyAxLjIgd2Fhcm9tIG5pZXQgaW5nZWRpZW5kIC0tLS0NCiMgKiBkYXRhIC0tLS0NCnJlZGVuX25pZXRfaW5nZWRpZW5kX3RibCA8LSANCiAgICB0aWJibGUoDQogICAgcmVkZW4gPSBjKCJLb25kZW4gZGVhZGxpbmUgbmlldCBoYWxlbiIsICJPbnZvbGRvZW5kZSBjYXBhY2l0ZWl0IiwgDQogICAgICAgICAgICAgICJPbnZvbGRvZW5kZSBtb2dlbGlqa2hlZGVuIiwgIlRlIGtsZWluIiwgDQogICAgICAgICAgICAgICJPbm1vZ2VsaWprIGJpbm5lbiB0ZSBoYWxlbiIsICJCdWl0ZW4gZGUgZG9lbG1hcmt0IiwgIA0KICAgICAgICAgICAgICAiU2xlY2h0ZSByZWxhdGllIikgJT4lIGFzX2ZhY3RvcigpICU+JSBmY3RfcmV2KCksDQogICAgYWFudGFsID0gYyg1NiwgNDIsIDI0LCAyMiwgMTIsIDgsIDgpDQogICAgKSAlPiUgDQogICAgbXV0YXRlKA0KICAgICAgICBwY3QgPSAoYWFudGFsIC8gc3VtKGFhbnRhbCkpICU+JSByb3VuZChkaWdpdHMgPSAyKSAlPiUgc2NhbGVzOjpwZXJjZW50KCkNCiAgICApDQoNCiMgKiB2aXN1YWwgLS0tLQ0KcDIgPC0gcmVkZW5fbmlldF9pbmdlZGllbmRfdGJsICU+JSANCiAgICANCiAgICAjIGJhc2lzIGdyYWZpZWsNCiAgICBnZ3Bsb3QoYWVzKHggPSByZWRlbiwgeSA9IGFhbnRhbCkpICsNCiAgICBnZW9tX2NvbChmaWxsID0gIm9yYW5nZSIsIHdpZHRoID0gMC43LCBzaXplID0gMSkgKw0KICAgICMgd2FhcmRlcyB0b2V2b2VnZW4gb3AgZGUga29sb21tZW4NCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gYWFudGFsKSwgY29sb3IgPSAid2hpdGUiLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDMpICsNCiAgICAjIHBlcmNlbnRhZ2VzIHRvZXZvZWdlbiBib3ZlbiBkZSBrb2xvbW1lbg0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwY3QpLCBjb2xvciA9ICJncmV5IiwgaGp1c3QgPSAtMC4yNSwgc2l6ZSA9IDMpICsNCiAgICANCiAgICAjIG9wbWFhaw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gYnF1b3RlKCJXYWFyb20gZGllbmVuIHdlIHZvb3JzdGVsbGVuIn5ib2xkKCJuaWV0IGluIikpLA0KICAgICAgICB4ID0gIiIsDQogICAgICAgIHkgPSAiIg0KICAgICkgKw0KICAgICMgcnVpbXRlIG1ha2VuIHZvb3IgZGUgYW5ub3RhdGllICdhYW50YWwgfCAlIHRvdCcNCiAgICBzY2FsZV94X2Rpc2NyZXRlKA0KICAgICAgICBsaW1pdHMgPSBjKHJlZGVuX25pZXRfaW5nZWRpZW5kX3RibCAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgIGFycmFuZ2UoYWFudGFsKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgIHB1bGwocmVkZW4pICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKCksICIiKQ0KICAgICAgICApICsNCiAgICAjIHRla3N0IGJvdmVuIGRlIGdyYWZpZWsNCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSA3Ljc1LCB5ID0gNTAsIGxhYmVsID0gImFhbnRhbCIsIA0KICAgICAgICAgICAgIGNvbG9yID0gIm9yYW5nZSIsIHNpemUgPSAzKSArDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gNy43NSwgeSA9IDYxLCBsYWJlbCA9ICJ8ICUgdG90IiwgDQogICAgICAgICAgICAgY29sb3IgPSAiZ3JleSIsIHNpemUgPSAzKSArDQogICAgIyBncmFmaWVrIGRyYWFpZW4NCiAgICBjb29yZF9mbGlwKCkgKw0KICAgICMgdGhlbWEgd2lqemlnZW4NCiAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICAjIG9wbWFhayB2YW4gdGl0ZWwsIHN1YnRpdGVsIGVuIGdyaWQNCiAgICB0aGVtZSgNCiAgICAgICAgcGxvdC50aXRsZSAgICAgICAgICA9IGVsZW1lbnRfdGV4dChzaXplPTE1KSwNCiAgICAgICAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSAgICAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLA0KICAgICAgICBheGlzLnRleHQueCAgICAgICAgID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciAgICA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciAgICA9IGVsZW1lbnRfYmxhbmsoKQ0KICAgICkNCg0KIyAxLjMgd2Fhcm9tIG5pZXQgZ2V3b25uZW4gLS0tLQ0KIyAqIGRhdGEgLS0tLQ0KdmVybG9yZW5fYmllZGluZ2VuX3RibCA8LSANCiAgICB0aWJibGUoDQogICAgICAgIHJlZGVuID0gYygiTGV2ZXJ0aWpkIiwgIlJlaWt3aWpkdGUgd2VyayIsICJQcmlqcyIsICJTbGVjaHRlIHJlbGF0aWUiKSAlPiUgDQogICAgICAgICAgICBhc19mYWN0b3IoKSAlPiUgZmN0X3JldigpLA0KICAgICAgICBhYW50YWwgPSBjKDE5LCAxOCwgMTEsIDcpDQogICAgKSAlPiUgDQogICAgbXV0YXRlKA0KICAgICAgICBwY3QgPSAoYWFudGFsIC8gc3VtKGFhbnRhbCkpICU+JSByb3VuZChkaWdpdHMgPSAyKSAlPiUgc2NhbGVzOjpwZXJjZW50KCkNCiAgICApDQoNCiMgLSB2aXN1YWwgLS0tLQ0KcDMgPC0gdmVybG9yZW5fYmllZGluZ2VuX3RibCAlPiUgDQogICAgDQogICAgIyBiYXNpcyBncmFmaWVrDQogICAgZ2dwbG90KGFlcyh4ID0gcmVkZW4sIHkgPSBhYW50YWwpKSArDQogICAgZ2VvbV9jb2woZmlsbCA9ICJkYXJrcmVkIiwgd2lkdGggPSAwLjcsIHNpemUgPSAxKSArDQogICAgIyB3YWFyZGVzIHRvZXZvZWdlbiBvcCBkZSBrb2xvbW1lbg0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBhYW50YWwpLCBjb2xvciA9ICJ3aGl0ZSIsIGhqdXN0ID0gMS41LCBzaXplID0gMykgKw0KICAgICMgcGVyY2VudGFnZXMgdG9ldm9lZ2VuIGJvdmVuIGRlIGtvbG9tbWVuDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBjdCksIGNvbG9yID0gImdyZXkiLCBoanVzdCA9IC0wLjI1LCBzaXplID0gMykgKw0KICAgIA0KICAgICMgb3BtYWFrDQogICAgbGFicygNCiAgICAgICAgdGl0bGUgPSBicXVvdGUoIldhYXJvbSB3ZSBiaWVkaW5nZW4ifmJvbGQoInZlcmxpZXplbiIpKSwNCiAgICAgICAgeCA9ICIiLA0KICAgICAgICB5ID0gIiINCiAgICApICsNCiAgICAjIHJ1aW10ZSBtYWtlbiB2b29yIGRlIGFubm90YXRpZSAnYWFudGFsIHwgJSB0b3QnDQogICAgc2NhbGVfeF9kaXNjcmV0ZSgNCiAgICAgICAgbGltaXRzID0gYyh2ZXJsb3Jlbl9iaWVkaW5nZW5fdGJsICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgYXJyYW5nZShhYW50YWwpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgcHVsbChyZWRlbikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoKSwgIiIpDQogICAgKSArDQogICAgIyB0ZWtzdCBib3ZlbiBkZSBncmFmaWVrDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gNSwgeSA9IDE2LjUsIGxhYmVsID0gImFhbnRhbCIsIA0KICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBzaXplID0gMykgKw0KICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDUsIHkgPSAyMSwgbGFiZWwgPSAifCAlIHRvdCIsIA0KICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLCBzaXplID0gMykgKw0KICAgICMgZ3JhZmllayBkcmFhaWVuDQogICAgY29vcmRfZmxpcCgpICsNCiAgICAjIHRoZW1hIHdpanppZ2VuDQogICAgdGhlbWVfbWluaW1hbCgpICsgDQogICAgIyBvcG1hYWsgdmFuIHRpdGVsLCBzdWJ0aXRlbCBlbiBncmlkDQogICAgdGhlbWUoDQogICAgICAgIHBsb3QudGl0bGUgICAgICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZT0xNSksDQogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsDQogICAgICAgIHBsb3Quc3VidGl0bGUgICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwNCiAgICAgICAgYXhpcy50ZXh0LnggICAgICAgICA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgICAgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgICAgPSBlbGVtZW50X2JsYW5rKCkNCiAgICApDQoNCnAxIC8gKHAyICsgcDMpICsgDQogICAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IHVuaXQoYyg2LCAzKSwgYygnY20nLCAnY20nKSkpDQoNCmBgYA0KDQojIyBMb2xsaXBvcC1kaWFncmFtDQpEZSB2b2xnZW5kZSB2aXN1YWwgaXMgZ2ViYXNlZXJkIG9wIGZpY3RpZXZlIGRhdGEuICANCkJpanpvbmRlcmhlZGVuOiAgDQogIA0KKiBHZXNvcnRlZXJkIG9wIG9temV0LCBtYWFyICdPdmVyaWdlJyBhbHMgbGFhdHN0ZTsgIA0KKiBMb2dvJ3MgaW4gcGxhYXRzIHZhbiBlZW4gd2FhcmRlIG9wIGRlIGFzOyAgDQoqIEtsZXVyIHZhbiBkZSBsaWpuIGVuIGRlIHRla3N0IHppam4gb3Zlcmdlbm9tZW4gdWl0IGRlIGxvZ28nczsgIA0KKiBEZSBkaWt0ZSB2YW4gZGUgbGlqbiB2YW4gZGUgbG9sbGlwb3AgaXMgYWZoYW5rZWxpamsgdmFuIGRlIG9temV0IChkaXQgem91IGplIG9vayBvcCBkZSB0ZWtzdHdhYXJkZSBhYW4gaGV0IGVpbmQgdmFuIGRlIGxvbGxpcG9wIHRvZSBrdW5uZW4gcGFzc2VuKS4gIA0KDQpgYGB7ciBsb2xsaXBvcCwgZmlnLmhlaWdodD11bml0KDQsImNtIil9DQoNCiMgZGF0YQ0KIyBMaWpzdCBtZXQgTmVkZXJsYW5kc2UgZmlldHNtZXJrZW4gbWFrZW4NCmZpZXRzbWVya2VuIDwtIGMoIkJhdGF2dXMiLCAiR2F6ZWxsZSIsICJHaWFudCIsICJLb2dhIiwgIlNwYXJ0YSIsICJPdmVyaWdlIikNCiMgZ2VnZXZlbnMgb3ZlciBrbGV1cmVuIGVuIGxvZ28ncw0KbWVya2dlZ2V2ZW5zIDwtIA0KICAgIHRpYmJsZSgNCiAgICAgICAgZmlldHNtZXJrID0gZmlldHNtZXJrZW4sDQogICAgICAgIGtsZXVyICAgICA9IGMoIiNjNDFkMzUiLCAiIzJhMzI4ZiIsICIjMDA2ZGFmIiwgIiMwZTdkYzciLCAiIzQ1YWMzNiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIiM4ZTI0YmIiKSAlPiUgc2V0TmFtZXMoZmlldHNtZXJrZW4pLA0KICAgICAgICBsb2dvICAgICAgPSBjKCJsb2dvX2JhdGF2dXMuanBnIiwgImxvZ29fZ2F6ZWxsZS5qcGciLCAibG9nb19naWFudC5wbmciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2dvX2tvZ2EuanBnIiwgImxvZ29fc3BhcnRhLnBuZyIsICJsb2dvX292ZXJpZ2UuanBnIikNCiAgICApDQoNCiMgRGF0YXNldCBtYWtlbiBtZXQgZmljdGlldmUgb216ZXRjaWpmZXJzDQpzZXQuc2VlZCgxMjMpDQp2ZXJrb29wZGF0YSA8LSANCiAgICB0aWJibGUoZmlldHNtZXJrID0gZmlldHNtZXJrZW4sIA0KICAgICAgICAgICBvbXpldCAgICAgPSByb3VuZChydW5pZihsZW5ndGgoZmlldHNtZXJrZW4pLCBtaW4gPSA1MDAsIG1heCA9IDUwMDApKSkgJT4lIA0KICAgIGFycmFuZ2UoZGVzYyhvbXpldCkpDQoNCiMgTmFiZXdlcmtlbiBkYXRhIHZvb3IganVpc3RlIHZvbGdvcmRlIGluIGdyYWZpZWsgZS5kLg0KZGF0YSA8LSB2ZXJrb29wZGF0YSAlPiUgDQogICAgbGVmdF9qb2luKG1lcmtnZWdldmVucykgJT4lIA0KICAgIG11dGF0ZSgNCiAgICAgICAgb216ZXQgPSBpZmVsc2UoZmlldHNtZXJrID09ICJPdmVyaWdlIiwgb216ZXQgKiA0LCBvbXpldCksDQogICAgICAgIGZpZXRzbWVyayA9IGZpZXRzbWVyayAlPiUgYXNfZmFjdG9yKCkgJT4lIGZjdF9yZXYoKSAlPiUgcmVsZXZlbCgiT3ZlcmlnZSIpDQogICAgICAgICkgJT4lIA0KICAgIG11dGF0ZShsZXZlbCA9IGFzLm51bWVyaWMoZmlldHNtZXJrKSkgJT4lIA0KICAgIG11dGF0ZShsb2dvID0gc3RyX2dsdWUoInd3dy97bG9nb30iKSwNCiAgICAgICAgICAgb216ZXRfbGFiZWwgPSBzY2FsZXM6Om51bWJlcihvbXpldCkpDQoNCiMgTWFhayBsb2xsaXBvcCBncmFmaWVrDQpnIDwtDQogICAgIyB0b2V3aWp6aW5nIHZhbiBkYXRhDQogICAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gZmlldHNtZXJrLCB5ID0gb216ZXQpKSArDQogICAgIyBsaWpuZW4gdm9vciBkZSBvbXpldCB2YW4gMCB0b3Qgb216ZXQgaG9vZ3RlDQogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gZmlldHNtZXJrLCB4ZW5kID0gZmlldHNtZXJrLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSAwLCB5ZW5kID0gb216ZXQsIGNvbG9yID0ga2xldXIpLCBzaXplID0gZGF0YSRvbXpldCAvIDI1MDApICsNCiAgICAjIG9temV0IGNpamZlciBhYW4gaGV0IGVpbmQgdmFuIGRlIGxpam4gdG9ldm9lZ2VuDQogICAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBvbXpldF9sYWJlbCwgY29sb3IgPSBrbGV1cikpICsNCiAgICANCiAgICAjIG9wbWFhaw0KICAgICMgdGl0ZWxzIGVuIGFzLWxhYmVscw0KICAgIGxhYnMoDQogICAgICAgIHRpdGxlICAgPSAiRmlldHNtZXJrZW4gZW4gb216ZXQiLA0KICAgICAgICB4ICAgICAgID0gIiIsDQogICAgICAgIHkgICAgICAgPSAiT216ZXQgKHgxMDAwIOKCrCkiLA0KICAgICAgICBjYXB0aW9uID0gImZpY3RpZXZlIGRhdGEiDQogICAgKSArDQogICAgIyB0aGVtYSB0b2VwYXNzbg0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgIyBrbGV1ciB0b2V3aWp6ZW4gb3AgYmFzaXMgdmFuIGZpZXRzbWVyaw0KICAgIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKw0KICAgICMgeC1hcyB2b2xsZWRpZyBibGFuY28gbWFrZW4NCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IE5VTEwsIGJyZWFrcyA9IE5VTEwpICsNCiAgICAjIHktYXMgdm9sbGVkaWcgYmxhbmNvIG1ha2VuDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IE5VTEwsIGJyZWFrcyA9IE5VTEwpICsNCiAgICAjIGdyYWZpZWsgZHJhYWllbiwgem9kYXQgZGUgbWVya2VuIG9wIGRlICd5LWFzJyBrb21lbg0KICAgIGNvb3JkX2ZsaXAoKSAgKw0KICAgICMgb3BtYWFrIHZhbiB0aXRlbCwgc3VidGl0ZWwgZW4gZ3JpZA0KICAgIHRoZW1lKA0KICAgICAgICBwbG90LnRpdGxlICAgICAgICAgID0gZWxlbWVudF90ZXh0KHNpemU9MTUpLA0KICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yICAgID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yICAgID0gZWxlbWVudF9ibGFuaygpDQogICAgKQ0KDQojIGxvZ28gYWxzIHgtYXMgJ3RpY2snDQpsaWJyYXJ5KGdnaW1hZ2UpDQpnICsNCiAgICBnZW9tX2ltYWdlKGFlcyhpbWFnZSA9IGxvZ28sIHkgPSAtMzAwKSwgc2l6ZSA9IDAuMDk1LCBieSA9ICJ3aWR0aCIpIA0KDQpgYGANCg==