library(readxl)
library(dplyr)
library(naniar)
library(dygraphs)
library(leaflet)
library(xts)
Photo by Valentin Panzirsch, Wikipedia

Photo by Valentin Panzirsch, Wikipedia

Sweden

Inspired by recent news on a fortcoming book by Hannah Lutz, and the notebook on Raccoon dog in Sweden by Christopher Kullenberg, I decided to check what do we know about the wild boar population (vildsvin in Swedish, villisika in Finnish) in Sweden and Finland.

Based on a few stays in Italian and French countryside, I can confirm that yes, there indeed are wild boars in mainland Europe. In Finland, I haven’t seen a single one yet myself. But they are here, both domesticated (or zoo) animals and wild ones. The wild wild boars are mostly arrived from Russia, or escaped from farms.

While at it, I decided to test the new R Notebook feature of RStudio. Having done a little R Markdown’ing before, watching a helpful 45 min video tutorial gave a head start.

First I followed Christopher’s steps and went to the Swedish The Analysis portal for biodiversity data for citizen science data on Sus scrofa.

data_se <- read_excel("SpeciesObservations.xlsx", sheet = "SLW Data")
data_se$Start <- as.Date(data_se$Start, format = "%Y-%m-%d")
nrow(data_se)
[1] 1657
colnames(data_se)
  [1] "ScientificName"                   "VernacularName"                   "TaxonRank"                        "OrganismGroup"                   
  [5] "HigherClassification"             "Kingdom"                          "Phylum"                           "Class"                           
  [9] "Order"                            "Family"                           "Genus"                            "Subgenus"                        
 [13] "SpecificEpithet"                  "InfraspecificEpithet"             "TaxonID"                          "TaxonConceptID"                  
 [17] "DyntaxaTaxonID"                   "TaxonomicStatus"                  "TaxonConceptStatus"               "TaxonRemarks"                    
 [21] "VerbatimScientificName"           "VerbatimTaxonRank"                "ScientificNameAuthorship"         "OriginalNameUsage"               
 [25] "IndividualCount"                  "OccurrenceStatus"                 "Quantity"                         "QuantityUnit"                    
 [29] "LifeStage"                        "Sex"                              "Behavior"                         "RecordedBy"                      
 [33] "IsNeverFoundObservation"          "IsNotRediscoveredObservation"     "EstablishmentMeans"               "IsNaturalOccurrence"             
 [37] "IsPositiveObservation"            "OccurrenceURL"                    "OccurrenceID"                     "ReproductiveCondition"           
 [41] "CatalogNumber"                    "OccurrenceRemarks"                "RecordNumber"                     "IndividualID"                    
 [45] "AssociatedMedia"                  "AssociatedReferences"             "Substrate"                        "Locality"                        
 [49] "LocationRemarks"                  "Continent"                        "Country"                          "CountryCode"                     
 [53] "StateProvince"                    "County"                           "Municipality"                     "Parish"                          
 [57] "DecimalLongitude"                 "DecimalLatitude"                  "CoordinateUncertaintyInMeters"    "CoordinateX"                     
 [61] "CoordinateY"                      "CoordinateSystemWkt"              "LocationID"                       "MinimumElevationInMeters"        
 [65] "MaximumElevationInMeters"         "MinimumDepthInMeters"             "MaximumDepthInMeters"             "Start"                           
 [69] "End"                              "Habitat"                          "EventID"                          "SamplingProtocol"                
 [73] "EventDate"                        "EventTime"                        "StartDayOfYear"                   "EndDayOfYear"                    
 [77] "Year"                             "Month"                            "Day"                              "DatasetName"                     
 [81] "RightsHolder"                     "Owner"                            "Rights"                           "AccessRights"                    
 [85] "InstitutionID"                    "InstitutionCode"                  "OwnerInstitutionCode"             "CollectionID"                    
 [89] "CollectionCode"                   "ReportedBy"                       "Modified"                         "BibliographicCitation"           
 [93] "BasisOfRecord"                    "InformationWithheld"              "DataGeneralizations"              "Language"                        
 [97] "Type"                             "IdentifiedBy"                     "DateIdentified"                   "IdentificationRemarks"           
[101] "UncertainDetermination"           "IdentificationVerificationStatus" "ProjectName"                      "ProjectDescription"              
[105] "ProjectURL"                       "SurveyMethod"                     "ProjectOwner"                     "RedlistCategory"                 
[109] "Natura2000"                       "ConservationRelevant"             "ActionPlan"                       "ProtectedByLaw"                  
[113] "ProtectionLevel"                  "SwedishOccurrence"                "SwedishImmigrationHistory"        "ObservationId"                   
min(data_se$Start)
[1] "1985-05-10"
max(data_se$Start)
[1] "2017-01-08"

The number of different variables is impressive, and over 1600 observations quite an achievement. The time span is over 30 years.

Yet, there are also seemingly many empty cells. What data should I expect NOT to find? To answer this question, the brand new naniar package is of great help.

Let’s take the first half of the columns first, and sort them by missingness.

vis_miss(data_se[ , 1:as.integer(ncol(data_se)/2)], sort_miss = TRUE) + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 48, 
        vjust = 1, hjust = 1, size = 7))

And then the rest.

vis_miss(data_se[ , as.integer(ncol(data_se)/2+1):ncol(data_se)], sort_miss = TRUE) + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 48, 
        vjust = 1, hjust = 1, size = 7))

It looks like the dataset is a little patchy on extra information like what the animals were doing, gender etc. This is understandable. On the other hand, the core details if you like such as location and date are well represented. I’d guess that some of these fields are mandatory.

Next, let’s calculate daily statistics, and plot them.

