Analiza danych projekt zespołowy

W ramach zaliczenia przedmiotu Analiza danych na kierunku Analityka Gospodarcza II zostaliśmy zobligowani do wykonania specjalnego projektu wybranego przez prowadzącego.
Wylosowanym zestawem danych okazał się zbiór dotyczący supermarketów.

Niniejszy projekt składa się z sześciu części tematycznych odpowiednio wyróżnionych nagłówkami stylu H2.

Tu będzie spis treści

Wprowadzenie

W dynamicznym świecie handlu detalicznego, rozwój supermarketów w najbardziej zaludnionych miastach osiąga coraz to nowe szczyty konkurencyjności. W kontekście tego zjawiska, kluczowym elementem staje się dogłębna analiza danych sprzedażowych różnych supermarketów. Niniejszy projekt ma na celu zbadanie i zrozumienie trendów sprzedażowych w trzech oddziałach jedenej firmy będącej supermarketem, reprezentujących różnorodne lokalizacje i demografie klientów.

Dane, na których opiera się ta analiza, zostały zebrane w ciągu trzech miesięcy, od stycznia do marca 2019 roku, i zawierają szczegółowe informacje na temat każdej transakcji. Fundamentami wykonanej w projekcie analizy są takie zmienne jak: numer identyfikacyjny faktury (Invoice ID), oddział, w którym dokonano zakupu (Branch), lokalizacja supermarketu (City), typ klienta (Customer type), płeć (Gender), linia produktów (Product line), cena jednostkowa (Unit price), ilość zakupionych produktów (Quantity), opłata podatkowa (Tax), całkowita cena zakupu (Total), data (Date) i czas transakcji (Time), metoda płatności (Payment), koszt sprzedanych towarów (COGS), procentowa marża brutto (Gross margin percentage), dochód brutto (Gross income) oraz ocena klienta (Rating).

W kontekście rosnącej konkurencji na rynku, zrozumienie tych danych może okazać się kluczowe dla formułowania strategii biznesowych i w nadążaniu za dynamicznymi zmianami. Analizując, jak różne czynniki, takie jak lokalizacja, typ klienta czy wybór produktów, wpływają na wyniki sprzedaży, możemy lepiej zrozumieć potrzeby i preferencje konsumentów. To z kolei może prowadzić do bardziej celowych i skutecznych decyzji biznesowych, co jest niezwykle istotne w świecie, gdzie klient i jego doświadczenia stanowią o sukcesie firmy.

Celem tego projektu jest nie tylko zrozumienie obecnych trendów, ale także identyfikacja potencjalnych obszarów do rozwoju i innowacji w branży supermarketów. Przez skupienie się na danych historycznych, projekt ten ma na celu dostarczenie wartościowych wskazówek dla przyszłych decyzji strategicznych i operacyjnych w branży supermarketów.

Data Cleansing & Data Wrangling - czyli czyścimy i ujednolicamy dane!

Pierwszy etap merytorycznej pracy z danymi, w żargonie analityków danych określany jako “Data Cleansing” oraz “Data Wrangling”, to nic innego jak czyszczenie i ujednolicanie danych na potrzeby niniejszego projektu.

Rozpakowanie danych w programie RStudio i wyświetlenie ich zawartości

Zanim jednak wdrożone zostaną takie prace, należy wgrać dane do programu R i zerknąć na tablicę. W tym miejscu zweryfikowaliśmy typy wszystkich zmiennych.

dane <- read.csv("Biedronki.csv")

Na potrzeby prac należy zainstalować niezbędne biblioteki, które towarzyszyć będą na etapie czyszcznia danych.

install.packages("tidyverse")
install.packages("dlookr")
install.packages("editrules")
install.packages("VIM")
install.packages("deducorrect")
install.packages("ISLR")

library(tidyverse)
library(dlookr)
library(editrules)
library(VIM)
library(deducorrect)
library(ISLR) 

Przechodząc do czyszczenia,cały proces rozpoczeliśmy od sprawdzenia, ile przypadków danych jest kompletnych, czyli czy występując NA. Jednak braki danych to nie wszystko. Postanowiliśmy sprawdzić, czy w zbiorze danych znajdują się jakieś wartości specjalne. Do tego posłużyła specjalna funkcja. Na wypadek, gdyby pojawiły się jakieś wartości specjalne, zdecydowaliśmy się, że przypiszemy im wartości NA. W tym celu wykorzystaliśmy pętlę. Tak przeprowadzone operacje pozwalają wydrukować statystyki opisowe dla zmiennych.

sum(complete.cases(dane))

is.special <- function(x){
  if (is.numeric(x)) !is.finite(x) else is.na(x)}
sapply(dane, is.special)

for (n in colnames(dane)){
  is.na(dane[[n]]) <- is.special(dane[[n]])
}
summary(dane)

Powyższy kod pozwala stwierdzić, że w zbiorze danych nie ma NA. Jednak postanowiliśmy zagłębić się jeszcze w słuszność tego twierdzenia. Ostatecznym potwierdzeniem będzie dla nas wizualizacja obecności ewentualnych NA. W tym celu pobraliśmy specjalny pakiet “naniar”.

install.packages("naniar")
library(naniar)
vis_miss(dane)

Co do braków danych mamy już absolutną pewność. Dzięki temu możemy pójść dalej i spojrzeć na to jak ukształtowują się zmienne jakościowe (liczbowe). W tym celu wykorzystamy popularną metodę wizualizacji - wykresy pudełkowe.Taki zabieg na danych pozwoli na wyselekcjonowanie tych zmiennych ciągłych, które zawierają wartości odstające.

boxplot(dane$Unit.price)
boxplot(dane$Tax.5.) #zawiera wartości odstające
boxplot(dane$Total) #zawiera wartości odstające 
boxplot(dane$cogs) #zawiera wartości odstające
boxplot(dane$gross.margin.percentage) 
boxplot(dane$gross.income) #zawiera wartości odstające
boxplot(dane$Rating)

Wizualizacja danych - spójrzmy jakie mamy informacje!

Po tym jak przeprowadziliśmy procedurę czyszczenia i ujednolicania naszych wejściowych danych, możemy skupić się na ich konkretnej wizualizacji. Postanowiliśmy za pomocą wykresu kolumnowego przedstawić stosunek produktów do ceny z podatkiem. Zwizuowaliśmy również stosunek miasta do ceny zawierającej podatek oraz stosunek miasta do satysfakcji klienta. Jednak zacznijmy od początku.

install.packages('ggplot2')
library('ggplot2')
ggplot(dane, aes(x = dane$Product.line, y = dane$Total)) +
  geom_col(fill="lightpink2") + 
  xlab("Kryteria produktów") +
  ylab("Cena wraz z podatkiem [$]")

Intepretacja: Analizując kryteria produktów oraz ceny, które mają już wliczony podatek, to w sklepie dyskontowym Biedronka najdroższe produkty względem cenowym znajdują się w jedzeniach i napojach, co uważamy, że jest zjawiskiem negatywnym, ponieważ podatki stanowią większość ceny przez podatek cukrowy.

Skupimy się teraz na stosunku miasta do ceny z obowiązującym podatkiem.

ggplot(dane, aes(x = dane$City, y = dane$Total)) +
  geom_col(fill="green4")+
  xlab("Miasto") +
  ylab("Cena wraz z podatkiem [$]")

Interpretacja: Biorąc pod uwagę miasta oraz cena z wliczony z podatkiem, można wysunąć pewne wnioski, a mianowicie miasto Naypyitaw charakteryzuje się najdroższymi produktami, z kolei dwa pozostałe miasta pod nazwami: Mandalay i Yangon są na tym samym poziomie.

Następnie postanowiliśmy zobrazować relację ceny do satysfakcji klienta.

ggplot(dane, aes(x = dane$Total, y = dane$Rating)) +
  geom_col(size=1, fill="blue4")+
  xlab("Cena z podatkiem") +
  ylab("Satysfakcja klienta")

Interpretacja: Biorąc pod uwagę stosunek satysfakcji klienta do samej ceny z wliczonym podatkiem, można wysunąc następujące wnioski. Największą liczbą ocen charakteryzują się ceny w okolicach 500$, zaś najmniejszą liczbą ocen ceny równe 1000 dol, bądź wyższe.

Najwyższy czas zatem zbadać kształtowanie się ilości. Zaczęliśmy od ilości ocen satysfakcji klienta.

ggplot(dane, aes(dane$Rating)) +
  geom_bar(fill="purple4")+
  xlab("Satysfakcja klienta") +
  ylab("Ilość ocen")

Interpretacja: Analizując poniższy wykres można stwierdzić, iż największą ilością ocen satysfakcji klienta zyskała ocena 6. Jest to powyżej połowy, co jest pozytywnym wynikiem dla sklepu dyskontowego.

Warto spojrzeć teraz na liczbę wykonanych zakupów w poszczególnych miastach.

ggplot(dane, aes(dane$City)) +
  geom_bar(fill="lightgreen")+
  xlab("Miasto") +
  ylab("Ilość faktur")

Interpretacja: Powyższy wykres przedstawia ilość wykonanych zakupów w oparciu o trzy miasta. Największą liczbą dokonanych zakupów charakteryzuje się miasto Yangon, zaś najmniejszą Naypyitaw.

Zobrazujemy teraz liczbę płatności ze względu na sposób ich dokonania.

ggplot(dane, aes(dane$Payment)) +
  geom_bar(fill="lightpink")+
  xlab("Rodzaj płatności") +
  ylab("Ilość płatności")

Intepretacja: Wykres przedstawia ilość płatności z podziałem na rodzaje. Największym zainteresowaniem cieszy się tradycyjna płatność, czyli płatność gotówką oraz elektronicznym portfelem, natomiast najmniejszym karta kredytowa jest to duże zaskoczenie, patrząc na to, że coraz więcej społeczeństwa odbiega od używania gotówki.