dailysums <- data_se %>%
  rename(Date = Start) %>% 
  group_by(Date) %>% 
  summarise(Observations = n()) 
  
dailysums <- as.data.frame(dailysums, stringsAsFactors = FALSE)
# Pad dates
alltime <- seq.Date(min(dailysums$Date), max(dailysums$Date), by="day")
alltime_df <- data.frame(Date = alltime)
alltime_dailysums <- full_join(alltime_df, dailysums, by=c("Date"="Date"))
alltime_dailysums$Observations <- ifelse(is.na(alltime_dailysums$Observations), 0, alltime_dailysums$Observations)
rownames(alltime_dailysums) <- alltime_dailysums[[1]]
dailysums.xts <- as.xts(alltime_dailysums, dateFormat = "Date")
dygraph(dailysums.xts) %>%
  dyHighlight(highlightCircleSize = 5, 
              highlightSeriesBackgroundAlpha = 0.2,
              hideOnMouseOut = FALSE) %>% 
  # https://www.timeanddate.com/holidays/sweden/midsummer-day
  dyEvent("2010-06-26", "Midsummer", labelLoc = "top") %>% 
  dyEvent("2011-06-25", "Midsummer", labelLoc = "top") %>% 
  dyEvent("2012-06-23", "Midsummer", labelLoc = "top") %>% 
  dyEvent("2013-06-22", "Midsummer", labelLoc = "top") %>% 
  dyEvent("2014-06-21", "Midsummer", labelLoc = "top") %>% 
  dyEvent("2015-06-20", "Midsummer", labelLoc = "top") %>% 
  dyEvent("2016-06-25", "Midsummer", labelLoc = "top") %>%
  dyEvent("2010-12-24", "Christmas", labelLoc = "top") %>% 
  dyEvent("2011-12-24", "Christmas", labelLoc = "top") %>% 
  dyEvent("2012-12-24", "Christmas", labelLoc = "top") %>% 
  dyEvent("2013-12-24", "Christmas", labelLoc = "top") %>% 
  dyEvent("2014-12-24", "Christmas", labelLoc = "top") %>% 
  dyEvent("2015-12-24", "Christmas", labelLoc = "top") %>% 
  dyEvent("2016-12-24", "Christmas", labelLoc = "top") %>%
  dyOptions(stepPlot=FALSE,
            connectSeparatedPoints = FALSE,
            colors = RColorBrewer::brewer.pal(3, "Set2")) %>% 
  dyRangeSelector(height = 20)

To zoom in, use the slider, or draw a horizontal region.

I added manually some holiday events from recent years. This way, we can investigate whether there are more sightings around Midsummer or Christmas. (Not)

Finland

You can search and download animal observation data from the (beta) site of Finnish Biodiversity Info Facility (FinBIF). To get data, you need to register, and log in. When you have selected a set, an email is sent to you with a link to the downloading site. There is also an API, nicely documented thanks to OpenAPI Specification, but because I’ve got my access token only recently, I haven’t had time to check the documentation that much.

This is my dataset http://tun.fi/HBF.AD8DA752-1013-4AA2-8BC5-7AA21F0F4FF5 (downloaded 12.1.2017).

FinBIF is a joined achievement of several parties, so data includes also information on preserved specimens like skulls. Let’s first filter observations.

data_fi <- read.csv("rows_HBF.AD8DA752-1013-4AA2-8BC5-7AA21F0F4FF5.csv", stringsAsFactors = FALSE)
data_fi <- data_fi %>% 
  filter(Unit.RecordBasis == "HUMAN_OBSERVATION_UNSPECIFIED" & Gathering.CountryVerbatim %in% c("Suomi", "FI"))
nrow(data_fi)
[1] 21

Compared to Swedish data, the number of observations is very modest. Finland is famous for its volunteer birdwatchers, and the amount of longitudinal data from monitoring birds is massive. It is interesting why other animals get much less attention. I’d guess though that as a country, Finland is not a remarkable exception. It’s easy to understand why birding is so popular. Birds are relatively easy to find and watch, and only few are nocturnal. Although around 30 species can legally be hunted in Finland, I’d say that in general, birds are not considered game like mammals.

In Finland, the main source for observational, non-scientific data on mammals is hunting organizations. I’d be more than happy if those citizens who do not hunt, would more actively look around, and register what they see.

Joined map

data_se <- data_se %>% 
  mutate(Lat = as.numeric(gsub(",", ".", DecimalLatitude))) %>% 
  mutate(Lon = as.numeric(gsub(",", ".", DecimalLongitude))) %>% 
  mutate(Time = EventTime) %>% 
  mutate(Count = ifelse(is.na(IndividualCount), 0, as.integer(IndividualCount))) %>%
  mutate(Quantity = ifelse(is.na(Quantity), "N/A", Quantity)) %>%
  mutate(LifeStage = ifelse(is.na(LifeStage), "N/A", LifeStage)) %>% 
  mutate(Behavior = ifelse(is.na(Behavior), "N/A", Behavior)) %>% 
  mutate(Sex = ifelse(is.na(Sex), "N/A", Sex)) %>% 
  mutate(OccurrenceRemarks = ifelse(is.na(OccurrenceRemarks), "N/A", OccurrenceRemarks))
# max_data_se <- data_se %>% 
#   arrange(desc(Count)) %>% 
#   head()
data_fi <- data_fi %>% 
  mutate(Gathering.Date.Begin = as.Date(Gathering.Date.Begin, format = "%Y-%m-%d")) %>% 
  mutate(Unit.Abundance = ifelse(Unit.Abundance == "", "N/A", Unit.Abundance)) %>%
  mutate(Gathering.MunicipalityVerbatim = ifelse(Gathering.MunicipalityVerbatim == "", "N/A", Gathering.MunicipalityVerbatim)) %>% 
  mutate(Gathering.BioProvinceVerbatim = ifelse(Gathering.BioProvinceVerbatim == "", "N/A", Gathering.BioProvinceVerbatim)) %>% 
  mutate(Unit.Notes = ifelse(Unit.Notes == "", "N/A", Unit.Notes))