Ostatnią tego typu graficzną prezentacją jest stosunek ilości produktów przynależących do określonych grup.

ggplot(dane, aes(dane$Product.line)) +
  geom_bar(fill="orange2")+
  xlab("Rodzaj produktu") +
  ylab("Ilość")

Intepretacja: Wykres kolumnowy przedstawia ilość produktów, które posiada każdy z rodzai produktów. Najwięcej produktów należy do grupy pod nazwą:modowe akcesoria, zaś najmniejszą: zdrowie i uroda.

Następnie postanowiliśmy wykonać wizualizację rozkładów poszczególnych zmiennych ilościowych. Zaczęliśmy od przedstawienia za pomocą histogramu relacji ilości produktów zakupionych przez klientów.

ggplot(dane, aes(dane$Quantity)) +
  geom_histogram(fill="lightblue")+
  xlab("Ilość produktów") +
  ylab("Potwórzenia ilości produktów zakupionych przez klientów")

Intepretacja: Wykres przedstawia powtórzenia w kontekście ilości produktó zakupionych przez klientów sklepu. Największy wynik wynosi 10 produktów, ponieważ powtórzył się on ponad 100 razy. Natomiast najmniejszą ilością charakteryzuje się ilość 8 produktów, bo powtórzenia wyniosły tylko ok. 86 powtórzeń.

Następnie zastanowiliśmy się na tym jak wygląda rozkład opłaty podatkowej.

ggplot(dane, aes(dane$Tax.5.)) +
geom_density(fill = "cornsilk")+
  xlab("Opłata podatkowa") +
  ylab("Ilość powtórzeń występującej opłaty podatkowej")

Intepretacja: Wykres przedstawia rozkład opłat podatkowych w wysokości 5% dla klienta dokonującego zakupy można stwierdzić, że największą ilościa charakteryzuje się podatek o wysokości ok. 6-7 dolara.

Wizualizacji poddaliśmy również liczbę kupionych produktów w danej cenie w różnych oddziałach supermarketów.

ggplot(dane, aes(dane$Unit.price)) +
  geom_histogram(binwidth = 4, center = 2.5, fill = "green3") +
  facet_wrap(vars(dane$Branch)) +
  xlab("Cena każdego produktu [$]") +
  ylab("Ilość powtórzeń")

Intepretacja: Wizualizacja rozkładów z podziałem na oddział supercentrum pokazuje nam ile razy kupiono produkt za daną cenę. Największymi wynikami wyróżnia się supercentrum C, czyli miejscowość Naypyitaw, ponieważ posiada największą ilość powtórzeń ceny 100 dolara w porównaniu z innymi oddziałami.

Zbadaliśmy również jak wygląda rozkład ocen satysfakcji z podziałem na płeć.

ggplot(dane, aes(dane$Rating)) +
  geom_histogram(binwidth = 4, center = 2.5, fill = "pink") +
  facet_wrap(vars(dane$Gender)) +
  xlab("Ocena satysfakcji") +
  ylab("Ilość powtórzeń")

Intepretacja: Wizualizacja rozkładów obrazuje nam ilość ocen satysfakcji w oparciu na podział względem płci. Mężczyźni wyróżniają się na tle kobiet, ponieważ najczęściej z większą ilością powtórzeń wybierali oceny w przedziale od 5.0-8.0. Kobiety również skłaniały się do tego samego przedziału, jednakże możemy zauważyć, że sklep Biedronka posiada więcej klientów płci męskiej, niż płci żeńskiej.

Za ciekawe uznaliśmy również przedstawienie za pomocą wykresu, cen z podatkiem w poszczególnych miesiącach. W tym celu musieliśmy wgrać niezbędne biblioteki.

install.packages("colorspace")
library(colorspace)
library(ggforce)
library(ggridges)

dane$month <- format( as.Date (dane$Date, format=" %m/%d/%Y ")," %m ")
ggplot(dane, aes(x = dane$month, y = dane$gross.income)) +
  geom_point(size = 0.75, fill="black") +
      xlab("Miesiąc") +
      ylab("Dochód brutto")

Intepretacja: analizując rozkład, który opiera się podziałem na miesiące względem dochodu brutto, można stwierdzić iż styczeń był najbardziej dochodowym miesiącem, jeśli chodzi o ceny posiadające wysoki zysk brutto. Natomiast najniższy dochód brutto osiągnięty przez filie supermarketu został osiągnięty w marcu.

Analiza opisowa - z czym w końcu mamy do czynienia?

Gdy już nasz zbiór danych nie skrywa wizualnych tajemnic, możemy ruszać do pracy z analizą opisową. Zaczynamy od wyznaczenia zakresu cen z wliczonym podatkiem oraz prac z tymi informacjami.

range(dane$Total) #znajdujemy zakres cen z wliczonym podatkiem
max(dane$Total)-min(dane$Total)

Intepretacja: Biedronka charakteryzuje się minimalną (10.68) oraz maksymalną ceną z podatkiem (1042.65), co pozwala stwierdzić, że sklep dyskontowy charakteryzuje się zróżnicowanym poziomem cen w produktach, które posiada. Z przedstawionych powyżej operacji wiadomym jest również, że różnica pomiędzy maksymalną, a minimalną ceną (z podatkiem) wynosi 1031.97 dolara.

Przejdziemy teraz do dalszych prac.

limits<-cut(dane$Total,seq(0,1100,by=100))
table1<-table(limits)
transform(table1,Rel_Freq=prop.table(Freq),Cum_Freq=cumsum(Freq))

# Przedstawmy powyższe na wykresie 

hist(dane$Total,prob=TRUE,breaks=seq(0,1100,by=100),main="Total",sub="w $")
lines(density(dane$Total),col=6)

install.packages("classInt")
library(classInt)

tab1<-classIntervals(dane$Total,n=11,style="fixed",fixedBreaks=seq(0,1100,by=100))
tab1

jenks.tests(tab1)

Zajmiemy się teraz statystykami opisowymi dla zmian całkowitej sprzedaży wraz z podatkiem. Oprócz obecnych pakietów, potrzebny będzie jeszcze inny: “kableExtra”.

library(kableExtra)
dane%>%
  group_by(Gender)%>%
  summarize('Suma sprzedaży'=sum(dane$Total),
            'Średnia sprzedaż'=mean(dane$Total),
            'Mediana sprzedaży'=median(dane$Total),
            'Minimalna sprzedaż'=min(dane$Total),
            'Maksymalna sprzedaż'=max(dane$Total),
            'Odchylenie standardowe'=sd(dane$Total))%>%
  arrange(desc('Suma sprzedaży')) %>%
  kbl()%>%
  kable_styling(bootstrap_options = c("striped", "hover","responsive"),position="center")

(Interpretacja wyników).Teraz zajmiemy się spojrzeniem na statystyki dotyczące oceny satysfakcji klienta.

dane%>%
  group_by(City)%>%
  summarize('Suma ocen satysfakcji klientów'=sum(dane$Rating),
            'Średnia ocen satysfakcji'=mean(dane$Rating),
            'Mediana ocen satysfakcji'=median(dane$Rating),
            'Minimalna ocena satysfakcji'=min(dane$Rating),
            'Maksymalna ocena satysfakcji'=max(dane$Rating),
            'Odchylenie standardowe'=sd(dane$Rating))%>%
  arrange(desc('Suma ocen satysfakcji')) %>%
  kbl()%>%
  kable_styling(bootstrap_options = c("striped", "hover","responsive"),position="center")

(Interpretacja wyników).Kolejne statystyki dotyczą natomiast cen produktów

dane%>%
  group_by(Product.line)%>%
  summarize('Suma cen produktów'=sum(dane$Rating),
            'Średnia cen produktów'=mean(dane$Rating),
            'Mediana cen produktów'=median(dane$Rating),
            'Minimalna cen produktów'=min(dane$Rating),
            'Maksymalna cena produktów'=max(dane$Rating),
            'Odchylenie standardowe'=sd(dane$Rating))%>%
  arrange(desc('Suma cen produktów')) %>%
  kbl()%>%
  kable_styling(bootstrap_options = c("striped", "hover","responsive"),position="center")

Wnioskowanie - warto sprawdzić pytania badawcze!

Hipotezy, które postawiliśmy w niniejszym projekcie na potrzeby weryfikacji wybranych zjawisk.

Prace w tym zakresie rozpoczynamy od pobrania niezbędnych bibliotek do R, popularnych z zakresu testowania statystycznego.

install.packages("ggstatsplot")
library(ggstatsplot)

Pierwszy wariant hipotez: H0: Kobiety kupują więcej produktów z kategorii: zdrowie i uroda niż mężczyźni; H1: Kobiety nie kupują więcej produktów z kategorii: zdrowie i uroda niż mężczyźni.

data=dane
x=dane$Gender
y=dane$Product.line
ggpiestats(x=Gender,
           y=Product.line,
           data=dane)

Wartość p (p-value) na poziomie 0,33 pokazuje, że nie ma podstaw do odrzucenia hipotezy zerowej. Oznacza to, że kobiety rzeczywiście kupują więcej produktów z kategorii produktów “zdrowie i uroda”.

Drugi wariant hipotez: H0: Mężczyźni kupują więcej produktów z kategorii: elektronika niż kobiety; H1: Mężczyźni nie kupują więcej produktów z kategorii: elektronika niż kobiety.

data=dane
x=dane$Gender
y=dane$Product.line
ggpiestats(x=Gender,
           y=Product.line,
           data=dane)

Wartość p (p-value) na poziomie 0,33 pokazuje, że nie ma podstaw do odrzucenia hipotezy zerowej. Oznacza to, że mężczyźni kupują więcej produktów z kategorii: elektronika niż kobiety.

Trzeci wariant hipotez: H0: Klienci posiadający kartę lojalnościową wydają mniej od osób jej nieposiadających; H1: Klienci posiadający kartę lojalnościową wydają więcej od osób jej nieposiadających.

data=dane
x=dane$Customer.type
y=dane$Total

ggbetweenstats(x=Customer.type,
           y=Total,
           data=dane)

Wynik wartości p (p-value) na poziomie 0,53 pokazuje, że nie ma podstaw do odrzucenia hipotezy zerowej. Oznacza to, że klienci posiadający kartę lojalnościową wydaj mniej od osób jej nieposiadających.

Czwarty wariant hipotez: H0: Mężczyźni częściej od kobiet płacą za zakupy eportfelem; H1: Mężczyźni rzadziej od kobiet płacą za zakupy eportfelem.

data=dane
x=dane$Gender
y=dane$Payment
ggpiestats(x=Gender,
           y=Payment,
           data=dane)

Wynik wartości p (p-value) na poziomie 0,23 pokazuje, że nie ma podstaw do odrzucenia hipotezy zerowej. Oznacza to, że mężczyźni częściej od kobiet płacą za zakupy eportfelem.