leaflet(data_se) %>% 
  addTiles() %>%
  addCircleMarkers(lng = ~Lon, lat = ~Lat, radius = ~sqrt(Count)*2, 
                   color= ~ifelse(data_se$Count >= 40 , "red", 
                                  ifelse(data_se$Count < 40 & data_se$Count > 20, "orange", "blue")),
                   stroke = TRUE, 
                   fillOpacity = 0.08,
                   popup = paste(sep = "<br/>",
                                paste0("County: ",data_se$County), 
                                paste0("Date: ", data_se$Start), 
                                paste0("Time: ", data_se$Time),
                                paste0("Quantity: ", data_se$Quantity),
                                paste0("Life stage: ", data_se$LifeStage),
                                paste0("Behavior: ", data_se$Behavior), 
                                paste0("Sex: ", data_se$Sex), 
                                paste0("Remarks: ", data_se$OccurrenceRemarks))) %>% 
  
  addMarkers(lng = data_fi$Gathering.Conversions.WGS84CenterPoint.Lon.E, lat = data_fi$Gathering.Conversions.WGS84CenterPoint.Lat.N,
             popup = paste(sep = "<br/>", 
                           paste0("Country: ",data_fi$Gathering.CountryVerbatim), 
                           paste0("Municipality: ", data_fi$Gathering.MunicipalityVerbatim),
                           paste0("Province: ", data_fi$Gathering.BioProvinceVerbatim), 
                           paste0("Date: ", data_fi$Gathering.Date.Begin), 
                           paste0("Quantity: ", data_fi$Unit.Abundance),
                           paste0("Remarks: ", data_fi$Unit.Notes)))

The user experience would benefit from reducing the number of data points but here we go.

In Sweden, some of the groups are surprisingly big, even in the outskirts of Stockholm! On the map, the biggest recorded herds have a red stroke, the next biggest orange, and the rest are blue.

From the popups of the red circles we learn that the bigger herd, the more this is because of feeding.

The most Northern - and rather lone - observation is near Umeå.

Note that in the Finnish dataset, there are few observations that are actually not from Finland at all, but from Estonia, Poland, Russia, and Germany. These ones have no coordinates, so they are not on the map.

Estonia is a special case at the moment. Finnish hunters returning from wild boar safaris in Estonia are under strict control by the Finnish authorities because of African swine feaver.

EDIT 2017-01-16: Check Andrew Clark’s revamped version of this notebook. He clustered the data points on the map, and published the notebook on RPubs. Note differences in render quality, and how much less cluttered the map now is. What you cannot now see at first glance though, is the top locations of herd sizes. But if you mainly want to drill down to specific observations, then clusterOptions = markerClusterOptions() is definitely the way to go on a leaflet map.

LS0tCnRpdGxlOiAiV2lsZCBib2FycyBhbW9uZ3N0IHVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzdGFydCwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobmFuaWFyKQpsaWJyYXJ5KGR5Z3JhcGhzKQpsaWJyYXJ5KGxlYWZsZXQpCmxpYnJhcnkoeHRzKQpgYGAKCiFbUGhvdG8gYnkgVmFsZW50aW4gUGFuemlyc2NoLCBXaWtpcGVkaWFdKGh0dHBzOi8vdXBsb2FkLndpa2ltZWRpYS5vcmcvd2lraXBlZGlhL2NvbW1vbnMvdGh1bWIvZC9kMS8yMDE2MDIwODA1NDk0OSUyMVdpbGRzY2hlaW4lMkNfTiVDMyVBNGhlX1B1bHZlcnN0YW1wZnRvcl8lMjhjcm9wcGVkJTI5LmpwZy84MDBweC0yMDE2MDIwODA1NDk0OSUyMVdpbGRzY2hlaW4lMkNfTiVDMyVBNGhlX1B1bHZlcnN0YW1wZnRvcl8lMjhjcm9wcGVkJTI5LmpwZykgCgojIyBTd2VkZW4KCkluc3BpcmVkIGJ5IHJlY2VudCBuZXdzIG9uIGEgW2ZvcnRjb21pbmcgYm9vayBieSBIYW5uYWggTHV0el0oaHR0cHM6Ly93d3cuaGJsLmZpL2FydGlrZWwvdmlsZHN2aW4tc29tLXZhZy10aWxsLXNqYWx2a2FubmVkb20vKSwgYW5kIHRoZSBub3RlYm9vayBvbiBbUmFjY29vbiBkb2cgaW4gU3dlZGVuXShodHRwczovL2dpdGh1Yi5jb20vY2hyaXN0b3BoZXJrdWxsZW5iZXJnL0FuYWx5c2lzcG9ydGFsTm90ZWJvb2tzL2Jsb2IvbWFzdGVyL21hcmRodW5kLmlweW5iKSBieSBDaHJpc3RvcGhlciBLdWxsZW5iZXJnLCBJIGRlY2lkZWQgdG8gY2hlY2sgd2hhdCBkbyB3ZSBrbm93IGFib3V0IHRoZSB3aWxkIGJvYXIgcG9wdWxhdGlvbiAodmlsZHN2aW4gaW4gU3dlZGlzaCwgdmlsbGlzaWthIGluIEZpbm5pc2gpIGluIFN3ZWRlbiBhbmQgRmlubGFuZC4gCgpCYXNlZCBvbiBhIGZldyBzdGF5cyBpbiBJdGFsaWFuIGFuZCBGcmVuY2ggY291bnRyeXNpZGUsIEkgY2FuIGNvbmZpcm0gdGhhdCB5ZXMsIHRoZXJlIGluZGVlZCBhcmUgd2lsZCBib2FycyBpbiBtYWlubGFuZCBFdXJvcGUuIEluIEZpbmxhbmQsIEkgaGF2ZW4ndCBzZWVuIGEgc2luZ2xlIG9uZSB5ZXQgbXlzZWxmLiBCdXQgdGhleSBhcmUgaGVyZSwgYm90aCBkb21lc3RpY2F0ZWQgKG9yIHpvbykgYW5pbWFscyBhbmQgd2lsZCBvbmVzLiBUaGUgd2lsZCB3aWxkIGJvYXJzIGFyZSBtb3N0bHkgYXJyaXZlZCBmcm9tIFJ1c3NpYSwgb3IgZXNjYXBlZCBmcm9tIGZhcm1zLiAKCldoaWxlIGF0IGl0LCBJIGRlY2lkZWQgdG8gdGVzdCB0aGUgbmV3IFIgTm90ZWJvb2sgZmVhdHVyZSBvZiBSU3R1ZGlvLiBIYXZpbmcgZG9uZSBhIGxpdHRsZSBSIE1hcmtkb3duJ2luZyBiZWZvcmUsIHdhdGNoaW5nIFthIGhlbHBmdWwgNDUgbWluIHZpZGVvIHR1dG9yaWFsXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvd2ViaW5hcnMvaW50cm9kdWNpbmctbm90ZWJvb2tzLXdpdGgtci1tYXJrZG93bi8pIGdhdmUgYSBoZWFkIHN0YXJ0LgoKRmlyc3QgSSBmb2xsb3dlZCBDaHJpc3RvcGhlcidzIHN0ZXBzIGFuZCB3ZW50IHRvIHRoZSBTd2VkaXNoIFtUaGUgQW5hbHlzaXMgcG9ydGFsIGZvciBiaW9kaXZlcnNpdHkgZGF0YV0oaHR0cHM6Ly93d3cuYW5hbHlzaXNwb3J0YWwuc2UpIGZvciBjaXRpemVuIHNjaWVuY2UgZGF0YSBvbiAqU3VzIHNjcm9mYSouCgpgYGB7ciBsb2FkLCB3YXJuaW5nPUZBTFNFfQpkYXRhX3NlIDwtIHJlYWRfZXhjZWwoIlNwZWNpZXNPYnNlcnZhdGlvbnMueGxzeCIsIHNoZWV0ID0gIlNMVyBEYXRhIikKZGF0YV9zZSRTdGFydCA8LSBhcy5EYXRlKGRhdGFfc2UkU3RhcnQsIGZvcm1hdCA9ICIlWS0lbS0lZCIpCgpucm93KGRhdGFfc2UpCmNvbG5hbWVzKGRhdGFfc2UpCm1pbihkYXRhX3NlJFN0YXJ0KQptYXgoZGF0YV9zZSRTdGFydCkKYGBgCgpUaGUgbnVtYmVyIG9mIGRpZmZlcmVudCB2YXJpYWJsZXMgaXMgaW1wcmVzc2l2ZSwgYW5kIG92ZXIgMTYwMCBvYnNlcnZhdGlvbnMgcXVpdGUgYW4gYWNoaWV2ZW1lbnQuIFRoZSB0aW1lIHNwYW4gaXMgb3ZlciAzMCB5ZWFycy4KCllldCwgdGhlcmUgYXJlIGFsc28gc2VlbWluZ2x5IG1hbnkgZW1wdHkgY2VsbHMuIFdoYXQgZGF0YSBzaG91bGQgSSBleHBlY3QgTk9UIHRvIGZpbmQ/IFRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLCB0aGUgYnJhbmQgbmV3IFtuYW5pYXIgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL25qdGllcm5leS9uYW5pYXIpIGlzIG9mIGdyZWF0IGhlbHAuIAoKTGV0J3MgdGFrZSB0aGUgZmlyc3QgaGFsZiBvZiB0aGUgY29sdW1ucyBmaXJzdCwgYW5kIHNvcnQgdGhlbSBieSBtaXNzaW5nbmVzcy4KCmBgYHtyIG1pc3NpbmcxfQp2aXNfbWlzcyhkYXRhX3NlWyAsIDE6YXMuaW50ZWdlcihuY29sKGRhdGFfc2UpLzIpXSwgc29ydF9taXNzID0gVFJVRSkgKyBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDQ4LCAKICAgICAgICB2anVzdCA9IDEsIGhqdXN0ID0gMSwgc2l6ZSA9IDcpKQoKYGBgCgpBbmQgdGhlbiB0aGUgcmVzdC4KCmBgYHtyIG1pc3NpbmcyfQp2aXNfbWlzcyhkYXRhX3NlWyAsIGFzLmludGVnZXIobmNvbChkYXRhX3NlKS8yKzEpOm5jb2woZGF0YV9zZSldLCBzb3J0X21pc3MgPSBUUlVFKSArIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KGFuZ2xlID0gNDgsIAogICAgICAgIHZqdXN0ID0gMSwgaGp1c3QgPSAxLCBzaXplID0gNykpCmBgYAoKSXQgbG9va3MgbGlrZSB0aGUgZGF0YXNldCBpcyBhIGxpdHRsZSBwYXRjaHkgb24gZXh0cmEgaW5mb3JtYXRpb24gbGlrZSB3aGF0IHRoZSBhbmltYWxzIHdlcmUgZG9pbmcsIGdlbmRlciBldGMuIFRoaXMgaXMgdW5kZXJzdGFuZGFibGUuIE9uIHRoZSBvdGhlciBoYW5kLCB0aGUgY29yZSBkZXRhaWxzIGlmIHlvdSBsaWtlIHN1Y2ggYXMgbG9jYXRpb24gYW5kIGRhdGUgYXJlIHdlbGwgcmVwcmVzZW50ZWQuIEknZCBndWVzcyB0aGF0IHNvbWUgb2YgdGhlc2UgZmllbGRzIGFyZSBtYW5kYXRvcnkuCgpOZXh0LCBsZXQncyBjYWxjdWxhdGUgZGFpbHkgc3RhdGlzdGljcywgYW5kIHBsb3QgdGhlbS4KCmBgYHtyIHRpbWVsaW5lfQoKZGFpbHlzdW1zIDwtIGRhdGFfc2UgJT4lCiAgcmVuYW1lKERhdGUgPSBTdGFydCkgJT4lIAogIGdyb3VwX2J5KERhdGUpICU+JSAKICBzdW1tYXJpc2UoT2JzZXJ2YXRpb25zID0gbigpKSAKICAKZGFpbHlzdW1zIDwtIGFzLmRhdGEuZnJhbWUoZGFpbHlzdW1zLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIFBhZCBkYXRlcwphbGx0aW1lIDwtIHNlcS5EYXRlKG1pbihkYWlseXN1bXMkRGF0ZSksIG1heChkYWlseXN1bXMkRGF0ZSksIGJ5PSJkYXkiKQphbGx0aW1lX2RmIDwtIGRhdGEuZnJhbWUoRGF0ZSA9IGFsbHRpbWUpCmFsbHRpbWVfZGFpbHlzdW1zIDwtIGZ1bGxfam9pbihhbGx0aW1lX2RmLCBkYWlseXN1bXMsIGJ5PWMoIkRhdGUiPSJEYXRlIikpCmFsbHRpbWVfZGFpbHlzdW1zJE9ic2VydmF0aW9ucyA8LSBpZmVsc2UoaXMubmEoYWxsdGltZV9kYWlseXN1bXMkT2JzZXJ2YXRpb25zKSwgMCwgYWxsdGltZV9kYWlseXN1bXMkT2JzZXJ2YXRpb25zKQoKcm93bmFtZXMoYWxsdGltZV9kYWlseXN1bXMpIDwtIGFsbHRpbWVfZGFpbHlzdW1zW1sxXV0KCmRhaWx5c3Vtcy54dHMgPC0gYXMueHRzKGFsbHRpbWVfZGFpbHlzdW1zLCBkYXRlRm9ybWF0ID0gIkRhdGUiKQoKZHlncmFwaChkYWlseXN1bXMueHRzKSAlPiUKICBkeUhpZ2hsaWdodChoaWdobGlnaHRDaXJjbGVTaXplID0gNSwgCiAgICAgICAgICAgICAgaGlnaGxpZ2h0U2VyaWVzQmFja2dyb3VuZEFscGhhID0gMC4yLAogICAgICAgICAgICAgIGhpZGVPbk1vdXNlT3V0ID0gRkFMU0UpICU+JSAKICAjIGh0dHBzOi8vd3d3LnRpbWVhbmRkYXRlLmNvbS9ob2xpZGF5cy9zd2VkZW4vbWlkc3VtbWVyLWRheQogIGR5RXZlbnQoIjIwMTAtMDYtMjYiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lIAogIGR5RXZlbnQoIjIwMTEtMDYtMjUiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lIAogIGR5RXZlbnQoIjIwMTItMDYtMjMiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lIAogIGR5RXZlbnQoIjIwMTMtMDYtMjIiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lIAogIGR5RXZlbnQoIjIwMTQtMDYtMjEiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lIAogIGR5RXZlbnQoIjIwMTUtMDYtMjAiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lIAogIGR5RXZlbnQoIjIwMTYtMDYtMjUiLCAiTWlkc3VtbWVyIiwgbGFiZWxMb2MgPSAidG9wIikgJT4lCiAgZHlFdmVudCgiMjAxMC0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUgCiAgZHlFdmVudCgiMjAxMS0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUgCiAgZHlFdmVudCgiMjAxMi0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUgCiAgZHlFdmVudCgiMjAxMy0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUgCiAgZHlFdmVudCgiMjAxNC0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUgCiAgZHlFdmVudCgiMjAxNS0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUgCiAgZHlFdmVudCgiMjAxNi0xMi0yNCIsICJDaHJpc3RtYXMiLCBsYWJlbExvYyA9ICJ0b3AiKSAlPiUKICBkeU9wdGlvbnMoc3RlcFBsb3Q9RkFMU0UsCiAgICAgICAgICAgIGNvbm5lY3RTZXBhcmF0ZWRQb2ludHMgPSBGQUxTRSwKICAgICAgICAgICAgY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsICJTZXQyIikpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoaGVpZ2h0ID0gMjApCgoKYGBgCgpUbyB6b29tIGluLCB1c2UgdGhlIHNsaWRlciwgb3IgZHJhdyBhIGhvcml6b250YWwgcmVnaW9uLiAKCkkgYWRkZWQgbWFudWFsbHkgc29tZSBob2xpZGF5IGV2ZW50cyBmcm9tIHJlY2VudCB5ZWFycy4gVGhpcyB3YXksIHdlIGNhbiBpbnZlc3RpZ2F0ZSB3aGV0aGVyIHRoZXJlIGFyZSBtb3JlIHNpZ2h0aW5ncyBhcm91bmQgTWlkc3VtbWVyIG9yIENocmlzdG1hcy4gKE5vdCkKCiMjIEZpbmxhbmQKCllvdSBjYW4gc2VhcmNoIGFuZCBkb3dubG9hZCBhbmltYWwgb2JzZXJ2YXRpb24gZGF0YSBmcm9tIHRoZSAoYmV0YSkgc2l0ZSBvZiBbRmlubmlzaCBCaW9kaXZlcnNpdHkgSW5mbyBGYWNpbGl0eSAoRmluQklGKV0oaHR0cHM6Ly9iZXRhLmxhamkuZmkvb2JzZXJ2YXRpb24vbWFwP3BhZ2U9MSkuIFRvIGdldCBkYXRhLCB5b3UgbmVlZCB0byByZWdpc3RlciwgYW5kIGxvZyBpbi4gV2hlbiB5b3UgaGF2ZSBzZWxlY3RlZCBhIHNldCwgYW4gZW1haWwgaXMgc2VudCB0byB5b3Ugd2l0aCBhIGxpbmsgdG8gdGhlIGRvd25sb2FkaW5nIHNpdGUuIFRoZXJlIGlzIGFsc28gYW4gW0FQSV0oaHR0cHM6Ly9hcGkubGFqaS5maS9leHBsb3Jlci8pLCBuaWNlbHkgZG9jdW1lbnRlZCB0aGFua3MgdG8gW09wZW5BUEkgU3BlY2lmaWNhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvT3BlbkFQSV9TcGVjaWZpY2F0aW9uKSwgYnV0IGJlY2F1c2UgSSd2ZSBnb3QgbXkgYWNjZXNzIHRva2VuIG9ubHkgcmVjZW50bHksIEkgaGF2ZW4ndCBoYWQgdGltZSB0byBjaGVjayB0aGUgZG9jdW1lbnRhdGlvbiB0aGF0IG11Y2guCgpUaGlzIGlzIG15IGRhdGFzZXQgaHR0cDovL3R1bi5maS9IQkYuQUQ4REE3NTItMTAxMy00QUEyLThCQzUtN0FBMjFGMEY0RkY1IChkb3dubG9hZGVkIDEyLjEuMjAxNykuCgpGaW5CSUYgaXMgYSBqb2luZWQgYWNoaWV2ZW1lbnQgb2Ygc2V2ZXJhbCBwYXJ0aWVzLCBzbyBkYXRhIGluY2x1ZGVzIGFsc28gaW5mb3JtYXRpb24gb24gcHJlc2VydmVkIHNwZWNpbWVucyBsaWtlIHNrdWxscy4gTGV0J3MgZmlyc3QgZmlsdGVyIG9ic2VydmF0aW9ucy4KCgpgYGB7ciBkYXRhX2ZpfQoKZGF0YV9maSA8LSByZWFkLmNzdigicm93c19IQkYuQUQ4REE3NTItMTAxMy00QUEyLThCQzUtN0FBMjFGMEY0RkY1LmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmRhdGFfZmkgPC0gZGF0YV9maSAlPiUgCiAgZmlsdGVyKFVuaXQuUmVjb3JkQmFzaXMgPT0gIkhVTUFOX09CU0VSVkFUSU9OX1VOU1BFQ0lGSUVEIiAmIEdhdGhlcmluZy5Db3VudHJ5VmVyYmF0aW0gJWluJSBjKCJTdW9taSIsICJGSSIpKQoKbnJvdyhkYXRhX2ZpKQpgYGAKCkNvbXBhcmVkIHRvIFN3ZWRpc2ggZGF0YSwgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaXMgdmVyeSBtb2Rlc3QuIEZpbmxhbmQgaXMgZmFtb3VzIGZvciBpdHMgdm9sdW50ZWVyIGJpcmR3YXRjaGVycywgYW5kIHRoZSBhbW91bnQgb2YgbG9uZ2l0dWRpbmFsIGRhdGEgZnJvbSBtb25pdG9yaW5nIGJpcmRzIGlzIG1hc3NpdmUuIEl0IGlzIGludGVyZXN0aW5nIHdoeSBvdGhlciBhbmltYWxzIGdldCBtdWNoIGxlc3MgYXR0ZW50aW9uLiBJJ2QgZ3Vlc3MgdGhvdWdoIHRoYXQgYXMgYSBjb3VudHJ5LCBGaW5sYW5kIGlzIG5vdCBhIHJlbWFya2FibGUgZXhjZXB0aW9uLiBJdCdzIGVhc3kgdG8gdW5kZXJzdGFuZCB3aHkgYmlyZGluZyBpcyBzbyBwb3B1bGFyLiBCaXJkcyBhcmUgcmVsYXRpdmVseSBlYXN5IHRvIGZpbmQgYW5kIHdhdGNoLCBhbmQgb25seSBmZXcgYXJlIG5vY3R1cm5hbC4gQWx0aG91Z2ggYXJvdW5kIDMwIHNwZWNpZXMgY2FuIGxlZ2FsbHkgYmUgaHVudGVkIGluIEZpbmxhbmQsIEknZCBzYXkgdGhhdCBpbiBnZW5lcmFsLCBiaXJkcyBhcmUgbm90IGNvbnNpZGVyZWQgZ2FtZSBsaWtlIG1hbW1hbHMuIAoKSW4gRmlubGFuZCwgdGhlIG1haW4gc291cmNlIGZvciBvYnNlcnZhdGlvbmFsLCBub24tc2NpZW50aWZpYyBkYXRhIG9uIG1hbW1hbHMgaXMgaHVudGluZyBvcmdhbml6YXRpb25zLiBJJ2QgYmUgbW9yZSB0aGFuIGhhcHB5IGlmIHRob3NlIGNpdGl6ZW5zIHdobyBkbyBub3QgaHVudCwgd291bGQgbW9yZSBhY3RpdmVseSBsb29rIGFyb3VuZCwgYW5kIHJlZ2lzdGVyIHdoYXQgdGhleSBzZWUuIAoKIyMgSm9pbmVkIG1hcAoKYGBge3IgbWFwfQoKZGF0YV9zZSA8LSBkYXRhX3NlICU+JSAKICBtdXRhdGUoTGF0ID0gYXMubnVtZXJpYyhnc3ViKCIsIiwgIi4iLCBEZWNpbWFsTGF0aXR1ZGUpKSkgJT4lIAogIG11dGF0ZShMb24gPSBhcy5udW1lcmljKGdzdWIoIiwiLCAiLiIsIERlY2ltYWxMb25naXR1ZGUpKSkgJT4lIAogIG11dGF0ZShUaW1lID0gRXZlbnRUaW1lKSAlPiUgCiAgbXV0YXRlKENvdW50ID0gaWZlbHNlKGlzLm5hKEluZGl2aWR1YWxDb3VudCksIDAsIGFzLmludGVnZXIoSW5kaXZpZHVhbENvdW50KSkpICU+JQogIG11dGF0ZShRdWFudGl0eSA9IGlmZWxzZShpcy5uYShRdWFudGl0eSksICJOL0EiLCBRdWFudGl0eSkpICU+JQogIG11dGF0ZShMaWZlU3RhZ2UgPSBpZmVsc2UoaXMubmEoTGlmZVN0YWdlKSwgIk4vQSIsIExpZmVTdGFnZSkpICU+JSAKICBtdXRhdGUoQmVoYXZpb3IgPSBpZmVsc2UoaXMubmEoQmVoYXZpb3IpLCAiTi9BIiwgQmVoYXZpb3IpKSAlPiUgCiAgbXV0YXRlKFNleCA9IGlmZWxzZShpcy5uYShTZXgpLCAiTi9BIiwgU2V4KSkgJT4lIAogIG11dGF0ZShPY2N1cnJlbmNlUmVtYXJrcyA9IGlmZWxzZShpcy5uYShPY2N1cnJlbmNlUmVtYXJrcyksICJOL0EiLCBPY2N1cnJlbmNlUmVtYXJrcykpCgpkYXRhX2ZpIDwtIGRhdGFfZmkgJT4lIAogIG11dGF0ZShHYXRoZXJpbmcuRGF0ZS5CZWdpbiA9IGFzLkRhdGUoR2F0aGVyaW5nLkRhdGUuQmVnaW4sIGZvcm1hdCA9ICIlWS0lbS0lZCIpKSAlPiUgCiAgbXV0YXRlKFVuaXQuQWJ1bmRhbmNlID0gaWZlbHNlKFVuaXQuQWJ1bmRhbmNlID09ICIiLCAiTi9BIiwgVW5pdC5BYnVuZGFuY2UpKSAlPiUKICBtdXRhdGUoR2F0aGVyaW5nLk11bmljaXBhbGl0eVZlcmJhdGltID0gaWZlbHNlKEdhdGhlcmluZy5NdW5pY2lwYWxpdHlWZXJiYXRpbSA9PSAiIiwgIk4vQSIsIEdhdGhlcmluZy5NdW5pY2lwYWxpdHlWZXJiYXRpbSkpICU+JSAKICBtdXRhdGUoR2F0aGVyaW5nLkJpb1Byb3ZpbmNlVmVyYmF0aW0gPSBpZmVsc2UoR2F0aGVyaW5nLkJpb1Byb3ZpbmNlVmVyYmF0aW0gPT0gIiIsICJOL0EiLCBHYXRoZXJpbmcuQmlvUHJvdmluY2VWZXJiYXRpbSkpICU+JSAKICBtdXRhdGUoVW5pdC5Ob3RlcyA9IGlmZWxzZShVbml0Lk5vdGVzID09ICIiLCAiTi9BIiwgVW5pdC5Ob3RlcykpCgpsZWFmbGV0KGRhdGFfc2UpICU+JSAKICBhZGRUaWxlcygpICU+JQogIGFkZENpcmNsZU1hcmtlcnMobG5nID0gfkxvbiwgbGF0ID0gfkxhdCwgcmFkaXVzID0gfnNxcnQoQ291bnQpKjIsIAogICAgICAgICAgICAgICAgICAgY29sb3I9IH5pZmVsc2UoZGF0YV9zZSRDb3VudCA+PSA0MCAsICJyZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkYXRhX3NlJENvdW50IDwgNDAgJiBkYXRhX3NlJENvdW50ID4gMjAsICJvcmFuZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmx1ZSIpKSwKICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjA4LAogICAgICAgICAgICAgICAgICAgcG9wdXAgPSBwYXN0ZShzZXAgPSAiPGJyLz4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQ291bnR5OiAiLGRhdGFfc2UkQ291bnR5KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJEYXRlOiAiLCBkYXRhX3NlJFN0YXJ0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJUaW1lOiAiLCBkYXRhX3NlJFRpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiUXVhbnRpdHk6ICIsIGRhdGFfc2UkUXVhbnRpdHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiTGlmZSBzdGFnZTogIiwgZGF0YV9zZSRMaWZlU3RhZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQmVoYXZpb3I6ICIsIGRhdGFfc2UkQmVoYXZpb3IpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIlNleDogIiwgZGF0YV9zZSRTZXgpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIlJlbWFya3M6ICIsIGRhdGFfc2UkT2NjdXJyZW5jZVJlbWFya3MpKSkgJT4lIAogIGFkZE1hcmtlcnMobG5nID0gZGF0YV9maSRHYXRoZXJpbmcuQ29udmVyc2lvbnMuV0dTODRDZW50ZXJQb2ludC5Mb24uRSwgbGF0ID0gZGF0YV9maSRHYXRoZXJpbmcuQ29udmVyc2lvbnMuV0dTODRDZW50ZXJQb2ludC5MYXQuTiwKICAgICAgICAgICAgIHBvcHVwID0gcGFzdGUoc2VwID0gIjxici8+IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQ291bnRyeTogIixkYXRhX2ZpJEdhdGhlcmluZy5Db3VudHJ5VmVyYmF0aW0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJNdW5pY2lwYWxpdHk6ICIsIGRhdGFfZmkkR2F0aGVyaW5nLk11bmljaXBhbGl0eVZlcmJhdGltKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJQcm92aW5jZTogIiwgZGF0YV9maSRHYXRoZXJpbmcuQmlvUHJvdmluY2VWZXJiYXRpbSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIkRhdGU6ICIsIGRhdGFfZmkkR2F0aGVyaW5nLkRhdGUuQmVnaW4pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJRdWFudGl0eTogIiwgZGF0YV9maSRVbml0LkFidW5kYW5jZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiUmVtYXJrczogIiwgZGF0YV9maSRVbml0Lk5vdGVzKSkpCmBgYAoKVGhlIHVzZXIgZXhwZXJpZW5jZSB3b3VsZCBiZW5lZml0IGZyb20gcmVkdWNpbmcgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBidXQgaGVyZSB3ZSBnby4KCkluIFN3ZWRlbiwgc29tZSBvZiB0aGUgZ3JvdXBzIGFyZSBzdXJwcmlzaW5nbHkgYmlnLCBldmVuIGluIHRoZSBvdXRza2lydHMgb2YgU3RvY2tob2xtISBPbiB0aGUgbWFwLCB0aGUgYmlnZ2VzdCByZWNvcmRlZCBoZXJkcyBoYXZlIGEgcmVkIHN0cm9rZSwgdGhlIG5leHQgYmlnZ2VzdCBvcmFuZ2UsIGFuZCB0aGUgcmVzdCBhcmUgYmx1ZS4KCkZyb20gdGhlIHBvcHVwcyBvZiB0aGUgcmVkIGNpcmNsZXMgd2UgbGVhcm4gdGhhdCB0aGUgYmlnZ2VyIGhlcmQsIHRoZSBtb3JlIHRoaXMgaXMgYmVjYXVzZSBvZiBmZWVkaW5nLgoKVGhlIG1vc3QgTm9ydGhlcm4gLSBhbmQgcmF0aGVyIGxvbmUgLSBvYnNlcnZhdGlvbiBpcyBuZWFyIFVtZcOlLiAKCk5vdGUgdGhhdCBpbiB0aGUgRmlubmlzaCBkYXRhc2V0LCB0aGVyZSBhcmUgZmV3IG9ic2VydmF0aW9ucyB0aGF0IGFyZSBhY3R1YWxseSBub3QgZnJvbSBGaW5sYW5kIGF0IGFsbCwgYnV0IGZyb20gRXN0b25pYSwgUG9sYW5kLCBSdXNzaWEsIGFuZCBHZXJtYW55LiBUaGVzZSBvbmVzIGhhdmUgbm8gY29vcmRpbmF0ZXMsIHNvIHRoZXkgYXJlIG5vdCBvbiB0aGUgbWFwLiAKCkVzdG9uaWEgaXMgYSBzcGVjaWFsIGNhc2UgYXQgdGhlIG1vbWVudC4gRmlubmlzaCBodW50ZXJzIHJldHVybmluZyBmcm9tIHdpbGQgYm9hciBzYWZhcmlzIGluIEVzdG9uaWEgYXJlIHVuZGVyIHN0cmljdCBjb250cm9sIGJ5IHRoZSBGaW5uaXNoIGF1dGhvcml0aWVzIGJlY2F1c2Ugb2YgW0FmcmljYW4gc3dpbmUgZmVhdmVyXShodHRwOi8vZWMuZXVyb3BhLmV1L2Zvb2QvYW5pbWFscy9hbmltYWwtZGlzZWFzZXMvY29udHJvbC1tZWFzdXJlcy9hc2Yvd2hhdC1pcy1hc2ZfZW4pLiAKCkVESVQgMjAxNy0wMS0xNjogQ2hlY2sgW0FuZHJldyBDbGFyaydzIHJldmFtcGVkIHZlcnNpb25dKGh0dHBzOi8vdC5jby9ocVhSeEZWTGVrKSBvZiB0aGlzIG5vdGVib29rLiBIZSBjbHVzdGVyZWQgdGhlIGRhdGEgcG9pbnRzIG9uIHRoZSBtYXAsIGFuZCBwdWJsaXNoZWQgdGhlIG5vdGVib29rIG9uIFtSUHVic10oaHR0cHM6Ly9ycHVicy5jb20vKS4gTm90ZSBkaWZmZXJlbmNlcyBpbiByZW5kZXIgcXVhbGl0eSwgYW5kIGhvdyBtdWNoIGxlc3MgY2x1dHRlcmVkIHRoZSBtYXAgbm93IGlzLiBXaGF0IHlvdSBjYW5ub3Qgbm93IHNlZSBhdCBmaXJzdCBnbGFuY2UgdGhvdWdoLCBpcyB0aGUgdG9wIGxvY2F0aW9ucyBvZiBoZXJkIHNpemVzLiBCdXQgaWYgeW91IG1haW5seSB3YW50IHRvIGRyaWxsIGRvd24gdG8gc3BlY2lmaWMgb2JzZXJ2YXRpb25zLCB0aGVuIGBjbHVzdGVyT3B0aW9ucyA9IG1hcmtlckNsdXN0ZXJPcHRpb25zKClgIGlzIGRlZmluaXRlbHkgdGhlIHdheSB0byBnbyBvbiBhIGxlYWZsZXQgbWFwLgo=