Podsumowanie i wnioski końcowe - co wiemy z całej pracy?

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEFuYWxpemEgZGFueWNoIHByb2pla3QgemVzcG/Fgm93eSANCg0KVyByYW1hY2ggemFsaWN6ZW5pYSBwcnplZG1pb3R1IEFuYWxpemEgZGFueWNoIG5hIGtpZXJ1bmt1IEFuYWxpdHlrYSBHb3Nwb2RhcmN6YSBJSSANCnpvc3RhbGnFm215IHpvYmxpZ293YW5pIGRvIHd5a29uYW5pYSBzcGVjamFsbmVnbyBwcm9qZWt0dSB3eWJyYW5lZ28gcHJ6ZXogcHJvd2FkesSFY2Vnby4gIA0KV3lsb3Nvd2FueW0gemVzdGF3ZW0gZGFueWNoIG9rYXphxYIgc2nEmSB6YmnDs3IgZG90eWN6xIVjeSBzdXBlcm1hcmtldMOzdy4gDQoNCk5pbmllanN6eSBwcm9qZWt0IHNrxYJhZGEgc2nEmSB6IHN6ZcWbY2l1IGN6xJnFm2NpIHRlbWF0eWN6bnljaCBvZHBvd2llZG5pbyB3eXLDs8W8bmlvbnljaCANCm5hZ8WCw7N3a2FtaSBzdHlsdSBIMi4gDQoNClR1IGLEmWR6aWUgc3BpcyB0cmXFm2NpIA0KDQojIyBXcHJvd2FkemVuaWUgDQoNClcgZHluYW1pY3pueW0gxZt3aWVjaWUgaGFuZGx1IGRldGFsaWN6bmVnbywgcm96d8OzaiBzdXBlcm1hcmtldMOzdyB3IG5hamJhcmR6aWVqIHphbHVkbmlvbnljaCBtaWFzdGFjaCBvc2nEhWdhIGNvcmF6IHRvIG5vd2Ugc3pjenl0eSBrb25rdXJlbmN5am5vxZtjaS4gVyBrb250ZWvFm2NpZSB0ZWdvIHpqYXdpc2thLCBrbHVjem93eW0gZWxlbWVudGVtIHN0YWplIHNpxJkgZG9nxYLEmWJuYSBhbmFsaXphIGRhbnljaCBzcHJ6ZWRhxbxvd3ljaCANCnLDs8W8bnljaCBzdXBlcm1hcmtldMOzdy4gTmluaWVqc3p5IHByb2pla3QgbWEgbmEgY2VsdSB6YmFkYW5pZSBpIHpyb3p1bWllbmllIHRyZW5kw7N3IHNwcnplZGHFvG93eWNoIHcgdHJ6ZWNoIG9kZHppYcWCYWNoIGplZGVuZWogZmlybXkgYsSZZMSFY2VqIHN1cGVybWFya2V0ZW0sIHJlcHJlemVudHVqxIVjeWNoIHLDs8W8bm9yb2RuZSBsb2thbGl6YWNqZSBpIGRlbW9ncmFmaWUga2xpZW50w7N3Lg0KDQpEYW5lLCBuYSBrdMOzcnljaCBvcGllcmEgc2nEmSB0YSBhbmFsaXphLCB6b3N0YcWCeSB6ZWJyYW5lIHcgY2nEhWd1IHRyemVjaCBtaWVzacSZY3ksIG9kIHN0eWN6bmlhIGRvIG1hcmNhIDIwMTkgcm9rdSwgaSB6YXdpZXJhasSFIHN6Y3plZ8OzxYJvd2UgaW5mb3JtYWNqZSBuYSB0ZW1hdCBrYcW8ZGVqIHRyYW5zYWtjamkuIEZ1bmRhbWVudGFtaSB3eWtvbmFuZWogdyBwcm9qZWtjaWUgYW5hbGl6eSBzxIUgdGFraWUgem1pZW5uZSBqYWs6IG51bWVyIGlkZW50eWZpa2FjeWpueSBmYWt0dXJ5IChJbnZvaWNlIElEKSwgb2RkemlhxYIsIHcga3TDs3J5bSBkb2tvbmFubyB6YWt1cHUgKEJyYW5jaCksIGxva2FsaXphY2phIHN1cGVybWFya2V0dSAoQ2l0eSksIHR5cCBrbGllbnRhIChDdXN0b21lciB0eXBlKSwgcMWCZcSHIChHZW5kZXIpLCBsaW5pYSBwcm9kdWt0w7N3IChQcm9kdWN0IGxpbmUpLCBjZW5hIGplZG5vc3Rrb3dhIChVbml0IHByaWNlKSwgaWxvxZvEhyB6YWt1cGlvbnljaCBwcm9kdWt0w7N3IChRdWFudGl0eSksIG9wxYJhdGEgcG9kYXRrb3dhIChUYXgpLCBjYcWCa293aXRhIGNlbmEgemFrdXB1IChUb3RhbCksIGRhdGEgKERhdGUpIGkgY3phcyB0cmFuc2FrY2ppIChUaW1lKSwgbWV0b2RhIHDFgmF0bm/Fm2NpIChQYXltZW50KSwga29zenQgc3ByemVkYW55Y2ggdG93YXLDs3cgKENPR1MpLCBwcm9jZW50b3dhIG1hcsW8YSBicnV0dG8gKEdyb3NzIG1hcmdpbiBwZXJjZW50YWdlKSwgZG9jaMOzZCBicnV0dG8gKEdyb3NzIGluY29tZSkgb3JheiBvY2VuYSBrbGllbnRhIChSYXRpbmcpLg0KDQpXIGtvbnRla8WbY2llIHJvc27EhWNlaiBrb25rdXJlbmNqaSBuYSByeW5rdSwgenJvenVtaWVuaWUgdHljaCBkYW55Y2ggbW/FvGUgb2themHEhyBzacSZIGtsdWN6b3dlIGRsYSBmb3JtdcWCb3dhbmlhIHN0cmF0ZWdpaSBiaXpuZXNvd3ljaCBpIHcgbmFkxIXFvGFuaXUgemEgZHluYW1pY3pueW1pIHptaWFuYW1pLiBBbmFsaXp1asSFYywgamFrIHLDs8W8bmUgY3p5bm5pa2ksIHRha2llIGphayBsb2thbGl6YWNqYSwgdHlwIGtsaWVudGEgY3p5IHd5YsOzciBwcm9kdWt0w7N3LCB3cMWCeXdhasSFIG5hIHd5bmlraSBzcHJ6ZWRhxbx5LCBtb8W8ZW15IGxlcGllaiB6cm96dW1pZcSHIHBvdHJ6ZWJ5IGkgcHJlZmVyZW5jamUga29uc3VtZW50w7N3LiBUbyB6IGtvbGVpIG1vxbxlIHByb3dhZHppxIcgZG8gYmFyZHppZWogY2Vsb3d5Y2ggaSBza3V0ZWN6bnljaCBkZWN5emppIGJpem5lc293eWNoLCBjbyBqZXN0IG5pZXp3eWtsZSBpc3RvdG5lIHcgxZt3aWVjaWUsIGdkemllIGtsaWVudCBpIGplZ28gZG/Fm3dpYWRjemVuaWEgc3Rhbm93acSFIG8gc3VrY2VzaWUgZmlybXkuDQoNCkNlbGVtIHRlZ28gcHJvamVrdHUgamVzdCBuaWUgdHlsa28genJvenVtaWVuaWUgb2JlY255Y2ggdHJlbmTDs3csIGFsZSB0YWvFvGUgaWRlbnR5ZmlrYWNqYSBwb3RlbmNqYWxueWNoIG9ic3phcsOzdyBkbyByb3p3b2p1IGkgaW5ub3dhY2ppIHcgYnJhbsW8eSBzdXBlcm1hcmtldMOzdy4gUHJ6ZXogc2t1cGllbmllIHNpxJkgbmEgZGFueWNoIGhpc3Rvcnljem55Y2gsIHByb2pla3QgdGVuIG1hIG5hIGNlbHUgZG9zdGFyY3plbmllIHdhcnRvxZtjaW93eWNoIHdza2F6w7N3ZWsgZGxhIHByenlzesWCeWNoIGRlY3l6amkgc3RyYXRlZ2ljem55Y2ggaSBvcGVyYWN5am55Y2ggdyBicmFuxbx5IHN1cGVybWFya2V0w7N3Lg0KDQojIyBEYXRhIENsZWFuc2luZyAmIERhdGEgV3JhbmdsaW5nIC0gY3p5bGkgY3p5xZtjaW15IGkgdWplZG5vbGljYW15IGRhbmUhIA0KDQpQaWVyd3N6eSBldGFwIG1lcnl0b3J5Y3puZWogcHJhY3kgeiBkYW55bWksIHcgxbxhcmdvbmllIGFuYWxpdHlrw7N3IGRhbnljaCBva3JlxZtsYW55IGpha28gIkRhdGEgQ2xlYW5zaW5nIiBvcmF6ICJEYXRhIFdyYW5nbGluZyIsIHRvIG5pYyBpbm5lZ28gamFrIGN6eXN6Y3plbmllIGkgdWplZG5vbGljYW5pZSBkYW55Y2ggbmEgcG90cnplYnkgbmluaWVqc3plZ28gcHJvamVrdHUuIA0KDQojIyMgUm96cGFrb3dhbmllIGRhbnljaCB3IHByb2dyYW1pZSBSU3R1ZGlvIGkgd3nFm3dpZXRsZW5pZSBpY2ggemF3YXJ0b8WbY2kNCg0KWmFuaW0gamVkbmFrIHdkcm/FvG9uZSB6b3N0YW7EhSB0YWtpZSBwcmFjZSwgbmFsZcW8eSB3Z3JhxIcgZGFuZSBkbyBwcm9ncmFtdSBSIGkgemVya27EhcSHIG5hIHRhYmxpY8SZLiBXIHR5bSBtaWVqc2N1IA0KendlcnlmaWtvd2FsacWbbXkgdHlweSB3c3p5c3RraWNoIHptaWVubnljaC4gDQoNCmBgYHtyfQ0KZGFuZSA8LSByZWFkLmNzdigiQmllZHJvbmtpLmNzdiIpDQpgYGANCg0KTmEgcG90cnplYnkgcHJhYyBuYWxlxbx5IHphaW5zdGFsb3dhxIcgbmllemLEmWRuZSBiaWJsaW90ZWtpLCBrdMOzcmUgdG93YXJ6eXN6ecSHIGLEmWTEhSBuYSBldGFwaWUgY3p5c3pjem5pYSBkYW55Y2guIA0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQppbnN0YWxsLnBhY2thZ2VzKCJkbG9va3IiKQ0KaW5zdGFsbC5wYWNrYWdlcygiZWRpdHJ1bGVzIikNCmluc3RhbGwucGFja2FnZXMoIlZJTSIpDQppbnN0YWxsLnBhY2thZ2VzKCJkZWR1Y29ycmVjdCIpDQppbnN0YWxsLnBhY2thZ2VzKCJJU0xSIikNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGRsb29rcikNCmxpYnJhcnkoZWRpdHJ1bGVzKQ0KbGlicmFyeShWSU0pDQpsaWJyYXJ5KGRlZHVjb3JyZWN0KQ0KbGlicmFyeShJU0xSKSANCmBgYA0KDQpQcnplY2hvZHrEhWMgZG8gY3p5c3pjemVuaWEsY2HFgnkgcHJvY2VzIHJvenBvY3plbGnFm215IG9kIHNwcmF3ZHplbmlhLCBpbGUgcHJ6eXBhZGvDs3cgZGFueWNoIGplc3Qga29tcGxldG55Y2gsIGN6eWxpIGN6eSB3eXN0xJlwdWrEhWMgTkEuIEplZG5hayBicmFraSBkYW55Y2ggdG8gbmllIHdzenlzdGtvLiBQb3N0YW5vd2lsacWbbXkgc3ByYXdkemnEhywgY3p5IHcgemJpb3J6ZSBkYW55Y2ggem5hamR1asSFIHNpxJkgamFraWXFmyB3YXJ0b8WbY2kgc3BlY2phbG5lLiBEbyB0ZWdvIHBvc8WCdcW8ecWCYSBzcGVjamFsbmEgZnVua2NqYS4gTmEgd3lwYWRlaywgZ2R5YnkgcG9qYXdpxYJ5IHNpxJkgamFraWXFmyB3YXJ0b8WbY2kgc3BlY2phbG5lLCB6ZGVjeWRvd2FsacWbbXkgc2nEmSwgxbxlIHByenlwaXN6ZW15IGltIHdhcnRvxZtjaSBOQS4gVyB0eW0gY2VsdSB3eWtvcnp5c3RhbGnFm215IHDEmXRsxJkuIFRhayBwcnplcHJvd2Fkem9uZSBvcGVyYWNqZSBwb3p3YWxhasSFIHd5ZHJ1a293YcSHIHN0YXR5c3R5a2kgb3Bpc293ZSBkbGEgem1pZW5ueWNoLiANCiANCmBgYHtyfQ0Kc3VtKGNvbXBsZXRlLmNhc2VzKGRhbmUpKQ0KDQppcy5zcGVjaWFsIDwtIGZ1bmN0aW9uKHgpew0KICBpZiAoaXMubnVtZXJpYyh4KSkgIWlzLmZpbml0ZSh4KSBlbHNlIGlzLm5hKHgpfQ0Kc2FwcGx5KGRhbmUsIGlzLnNwZWNpYWwpDQoNCmZvciAobiBpbiBjb2xuYW1lcyhkYW5lKSl7DQogIGlzLm5hKGRhbmVbW25dXSkgPC0gaXMuc3BlY2lhbChkYW5lW1tuXV0pDQp9DQpzdW1tYXJ5KGRhbmUpDQpgYGANCg0KUG93ecW8c3p5IGtvZCBwb3p3YWxhIHN0d2llcmR6acSHLCDFvGUgdyB6YmlvcnplIGRhbnljaCBuaWUgbWEgTkEuIEplZG5hayBwb3N0YW5vd2lsacWbbXkgemFnxYLEmWJpxIcgc2nEmSBqZXN6Y3plIHcgc8WCdXN6bm/Fm8SHIHRlZ28gdHdpZXJkemVuaWEuIE9zdGF0ZWN6bnltIHBvdHdpZXJkemVuaWVtIGLEmWR6aWUgZGxhIG5hcyB3aXp1YWxpemFjamEgb2JlY25vxZtjaSBld2VudHVhbG55Y2ggTkEuIFcgdHltIGNlbHUgcG9icmFsacWbbXkgc3BlY2phbG55IHBha2lldCAibmFuaWFyIi4gDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygibmFuaWFyIikNCmxpYnJhcnkobmFuaWFyKQ0KdmlzX21pc3MoZGFuZSkNCmBgYA0KDQpDbyBkbyBicmFrw7N3IGRhbnljaCBtYW15IGp1xbwgYWJzb2x1dG7EhSBwZXdub8WbxIcuIER6acSZa2kgdGVtdSBtb8W8ZW15IHDDs2rFm8SHIGRhbGVqIGkgc3BvanJ6ZcSHIG5hIHRvIGphayB1a3N6dGHFgnRvd3VqxIUgc2nEmSB6bWllbm5lIGpha2/Fm2Npb3dlIChsaWN6Ym93ZSkuIFcgdHltIGNlbHUgd3lrb3J6eXN0YW15IHBvcHVsYXJuxIUgbWV0b2TEmSB3aXp1YWxpemFjamkgLSB3eWtyZXN5IHB1ZGXFgmtvd2UuVGFraSB6YWJpZWcgbmEgZGFueWNoIHBvendvbGkgbmEgd3lzZWxla2Nqb25vd2FuaWUgdHljaCB6bWllbm55Y2ggY2nEhWfFgnljaCwga3TDs3JlIHphd2llcmFqxIUgd2FydG/Fm2NpIG9kc3RhasSFY2UuIA0KDQpgYGB7cn0NCmJveHBsb3QoZGFuZSRVbml0LnByaWNlKQ0KYm94cGxvdChkYW5lJFRheC41LikgI3phd2llcmEgd2FydG/Fm2NpIG9kc3RhasSFY2UNCmJveHBsb3QoZGFuZSRUb3RhbCkgI3phd2llcmEgd2FydG/Fm2NpIG9kc3RhasSFY2UgDQpib3hwbG90KGRhbmUkY29ncykgI3phd2llcmEgd2FydG/Fm2NpIG9kc3RhasSFY2UNCmJveHBsb3QoZGFuZSRncm9zcy5tYXJnaW4ucGVyY2VudGFnZSkgDQpib3hwbG90KGRhbmUkZ3Jvc3MuaW5jb21lKSAjemF3aWVyYSB3YXJ0b8WbY2kgb2RzdGFqxIVjZQ0KYm94cGxvdChkYW5lJFJhdGluZykNCmBgYA0KDQojIyBXaXp1YWxpemFjamEgZGFueWNoIC0gc3DDs2pyem15IGpha2llIG1hbXkgaW5mb3JtYWNqZSEgDQoNClBvIHR5bSBqYWsgcHJ6ZXByb3dhZHppbGnFm215IHByb2NlZHVyxJkgY3p5c3pjemVuaWEgaSB1amVkbm9saWNhbmlhIG5hc3p5Y2ggd2VqxZtjaW93eWNoIGRhbnljaCwgbW/FvGVteSBza3VwacSHIHNpxJkgbmEgaWNoIGtvbmtyZXRuZWogd2l6dWFsaXphY2ppLiBQb3N0YW5vd2lsacWbbXkgemEgcG9tb2PEhSB3eWtyZXN1IGtvbHVtbm93ZWdvIHByemVkc3Rhd2nEhyBzdG9zdW5layBwcm9kdWt0w7N3IGRvIGNlbnkgeiBwb2RhdGtpZW0uIFp3aXp1b3dhbGnFm215IHLDs3duaWXFvCBzdG9zdW5layBtaWFzdGEgZG8gY2VueSB6YXdpZXJhasSFY2VqIHBvZGF0ZWsgb3JheiBzdG9zdW5layBtaWFzdGEgZG8gc2F0eXNmYWtjamkga2xpZW50YS4gSmVkbmFrIHphY3puaWpteSBvZCBwb2N6xIV0a3UuIA0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKQ0KbGlicmFyeSgnZ2dwbG90MicpDQpnZ3Bsb3QoZGFuZSwgYWVzKHggPSBkYW5lJFByb2R1Y3QubGluZSwgeSA9IGRhbmUkVG90YWwpKSArDQogIGdlb21fY29sKGZpbGw9ImxpZ2h0cGluazIiKSArIA0KICB4bGFiKCJLcnl0ZXJpYSBwcm9kdWt0w7N3IikgKw0KICB5bGFiKCJDZW5hIHdyYXogeiBwb2RhdGtpZW0gWyRdIikNCmBgYA0KDQpJbnRlcHJldGFjamE6IEFuYWxpenVqxIVjIGtyeXRlcmlhIHByb2R1a3TDs3cgb3JheiBjZW55LCBrdMOzcmUgbWFqxIUganXFvCB3bGljem9ueSBwb2RhdGVrLCB0byB3IHNrbGVwaWUgZHlza29udG93eW0NCkJpZWRyb25rYSBuYWpkcm/FvHN6ZSBwcm9kdWt0eSB3emdsxJlkZW0gY2Vub3d5bSB6bmFqZHVqxIUgc2nEmSB3IGplZHplbmlhY2ggaSBuYXBvamFjaCwgY28gdXdhxbxhbXksIMW8ZSBqZXN0IHpqYXdpc2tpZW0NCm5lZ2F0eXdueW0sIHBvbmlld2HFvCBwb2RhdGtpIHN0YW5vd2nEhSB3acSZa3N6b8WbxIcgY2VueSBwcnpleiBwb2RhdGVrIGN1a3Jvd3kuIA0KDQpTa3VwaW15IHNpxJkgdGVyYXogbmEgc3Rvc3Vua3UgbWlhc3RhIGRvIGNlbnkgeiBvYm93acSFenVqxIVjeW0gcG9kYXRraWVtLiANCg0KYGBge3J9DQpnZ3Bsb3QoZGFuZSwgYWVzKHggPSBkYW5lJENpdHksIHkgPSBkYW5lJFRvdGFsKSkgKw0KICBnZW9tX2NvbChmaWxsPSJncmVlbjQiKSsNCiAgeGxhYigiTWlhc3RvIikgKw0KICB5bGFiKCJDZW5hIHdyYXogeiBwb2RhdGtpZW0gWyRdIikNCmBgYA0KDQpJbnRlcnByZXRhY2phOiBCaW9yxIVjIHBvZCB1d2FnxJkgbWlhc3RhIG9yYXogY2VuYSB6IHdsaWN6b255IHogcG9kYXRraWVtLCBtb8W8bmEgd3lzdW7EhcSHIHBld25lIHduaW9za2ksIGEgbWlhbm93aWNpZSBtaWFzdG8gTmF5cHlpdGF3IGNoYXJha3Rlcnl6dWplIHNpxJkgbmFqZHJvxbxzenltaSBwcm9kdWt0YW1pLCB6IGtvbGVpIGR3YSBwb3pvc3RhxYJlIG1pYXN0YSBwb2QgbmF6d2FtaTogTWFuZGFsYXkgaSBZYW5nb24gc8SFIG5hIHR5bSBzYW15bSBwb3ppb21pZS4NCg0KTmFzdMSZcG5pZSBwb3N0YW5vd2lsacWbbXkgem9icmF6b3dhxIcgcmVsYWNqxJkgY2VueSBkbyBzYXR5c2Zha2NqaSBrbGllbnRhLiANCg0KYGBge3J9DQpnZ3Bsb3QoZGFuZSwgYWVzKHggPSBkYW5lJFRvdGFsLCB5ID0gZGFuZSRSYXRpbmcpKSArDQogIGdlb21fY29sKHNpemU9MSwgZmlsbD0iYmx1ZTQiKSsNCiAgeGxhYigiQ2VuYSB6IHBvZGF0a2llbSIpICsNCiAgeWxhYigiU2F0eXNmYWtjamEga2xpZW50YSIpDQpgYGANCg0KSW50ZXJwcmV0YWNqYTogQmlvcsSFYyBwb2QgdXdhZ8SZIHN0b3N1bmVrIHNhdHlzZmFrY2ppIGtsaWVudGEgZG8gc2FtZWogY2VueSB6IHdsaWN6b255bSBwb2RhdGtpZW0sIG1vxbxuYSB3eXN1bsSFYyBuYXN0xJlwdWrEhWNlIHduaW9za2kuIE5handpxJlrc3rEhSBsaWN6YsSFIG9jZW4gY2hhcmFrdGVyeXp1asSFIHNpxJkgY2VueSB3IG9rb2xpY2FjaCA1MDAkLCB6YcWbIG5ham1uaWVqc3rEhSBsaWN6YsSFIG9jZW4gY2VueSByw7N3bmUgMTAwMCBkb2wsIGLEhWTFuiB3ecW8c3plLg0KDQpOYWp3ecW8c3p5IGN6YXMgemF0ZW0gemJhZGHEhyBrc3p0YcWCdG93YW5pZSBzacSZIGlsb8WbY2kuIFphY3rEmWxpxZtteSBvZCBpbG/Fm2NpIG9jZW4gc2F0eXNmYWtjamkga2xpZW50YS4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhbmUsIGFlcyhkYW5lJFJhdGluZykpICsNCiAgZ2VvbV9iYXIoZmlsbD0icHVycGxlNCIpKw0KICB4bGFiKCJTYXR5c2Zha2NqYSBrbGllbnRhIikgKw0KICB5bGFiKCJJbG/Fm8SHIG9jZW4iKQ0KYGBgDQoNCkludGVycHJldGFjamE6IEFuYWxpenVqxIVjIHBvbmnFvHN6eSB3eWtyZXMgbW/FvG5hIHN0d2llcmR6acSHLCBpxbwgbmFqd2nEmWtzesSFIGlsb8WbY2nEhSBvY2VuIHNhdHlzZmFrY2ppIGtsaWVudGEgenlza2HFgmEgb2NlbmEgNi4gSmVzdCB0byBwb3d5xbxlaiBwb8WCb3d5LCBjbyBqZXN0IHBvenl0eXdueW0gd3luaWtpZW0gZGxhIHNrbGVwdSBkeXNrb250b3dlZ28uDQoNCldhcnRvIHNwb2pyemXEhyB0ZXJheiBuYSBsaWN6YsSZIHd5a29uYW55Y2ggemFrdXDDs3cgdyBwb3N6Y3plZ8OzbG55Y2ggbWlhc3RhY2guIA0KDQpgYGB7cn0NCmdncGxvdChkYW5lLCBhZXMoZGFuZSRDaXR5KSkgKw0KICBnZW9tX2JhcihmaWxsPSJsaWdodGdyZWVuIikrDQogIHhsYWIoIk1pYXN0byIpICsNCiAgeWxhYigiSWxvxZvEhyBmYWt0dXIiKQ0KYGBgDQoNCkludGVycHJldGFjamE6IFBvd3nFvHN6eSB3eWtyZXMgcHJ6ZWRzdGF3aWEgaWxvxZvEhyB3eWtvbmFueWNoIHpha3Vww7N3IHcgb3BhcmNpdSBvIHRyenkgbWlhc3RhLiBOYWp3acSZa3N6xIUgbGljemLEhSBkb2tvbmFueWNoIHpha3Vww7N3IGNoYXJha3Rlcnl6dWplIHNpxJkgbWlhc3RvIFlhbmdvbiwgemHFmyBuYWptbmllanN6xIUgTmF5cHlpdGF3Lg0KDQpab2JyYXp1amVteSB0ZXJheiBsaWN6YsSZIHDFgmF0bm/Fm2NpIHplIHd6Z2zEmWR1IG5hIHNwb3PDs2IgaWNoIGRva29uYW5pYS4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhbmUsIGFlcyhkYW5lJFBheW1lbnQpKSArDQogIGdlb21fYmFyKGZpbGw9ImxpZ2h0cGluayIpKw0KICB4bGFiKCJSb2R6YWogcMWCYXRub8WbY2kiKSArDQogIHlsYWIoIklsb8WbxIcgcMWCYXRub8WbY2kiKQ0KYGBgDQoNCkludGVwcmV0YWNqYTogV3lrcmVzIHByemVkc3Rhd2lhIGlsb8WbxIcgcMWCYXRub8WbY2kgeiBwb2R6aWHFgmVtIG5hIHJvZHphamUuIE5handpxJlrc3p5bSB6YWludGVyZXNvd2FuaWVtIGNpZXN6eSBzacSZIA0KdHJhZHljeWpuYSBwxYJhdG5vxZvEhywgY3p5bGkgcMWCYXRub8WbxIcgZ290w7N3a8SFIG9yYXogZWxla3Ryb25pY3pueW0gcG9ydGZlbGVtLCBuYXRvbWlhc3QgbmFqbW5pZWpzenltIGthcnRhIGtyZWR5dG93YQ0KamVzdCB0byBkdcW8ZSB6YXNrb2N6ZW5pZSwgcGF0cnrEhWMgbmEgdG8sIMW8ZSBjb3JheiB3acSZY2VqIHNwb8WCZWN6ZcWEc3R3YSBvZGJpZWdhIG9kIHXFvHl3YW5pYSBnb3TDs3draS4NCg0KT3N0YXRuacSFIHRlZ28gdHlwdSBncmFmaWN6bsSFIHByZXplbnRhY2rEhSBqZXN0IHN0b3N1bmVrIGlsb8WbY2kgcHJvZHVrdMOzdyBwcnp5bmFsZcW8xIVjeWNoIGRvIG9rcmXFm2xvbnljaCBncnVwLiANCg0KYGBge3J9DQpnZ3Bsb3QoZGFuZSwgYWVzKGRhbmUkUHJvZHVjdC5saW5lKSkgKw0KICBnZW9tX2JhcihmaWxsPSJvcmFuZ2UyIikrDQogIHhsYWIoIlJvZHphaiBwcm9kdWt0dSIpICsNCiAgeWxhYigiSWxvxZvEhyIpDQpgYGANCg0KSW50ZXByZXRhY2phOiBXeWtyZXMga29sdW1ub3d5IHByemVkc3Rhd2lhIGlsb8WbxIcgcHJvZHVrdMOzdywga3TDs3JlIHBvc2lhZGEga2HFvGR5IHogcm9kemFpIHByb2R1a3TDs3cuIE5handpxJljZWogcHJvZHVrdMOzdyBuYWxlxbx5IGRvIGdydXB5IHBvZCBuYXp3xIU6bW9kb3dlIGFrY2Vzb3JpYSwgemHFmyBuYWptbmllanN6xIU6IHpkcm93aWUgaSB1cm9kYS4gDQoNCk5hc3TEmXBuaWUgcG9zdGFub3dpbGnFm215IHd5a29uYcSHIHdpenVhbGl6YWNqxJkgcm96a8WCYWTDs3cgcG9zemN6ZWfDs2xueWNoIHptaWVubnljaCBpbG/Fm2Npb3d5Y2guIFphY3rEmWxpxZtteSBvZCBwcnplZHN0YXdpZW5pYSB6YSBwb21vY8SFIGhpc3RvZ3JhbXUgcmVsYWNqaSBpbG/Fm2NpIHByb2R1a3TDs3cgemFrdXBpb255Y2ggcHJ6ZXoga2xpZW50w7N3LiANCg0KYGBge3J9DQpnZ3Bsb3QoZGFuZSwgYWVzKGRhbmUkUXVhbnRpdHkpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Ymx1ZSIpKw0KICB4bGFiKCJJbG/Fm8SHIHByb2R1a3TDs3ciKSArDQogIHlsYWIoIlBvdHfDs3J6ZW5pYSBpbG/Fm2NpIHByb2R1a3TDs3cgemFrdXBpb255Y2ggcHJ6ZXoga2xpZW50w7N3IikNCmBgYA0KDQpJbnRlcHJldGFjamE6IFd5a3JlcyBwcnplZHN0YXdpYSBwb3d0w7NyemVuaWEgdyBrb250ZWvFm2NpZSBpbG/Fm2NpIHByb2R1a3TDsyB6YWt1cGlvbnljaCBwcnpleiBrbGllbnTDs3cgc2tsZXB1LiBOYWp3acSZa3N6eSB3eW5payB3eW5vc2kgMTAgcHJvZHVrdMOzdywgcG9uaWV3YcW8IHBvd3TDs3J6ecWCIHNpxJkgb24gcG9uYWQgMTAwIHJhenkuIE5hdG9taWFzdCBuYWptbmllanN6xIUgaWxvxZtjacSFIGNoYXJha3Rlcnl6dWplIHNpxJkgaWxvxZvEhyA4IHByb2R1a3TDs3csIGJvIHBvd3TDs3J6ZW5pYSB3eW5pb3PFgnkgdHlsa28gb2suIDg2IHBvd3TDs3J6ZcWELiANCg0KTmFzdMSZcG5pZSB6YXN0YW5vd2lsacWbbXkgc2nEmSBuYSB0eW0gamFrIHd5Z2zEhWRhIHJvemvFgmFkIG9wxYJhdHkgcG9kYXRrb3dlai4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhbmUsIGFlcyhkYW5lJFRheC41LikpICsNCmdlb21fZGVuc2l0eShmaWxsID0gImNvcm5zaWxrIikrDQogIHhsYWIoIk9wxYJhdGEgcG9kYXRrb3dhIikgKw0KICB5bGFiKCJJbG/Fm8SHIHBvd3TDs3J6ZcWEIHd5c3TEmXB1asSFY2VqIG9wxYJhdHkgcG9kYXRrb3dlaiIpDQpgYGANCg0KSW50ZXByZXRhY2phOiBXeWtyZXMgcHJ6ZWRzdGF3aWEgcm96a8WCYWQgb3DFgmF0IHBvZGF0a293eWNoIHcgd3lzb2tvxZtjaSA1JSBkbGEga2xpZW50YSBkb2tvbnVqxIVjZWdvIHpha3VweSBtb8W8bmEgc3R3aWVyZHppxIcsIMW8ZSBuYWp3acSZa3N6xIUgaWxvxZtjaWEgY2hhcmFrdGVyeXp1amUgc2nEmSBwb2RhdGVrIG8gd3lzb2tvxZtjaSBvay4gNi03IGRvbGFyYS4NCg0KV2l6dWFsaXphY2ppIHBvZGRhbGnFm215IHLDs3duaWXFvCBsaWN6YsSZIGt1cGlvbnljaCBwcm9kdWt0w7N3IHcgZGFuZWogY2VuaWUgdyByw7PFvG55Y2ggb2RkemlhxYJhY2ggc3VwZXJtYXJrZXTDs3cuIA0KDQpgYGB7cn0NCmdncGxvdChkYW5lLCBhZXMoZGFuZSRVbml0LnByaWNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDQsIGNlbnRlciA9IDIuNSwgZmlsbCA9ICJncmVlbjMiKSArDQogIGZhY2V0X3dyYXAodmFycyhkYW5lJEJyYW5jaCkpICsNCiAgeGxhYigiQ2VuYSBrYcW8ZGVnbyBwcm9kdWt0dSBbJF0iKSArDQogIHlsYWIoIklsb8WbxIcgcG93dMOzcnplxYQiKQ0KYGBgDQoNCkludGVwcmV0YWNqYTogV2l6dWFsaXphY2phIHJvemvFgmFkw7N3IHogcG9kemlhxYJlbSBuYSBvZGR6aWHFgiBzdXBlcmNlbnRydW0gcG9rYXp1amUgbmFtIGlsZSByYXp5IGt1cGlvbm8gcHJvZHVrdCB6YSBkYW7EhSBjZW7EmS4gTmFqd2nEmWtzenltaSB3eW5pa2FtaSB3eXLDs8W8bmlhIHNpxJkgc3VwZXJjZW50cnVtIEMsIGN6eWxpIG1pZWpzY293b8WbxIcgTmF5cHlpdGF3LCBwb25pZXdhxbwgcG9zaWFkYSBuYWp3acSZa3N6xIUgaWxvxZvEhyBwb3d0w7NyemXFhCBjZW55IDEwMCBkb2xhcmEgdyBwb3LDs3duYW5pdSB6ICBpbm55bWkgb2RkemlhxYJhbWkuDQoNClpiYWRhbGnFm215IHLDs3duaWXFvCBqYWsgd3lnbMSFZGEgcm96a8WCYWQgb2NlbiBzYXR5c2Zha2NqaSB6IHBvZHppYcWCZW0gbmEgcMWCZcSHLiANCg0KYGBge3J9DQpnZ3Bsb3QoZGFuZSwgYWVzKGRhbmUkUmF0aW5nKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDQsIGNlbnRlciA9IDIuNSwgZmlsbCA9ICJwaW5rIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGFuZSRHZW5kZXIpKSArDQogIHhsYWIoIk9jZW5hIHNhdHlzZmFrY2ppIikgKw0KICB5bGFiKCJJbG/Fm8SHIHBvd3TDs3J6ZcWEIikNCmBgYA0KDQpJbnRlcHJldGFjamE6IFdpenVhbGl6YWNqYSByb3prxYJhZMOzdyBvYnJhenVqZSBuYW0gaWxvxZvEhyBvY2VuIHNhdHlzZmFrY2ppIHcgb3BhcmNpdSBuYSBwb2R6aWHFgiB3emdsxJlkZW0gIHDFgmNpLiBNxJnFvGN6ecW6bmkgd3lyw7PFvG5pYWrEhSBzacSZIG5hIHRsZSBrb2JpZXQsIHBvbmlld2HFvCBuYWpjesSZxZtjaWVqIHogd2nEmWtzesSFIGlsb8WbY2nEhSBwb3d0w7NyemXFhCB3eWJpZXJhbGkgb2NlbnkgdyBwcnplZHppYWxlIG9kIDUuMC04LjAuDQpLb2JpZXR5IHLDs3duaWXFvCBza8WCYW5pYcWCeSBzacSZIGRvIHRlZ28gc2FtZWdvIHByemVkemlhxYJ1LCBqZWRuYWvFvGUgbW/FvGVteSB6YXV3YcW8ecSHLCDFvGUgc2tsZXAgQmllZHJvbmthIHBvc2lhZGEgd2nEmWNlaiBrbGllbnTDs3cgcMWCY2kgbcSZc2tpZWosIG5pxbwgcMWCY2kgxbxlxYRza2llai4gDQoNClphIGNpZWthd2UgdXpuYWxpxZtteSByw7N3bmllxbwgcHJ6ZWRzdGF3aWVuaWUgemEgcG9tb2PEhSB3eWtyZXN1LCBjZW4geiBwb2RhdGtpZW0gdyBwb3N6Y3plZ8OzbG55Y2ggbWllc2nEhWNhY2guIFcgdHltIGNlbHUgbXVzaWVsacWbbXkgd2dyYcSHIG5pZXpixJlkbmUgYmlibGlvdGVraS4gDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiY29sb3JzcGFjZSIpDQpsaWJyYXJ5KGNvbG9yc3BhY2UpDQpsaWJyYXJ5KGdnZm9yY2UpDQpsaWJyYXJ5KGdncmlkZ2VzKQ0KDQpkYW5lJG1vbnRoIDwtIGZvcm1hdCggYXMuRGF0ZSAoZGFuZSREYXRlLCBmb3JtYXQ9IiAlbS8lZC8lWSAiKSwiICVtICIpDQpnZ3Bsb3QoZGFuZSwgYWVzKHggPSBkYW5lJG1vbnRoLCB5ID0gZGFuZSRncm9zcy5pbmNvbWUpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNzUsIGZpbGw9ImJsYWNrIikgKw0KICAgICAgeGxhYigiTWllc2nEhWMiKSArDQogICAgICB5bGFiKCJEb2Now7NkIGJydXR0byIpDQpgYGANCg0KSW50ZXByZXRhY2phOiBhbmFsaXp1asSFYyByb3prxYJhZCwga3TDs3J5IG9waWVyYSBzacSZIHBvZHppYcWCZW0gbmEgbWllc2nEhWNlIHd6Z2zEmWRlbSBkb2Nob2R1IGJydXR0bywgbW/FvG5hIHN0d2llcmR6acSHIGnFvCBzdHljemXFhCBiecWCIG5hamJhcmR6aWVqIGRvY2hvZG93eW0gbWllc2nEhWNlbSwgamXFm2xpIGNob2R6aSBvIGNlbnkgcG9zaWFkYWrEhWNlIHd5c29raSB6eXNrIGJydXR0by4gTmF0b21pYXN0IG5ham5pxbxzenkgZG9jaMOzZCBicnV0dG8gb3NpxIVnbmnEmXR5IHByemV6IGZpbGllIHN1cGVybWFya2V0dSB6b3N0YcWCIG9zacSFZ25pxJl0eSB3IG1hcmN1LiANCg0KIyMgQW5hbGl6YSBvcGlzb3dhIC0geiBjenltIHcga2/FhGN1IG1hbXkgZG8gY3p5bmllbmlhPyANCg0KR2R5IGp1xbwgbmFzeiB6YmnDs3IgZGFueWNoIG5pZSBza3J5d2Egd2l6dWFsbnljaCB0YWplbW5pYywgbW/FvGVteSBydXN6YcSHIGRvIHByYWN5IHogYW5hbGl6xIUgb3Bpc293xIUuIFphY3p5bmFteSBvZCB3eXpuYWN6ZW5pYSB6YWtyZXN1IGNlbiB6IHdsaWN6b255bSBwb2RhdGtpZW0gb3JheiBwcmFjIHogdHltaSBpbmZvcm1hY2phbWkuIA0KDQpgYGB7cn0NCnJhbmdlKGRhbmUkVG90YWwpICN6bmFqZHVqZW15IHpha3JlcyBjZW4geiB3bGljem9ueW0gcG9kYXRraWVtDQptYXgoZGFuZSRUb3RhbCktbWluKGRhbmUkVG90YWwpDQpgYGANCg0KSW50ZXByZXRhY2phOiBCaWVkcm9ua2EgY2hhcmFrdGVyeXp1amUgc2nEmSBtaW5pbWFsbsSFICgxMC42OCkgb3JheiBtYWtzeW1hbG7EhSBjZW7EhSB6IHBvZGF0a2llbSAoMTA0Mi42NSksIGNvIHBvendhbGEgc3R3aWVyZHppxIcsIMW8ZSBza2xlcCBkeXNrb250b3d5IGNoYXJha3Rlcnl6dWplIHNpxJkgenLDs8W8bmljb3dhbnltIHBvemlvbWVtIGNlbiB3IHByb2R1a3RhY2gsIGt0w7NyZSBwb3NpYWRhLiBaIHByemVkc3Rhd2lvbnljaCBwb3d5xbxlaiBvcGVyYWNqaSB3aWFkb215bSBqZXN0IHLDs3duaWXFvCwgxbxlIHLDs8W8bmljYSBwb21pxJlkenkgbWFrc3ltYWxuxIUsIGEgbWluaW1hbG7EhSBjZW7EhSAoeiBwb2RhdGtpZW0pIHd5bm9zaSAxMDMxLjk3IGRvbGFyYS4gDQoNClByemVqZHppZW15IHRlcmF6IGRvIGRhbHN6eWNoIHByYWMuDQoNCmBgYHtyfQ0KbGltaXRzPC1jdXQoZGFuZSRUb3RhbCxzZXEoMCwxMTAwLGJ5PTEwMCkpDQp0YWJsZTE8LXRhYmxlKGxpbWl0cykNCnRyYW5zZm9ybSh0YWJsZTEsUmVsX0ZyZXE9cHJvcC50YWJsZShGcmVxKSxDdW1fRnJlcT1jdW1zdW0oRnJlcSkpDQoNCiMgUHJ6ZWRzdGF3bXkgcG93ecW8c3plIG5hIHd5a3Jlc2llIA0KDQpoaXN0KGRhbmUkVG90YWwscHJvYj1UUlVFLGJyZWFrcz1zZXEoMCwxMTAwLGJ5PTEwMCksbWFpbj0iVG90YWwiLHN1Yj0idyAkIikNCmxpbmVzKGRlbnNpdHkoZGFuZSRUb3RhbCksY29sPTYpDQoNCmluc3RhbGwucGFja2FnZXMoImNsYXNzSW50IikNCmxpYnJhcnkoY2xhc3NJbnQpDQoNCnRhYjE8LWNsYXNzSW50ZXJ2YWxzKGRhbmUkVG90YWwsbj0xMSxzdHlsZT0iZml4ZWQiLGZpeGVkQnJlYWtzPXNlcSgwLDExMDAsYnk9MTAwKSkNCnRhYjENCg0KamVua3MudGVzdHModGFiMSkNCmBgYA0KDQpaYWptaWVteSBzacSZIHRlcmF6IHN0YXR5c3R5a2FtaSBvcGlzb3d5bWkgZGxhIHptaWFuIGNhxYJrb3dpdGVqIHNwcnplZGHFvHkgd3JheiB6IHBvZGF0a2llbS4gT3Byw7NjeiBvYmVjbnljaCBwYWtpZXTDs3csIHBvdHJ6ZWJueSBixJlkemllIGplc3pjemUgaW5ueTogImthYmxlRXh0cmEiLiANCg0KYGBge3J9DQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpkYW5lJT4lDQogIGdyb3VwX2J5KEdlbmRlciklPiUNCiAgc3VtbWFyaXplKCdTdW1hIHNwcnplZGHFvHknPXN1bShkYW5lJFRvdGFsKSwNCiAgICAgICAgICAgICfFmnJlZG5pYSBzcHJ6ZWRhxbwnPW1lYW4oZGFuZSRUb3RhbCksDQogICAgICAgICAgICAnTWVkaWFuYSBzcHJ6ZWRhxbx5Jz1tZWRpYW4oZGFuZSRUb3RhbCksDQogICAgICAgICAgICAnTWluaW1hbG5hIHNwcnplZGHFvCc9bWluKGRhbmUkVG90YWwpLA0KICAgICAgICAgICAgJ01ha3N5bWFsbmEgc3ByemVkYcW8Jz1tYXgoZGFuZSRUb3RhbCksDQogICAgICAgICAgICAnT2RjaHlsZW5pZSBzdGFuZGFyZG93ZSc9c2QoZGFuZSRUb3RhbCkpJT4lDQogIGFycmFuZ2UoZGVzYygnU3VtYSBzcHJ6ZWRhxbx5JykpICU+JQ0KICBrYmwoKSU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsInJlc3BvbnNpdmUiKSxwb3NpdGlvbj0iY2VudGVyIikNCmBgYA0KDQooSW50ZXJwcmV0YWNqYSB3eW5pa8OzdykuVGVyYXogemFqbWllbXkgc2nEmSBzcG9qcnplbmllbSBuYSBzdGF0eXN0eWtpIGRvdHljesSFY2Ugb2Nlbnkgc2F0eXNmYWtjamkga2xpZW50YS4gDQoNCmBgYHtyfQ0KZGFuZSU+JQ0KICBncm91cF9ieShDaXR5KSU+JQ0KICBzdW1tYXJpemUoJ1N1bWEgb2NlbiBzYXR5c2Zha2NqaSBrbGllbnTDs3cnPXN1bShkYW5lJFJhdGluZyksDQogICAgICAgICAgICAnxZpyZWRuaWEgb2NlbiBzYXR5c2Zha2NqaSc9bWVhbihkYW5lJFJhdGluZyksDQogICAgICAgICAgICAnTWVkaWFuYSBvY2VuIHNhdHlzZmFrY2ppJz1tZWRpYW4oZGFuZSRSYXRpbmcpLA0KICAgICAgICAgICAgJ01pbmltYWxuYSBvY2VuYSBzYXR5c2Zha2NqaSc9bWluKGRhbmUkUmF0aW5nKSwNCiAgICAgICAgICAgICdNYWtzeW1hbG5hIG9jZW5hIHNhdHlzZmFrY2ppJz1tYXgoZGFuZSRSYXRpbmcpLA0KICAgICAgICAgICAgJ09kY2h5bGVuaWUgc3RhbmRhcmRvd2UnPXNkKGRhbmUkUmF0aW5nKSklPiUNCiAgYXJyYW5nZShkZXNjKCdTdW1hIG9jZW4gc2F0eXNmYWtjamknKSkgJT4lDQogIGtibCgpJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwicmVzcG9uc2l2ZSIpLHBvc2l0aW9uPSJjZW50ZXIiKQ0KYGBgDQoNCihJbnRlcnByZXRhY2phIHd5bmlrw7N3KS5Lb2xlam5lIHN0YXR5c3R5a2kgZG90eWN6xIUgbmF0b21pYXN0IGNlbiBwcm9kdWt0w7N3IA0KDQpgYGB7cn0NCmRhbmUlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdC5saW5lKSU+JQ0KICBzdW1tYXJpemUoJ1N1bWEgY2VuIHByb2R1a3TDs3cnPXN1bShkYW5lJFJhdGluZyksDQogICAgICAgICAgICAnxZpyZWRuaWEgY2VuIHByb2R1a3TDs3cnPW1lYW4oZGFuZSRSYXRpbmcpLA0KICAgICAgICAgICAgJ01lZGlhbmEgY2VuIHByb2R1a3TDs3cnPW1lZGlhbihkYW5lJFJhdGluZyksDQogICAgICAgICAgICAnTWluaW1hbG5hIGNlbiBwcm9kdWt0w7N3Jz1taW4oZGFuZSRSYXRpbmcpLA0KICAgICAgICAgICAgJ01ha3N5bWFsbmEgY2VuYSBwcm9kdWt0w7N3Jz1tYXgoZGFuZSRSYXRpbmcpLA0KICAgICAgICAgICAgJ09kY2h5bGVuaWUgc3RhbmRhcmRvd2UnPXNkKGRhbmUkUmF0aW5nKSklPiUNCiAgYXJyYW5nZShkZXNjKCdTdW1hIGNlbiBwcm9kdWt0w7N3JykpICU+JQ0KICBrYmwoKSU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsInJlc3BvbnNpdmUiKSxwb3NpdGlvbj0iY2VudGVyIikNCmBgYA0KDQoNCiMjIFduaW9za293YW5pZSAtIHdhcnRvIHNwcmF3ZHppxIcgcHl0YW5pYSBiYWRhd2N6ZSEgDQoNCkhpcG90ZXp5LCBrdMOzcmUgcG9zdGF3aWxpxZtteSB3IG5pbmllanN6eW0gcHJvamVrY2llIG5hIHBvdHJ6ZWJ5IHdlcnlmaWthY2ppIHd5YnJhbnljaCB6amF3aXNrLiANCg0KUHJhY2UgdyB0eW0gemFrcmVzaWUgcm96cG9jenluYW15IG9kIHBvYnJhbmlhIG5pZXpixJlkbnljaCBiaWJsaW90ZWsgZG8gUiwgcG9wdWxhcm55Y2ggeiB6YWtyZXN1IHRlc3Rvd2FuaWEgc3RhdHlzdHljem5lZ28uIA0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJnZ3N0YXRzcGxvdCIpDQpsaWJyYXJ5KGdnc3RhdHNwbG90KQ0KYGBgDQoNClBpZXJ3c3p5IHdhcmlhbnQgaGlwb3RlejogDQpIMDogS29iaWV0eSBrdXB1asSFIHdpxJljZWogcHJvZHVrdMOzdyB6IGthdGVnb3JpaTogemRyb3dpZSBpIHVyb2RhIG5pxbwgbcSZxbxjennFum5pOyANCkgxOiBLb2JpZXR5IG5pZSBrdXB1asSFIHdpxJljZWogcHJvZHVrdMOzdyB6IGthdGVnb3JpaTogemRyb3dpZSBpIHVyb2RhIG5pxbwgbcSZxbxjennFum5pLg0KDQpgYGB7cn0NCmRhdGE9ZGFuZQ0KeD1kYW5lJEdlbmRlcg0KeT1kYW5lJFByb2R1Y3QubGluZQ0KZ2dwaWVzdGF0cyh4PUdlbmRlciwNCiAgICAgICAgICAgeT1Qcm9kdWN0LmxpbmUsDQogICAgICAgICAgIGRhdGE9ZGFuZSkNCmBgYA0KDQpXYXJ0b8WbxIcgcCAocC12YWx1ZSkgbmEgcG96aW9taWUgMCwzMyBwb2thenVqZSwgxbxlIG5pZSBtYSBwb2RzdGF3IGRvIG9kcnp1Y2VuaWEgaGlwb3RlenkgemVyb3dlai4gT3puYWN6YSB0bywgxbxlIGtvYmlldHkgcnplY3p5d2nFm2NpZSBrdXB1asSFIHdpxJljZWogcHJvZHVrdMOzdyB6IGthdGVnb3JpaSBwcm9kdWt0w7N3ICJ6ZHJvd2llIGkgdXJvZGEiLiANCg0KRHJ1Z2kgd2FyaWFudCBoaXBvdGV6OiANCkgwOiBNxJnFvGN6ecW6bmkga3VwdWrEhSB3acSZY2VqIHByb2R1a3TDs3cgeiBrYXRlZ29yaWk6IGVsZWt0cm9uaWthIG5pxbwga29iaWV0eTsgDQpIMTogTcSZxbxjennFum5pIG5pZSBrdXB1asSFIHdpxJljZWogcHJvZHVrdMOzdyB6IGthdGVnb3JpaTogZWxla3Ryb25pa2EgbmnFvCBrb2JpZXR5LiANCg0KYGBge3J9DQpkYXRhPWRhbmUNCng9ZGFuZSRHZW5kZXINCnk9ZGFuZSRQcm9kdWN0LmxpbmUNCmdncGllc3RhdHMoeD1HZW5kZXIsDQogICAgICAgICAgIHk9UHJvZHVjdC5saW5lLA0KICAgICAgICAgICBkYXRhPWRhbmUpDQpgYGANCg0KV2FydG/Fm8SHIHAgKHAtdmFsdWUpIG5hIHBvemlvbWllIDAsMzMgcG9rYXp1amUsIMW8ZSBuaWUgbWEgcG9kc3RhdyBkbyBvZHJ6dWNlbmlhIGhpcG90ZXp5IHplcm93ZWouIE96bmFjemEgdG8sIMW8ZSBtxJnFvGN6ecW6bmkga3VwdWrEhSB3acSZY2VqIHByb2R1a3TDs3cgeiBrYXRlZ29yaWk6IGVsZWt0cm9uaWthIG5pxbwga29iaWV0eS4gDQoNClRyemVjaSB3YXJpYW50IGhpcG90ZXo6IA0KSDA6IEtsaWVuY2kgcG9zaWFkYWrEhWN5IGthcnTEmSBsb2phbG5vxZtjaW93xIUgd3lkYWrEhSBtbmllaiBvZCBvc8OzYiBqZWogbmllcG9zaWFkYWrEhWN5Y2g7IA0KSDE6IEtsaWVuY2kgcG9zaWFkYWrEhWN5IGthcnTEmSBsb2phbG5vxZtjaW93xIUgd3lkYWrEhSB3acSZY2VqIG9kIG9zw7NiIGplaiBuaWVwb3NpYWRhasSFY3ljaC4gDQoNCmBgYHtyfQ0KZGF0YT1kYW5lDQp4PWRhbmUkQ3VzdG9tZXIudHlwZQ0KeT1kYW5lJFRvdGFsDQoNCmdnYmV0d2VlbnN0YXRzKHg9Q3VzdG9tZXIudHlwZSwNCiAgICAgICAgICAgeT1Ub3RhbCwNCiAgICAgICAgICAgZGF0YT1kYW5lKQ0KYGBgDQoNCld5bmlrIHdhcnRvxZtjaSBwIChwLXZhbHVlKSBuYSBwb3ppb21pZSAwLDUzIHBva2F6dWplLCDFvGUgbmllIG1hIHBvZHN0YXcgZG8gb2RyenVjZW5pYSBoaXBvdGV6eSB6ZXJvd2VqLiBPem5hY3phIHRvLCDFvGUga2xpZW5jaSBwb3NpYWRhasSFY3kga2FydMSZIGxvamFsbm/Fm2Npb3fEhSB3eWRhaiBtbmllaiBvZCBvc8OzYiBqZWogbmllcG9zaWFkYWrEhWN5Y2guIA0KDQpDendhcnR5IHdhcmlhbnQgaGlwb3RlejogDQpIMDogTcSZxbxjennFum5pIGN6xJnFm2NpZWogb2Qga29iaWV0IHDFgmFjxIUgemEgemFrdXB5IGVwb3J0ZmVsZW07IA0KSDE6IE3EmcW8Y3p5xbpuaSByemFkemllaiBvZCBrb2JpZXQgcMWCYWPEhSB6YSB6YWt1cHkgZXBvcnRmZWxlbS4gDQoNCmBgYHtyfQ0KZGF0YT1kYW5lDQp4PWRhbmUkR2VuZGVyDQp5PWRhbmUkUGF5bWVudA0KZ2dwaWVzdGF0cyh4PUdlbmRlciwNCiAgICAgICAgICAgeT1QYXltZW50LA0KICAgICAgICAgICBkYXRhPWRhbmUpDQpgYGANCg0KV3luaWsgd2FydG/Fm2NpIHAgKHAtdmFsdWUpIG5hIHBvemlvbWllIDAsMjMgcG9rYXp1amUsIMW8ZSBuaWUgbWEgcG9kc3RhdyBkbyBvZHJ6dWNlbmlhIGhpcG90ZXp5IHplcm93ZWouIE96bmFjemEgdG8sIMW8ZSBtxJnFvGN6ecW6bmkgY3rEmcWbY2llaiBvZCBrb2JpZXQgcMWCYWPEhSB6YSB6YWt1cHkgZXBvcnRmZWxlbS4gDQoNCiMjIFBvZHN1bW93YW5pZSBpIHduaW9za2kga2/FhGNvd2UgLSBjbyB3aWVteSB6IGNhxYJlaiBwcmFjeT8gDQoNCg0KDQoNCg==