1 Libraries

library(tidyverse)
library(sf)
library(tmap)


2 What it tmap

  • “The short answer is that tmap is an R package for visualization spatial data. The slightly longer answer is that tmap allows users to explore, analyze, and present spatial data in a intuitive way.” (Martijn Tennekes & Jakub, 2021 Nowosad)[https://r-tmap.github.io/tmap-book/intro.html]
  • You can create static or interactive maps
  • Conceptually, it is similar to ggplot and the layered grammar of graphics

3 Data

Accidents <- st_read("Montevideo_Data/Accidentes2006-2010/accidentes2006-2010.shp")
Reading layer `accidentes2006-2010' from data source 
  `C:\Users\Orlan\Dropbox\Teaching\SpatialAnalysis\Tutorials\03_MapMaking\Montevideo_Data\Accidentes2006-2010\accidentes2006-2010.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 41121 features and 7 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 558003.5 ymin: 6134508 xmax: 588206.1 ymax: 6157857
CRS:           NA
Accidents = st_set_crs(Accidents, 32721)

Stations <- st_read("Montevideo_Data/afe_estaciones/afe_estaciones.shp")
Reading layer `afe_estaciones' from data source 
  `C:\Users\Orlan\Dropbox\Teaching\SpatialAnalysis\Tutorials\03_MapMaking\Montevideo_Data\afe_estaciones\afe_estaciones.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 105 features and 13 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -58.07525 ymin: -34.89368 xmax: -53.38024 ymax: -30.90302
Geodetic CRS:  WGS 84
Stations <- st_set_crs(Stations, 32721)
Warning: st_crs<- : replacing crs does not reproject data; use st_transform for that
Uruguay <- st_read("Montevideo_Data/Vectoriales_2011/ine_depto.shp")
Reading layer `ine_depto' from data source 
  `C:\Users\Orlan\Dropbox\Teaching\SpatialAnalysis\Tutorials\03_MapMaking\Montevideo_Data\Vectoriales_2011\ine_depto.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 20 features and 5 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 366582.2 ymin: 6127919 xmax: 858252.1 ymax: 6671738
CRS:           NA
Uruguay <- st_set_crs(Uruguay, 32721)

Bicicircuitos <- st_read(
  "Montevideo_Data/CyclingMontevideoData/v_bi_bicicircuitos/v_bi_bicicircuitos.shp")
Reading layer `v_bi_bicicircuitos' from data source 
  `C:\Users\Orlan\Dropbox\Teaching\SpatialAnalysis\Tutorials\03_MapMaking\Montevideo_Data\CyclingMontevideoData\v_bi_bicicircuitos\v_bi_bicicircuitos.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 248 features and 3 fields
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 571736.8 ymin: 6134478 xmax: 586683.5 ymax: 6147830
Projected CRS: WGS 84 / UTM zone 21S

4 Rudiments

tm_shape(Uruguay) + 
  tm_polygons()

tm_shape(Uruguay) + 
  tm_fill()

tm_shape(Uruguay) + 
  tm_fill() +
  tm_layout(bg.color = "lightblue")

tm_shape(Uruguay) + 
  tm_borders(col = "black", lwd = 0.5, lty = "dashed")

Montevideo <- Uruguay %>% filter(NOMBRE == "MONTEVIDEO")
tm_shape(Montevideo) + 
  tm_polygons() +
  tm_shape(Accidents) + tm_bubbles()

tm_shape(Montevideo) + 
  tm_polygons() +
  tm_shape(Accidents) + tm_bubbles() +
  tm_layout(frame = FALSE)

MyMap <- tm_shape(Uruguay) + 
  tm_polygons(col = "deepskyblue1") 

MyMap

st_bbox(Uruguay)
     xmin      ymin      xmax      ymax 
 366582.2 6127918.5  858252.1 6671738.4 
st_bbox(Montevideo)
     xmin      ymin      xmax      ymax 
 551982.7 6133498.9  589227.2 6159810.3 
tm_shape(Uruguay, bbox = st_bbox(Montevideo)) + 
  tm_polygons(col = "deepskyblue1") 

Nort Carolina SIDS data

nc = st_read(system.file("shape/nc.shp", package="sf"))
Reading layer `nc' from data source 
  `C:\Users\Orlan\AppData\Local\R\win-library\4.2\sf\shape\nc.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 100 features and 14 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
Geodetic CRS:  NAD27
tm_shape(nc) + tm_polygons(col = "BIR79")

tm_shape(nc) + tm_fill(col = "BIR79")

tm_shape(nc) + tm_fill(col = "BIR79") + tm_borders()



5 The road to a map

tm_shape(nc) + tm_polygons(col = "BIR79")

tm_shape(nc) + tm_polygons(col = "BIR79") + 
  tm_compass() + tm_scale_bar()

5.1 Legends - tm_layout function

tm_shape(nc) + tm_polygons(col="BIR79") + 
   tm_layout(legend.outside = TRUE, frame = FALSE)

tm_shape(nc) + tm_polygons(col="BIR79") + 
    tm_layout(legend.position = c(0.2, 0.03),
              scale = 0.8,
              legend.frame = T)

tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T)

tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(title = "THI IS MY TITLE",
              legend.position = c(0.01, 0.05),
              scale = 0.9,
              legend.frame = T) + 
   tm_add_legend(type = c("fill"),
                labels = c("Some Area", "Other Area"), 
                col = c("#66c2a5", "#e5c494"),
                title = "Other Areas") +
  tm_add_legend(type = "symbol", 
                labels = c("A point", "Other Point"), 
                col = c("#e78ac3", "grey"))

5.2 Where is the south?

But coming back to our map, it needs a compass so your audience know where noth and east are.

tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T) + 
    tm_compass() 

NA
tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T) + 
    tm_compass(size = 5, type = "8star") 

NA
tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T) + 
    tm_compass(size = 5, type = "rose", lwd = 3, show.labels = 0) 

5.3 The scale

tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T) + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar()

tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T) + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0, 50, 100, 150, 200), text.size = 2, position = c(0.06, 0))

Documentation for the tm_layout() function Documentation for the tm_compass() function Documentation for the tm_scale_bar() function

tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T) + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0, 50, 100, 150, 200), text.size = 1, position = c(0.06, 0),
                 lwd = 5, color.dark = "darkgreen", color.light = "blue")

MyMap <- tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84") + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0, 50, 100, 150, 200), text.size = 1, position = c(0.06, 0),
                 lwd = 3)
MyMap

5.4 Mode: View

tmap_mode("view")
tmap mode set to interactive viewing
MyMap
Compass not supported in view mode.
legend.postion is used for plot mode. Use view.legend.position in tm_view to set the legend position in view mode.
Warning: In view mode, scale bar breaks are ignored.
tmap_mode("plot")
tmap mode set to plotting

5.5 Colors

tm_shape(nc) + tm_polygons(col="BIR79", title = "Births 1979 - 84", palette = "Blues") + 
    tm_layout(legend.position = c(0.01,0.65),
              scale=0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0,50,100,150,200), text.size = 1, position = c(0.06,0),
                 lwd = 3)

tm_shape(nc) + 
    tm_polygons(col="BIR79", title = "Births 1979 - 84", palette = "Blues",  n = 4) + 
    tm_layout(legend.position = c(0.01,0.65),
              scale=0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0,50,100,150,200), text.size = 1, position = c(0.06,0),
                 lwd = 3)

tm_shape(nc) + 
    tm_polygons(col="BIR79", title = "Births 1979 - 84", palette = "Blues", 
                style = "pretty") + 
    tm_layout(legend.position = c(0.01,0.65),
              scale=0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0,50,100,150,200), text.size = 1, position = c(0.06,0),
                 lwd = 3)

library(RColorBrewer)
display.brewer.all()

tm_shape(nc) + tm_polygons(col="BIR79", title = "Births 1979 - 84", palette = "YlGnBu") + 
    tm_layout(legend.position = c(0.01,0.65),
              scale=0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0,50,100,150,200),size = 1, position = c(0.06,0),
                 lwd = 3)
Warning: The argument size of tm_scale_bar is deprecated. It has been renamed to text.size

tm_shape(nc) + tm_polygons(col="BIR79", title = "Births 1979 - 84", palette = "-YlGnBu") + 
    tm_layout(legend.position = c(0.01,0.65),
              scale=0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0,50,100,150,200), text.size = 1, position = c(0.06,0),
                 lwd = 3)

summary(nc$BIR79)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    319    1336    2636    4224    4889   30757 
nc %>% ggplot(aes(x=BIR79)) + geom_histogram()

Breaks <- c(0, 2000, 4000, 6000, 31000)
Labels <- c("0 - 2000", "2000 - 4000", "4000 - 6000", ">6000")
tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84", palette = "YlGnBu",
                           breaks = Breaks, labels = Labels) + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0, 50, 100, 150, 200), text.size = 1, position = c(0.06, 0),
                 lwd = 3)

MyPalette <- c("#f2f0f7", "#cbc9e2", "#9e9ac8", "#6a51a3")
tm_shape(nc) + tm_polygons(col = "BIR79", title = "Births 1979 - 84", palette = MyPalette,
                           breaks = Breaks, labels = Labels) + 
    tm_layout(legend.position = c(0.01, 0.65),
              scale = 0.65,
              legend.frame = T,
              bg.color = "lightblue") + 
    tm_compass(size = 5, type = "rose", lwd = 3) + 
    tm_scale_bar(breaks = c(0, 50, 100, 150, 200), text.size = 1, position = c(0.06, 0),
                 lwd = 3)



6 Auxiliar map

6.1 Alternative A

MontevideoMap <- tm_shape(Montevideo) + 
  tm_polygons() +
  tm_shape(Accidents) + tm_bubbles()

UruguayMap <- tm_shape(Uruguay) + 
  tm_polygons()
library(grid)
MontevideoMap
print(UruguayMap, vp = viewport(0.17, 0.8, width = 0.25, height = 0.25))

MontevideoMap <- tm_shape(Montevideo) + 
  tm_polygons(col = "deepskyblue1") 


MontevideoNeigh <- Uruguay %>% filter(NOMBRE %in% c("SAN JOSE", "CANELONES", "MONTEVIDEO"))

AuxMap <- tm_shape(MontevideoNeigh) + tm_polygons(col = "green")
MontevideoMap
print(AuxMap, vp = viewport(0.17, 0.8, width = 0.2, height = 0.2))

Our map needs some context!

MontevideoBox <- st_bbox(Montevideo) %>% st_as_sfc() #Why not st_as_sf?
tm_shape(MontevideoBox) + tm_polygons(col = "white", border.col = "blue", lwd = 4)

#plot(st_geometry(MontevideoBox))

AuxMap <- tm_shape(MontevideoNeigh) + tm_polygons(col = "green") + 
  tm_shape(MontevideoBox) + tm_polygons(border.col = "red", lwd = 2.5, alpha = 0.3)
  

MontevideoMap
print(AuxMap, vp = viewport(0.17, 0.8, width = 0.2, height = 0.2))

6.2 Alternative B

See this link



7 Faceted maps

tm_shape(Montevideo) + tm_polygons() +
  tm_shape(Accidents) + tm_dots(alpha = 0.5) + tm_facets(by = "ANIO")

tm_shape(Montevideo) + tm_polygons() +
  tm_shape(Accidents) + tm_dots(alpha = 0.5) + tm_facets(by = "ANIO", nrow = 1)

tm_shape(Montevideo) + tm_polygons() +
  tm_shape(Accidents) + tm_dots(alpha = 0.5) + tm_facets(by = "ANIO", ncol = 1)

Accidents2006 <- Accidents %>% filter(ANIO == "2006")
Accidents2010 <- Accidents %>% filter(ANIO == "2010")

Complements <- tm_compass() + tm_scale_bar()

Map2006 <- tm_shape(Accidents2006) + tm_dots() + Complements
Map2010 <- tm_shape(Accidents2010) + tm_dots() + Complements
tmap_arrange(Map2006, Map2010)

8 Export

LS0tDQp0aXRsZTogInRtYXAgLSB0dXRvcmlhbCINCmF1dGhvcjogIk9ybGFuZG8gU2Fib2dhbC1DYXJkb25hIg0KZGF0ZTogIlN1bW1lciAyMDIzIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IHRydWUNCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQotLS0NCg0KIyBMaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHRtYXApDQpgYGANCg0KPGJyLz4NCjxoci8+DQoNCiMgV2hhdCBpdCB0bWFwDQoNCi0gIlRoZSBzaG9ydCBhbnN3ZXIgaXMgdGhhdCB0bWFwIGlzIGFuIFIgcGFja2FnZSBmb3IgdmlzdWFsaXphdGlvbiBzcGF0aWFsIGRhdGEuIFRoZSBzbGlnaHRseSBsb25nZXIgYW5zd2VyIGlzIHRoYXQgdG1hcCBhbGxvd3MgdXNlcnMgdG8gZXhwbG9yZSwgYW5hbHl6ZSwgYW5kIHByZXNlbnQgc3BhdGlhbCBkYXRhIGluIGEgaW50dWl0aXZlIHdheS4iIChNYXJ0aWpuIFRlbm5la2VzICYgSmFrdWIsIDIwMjEgTm93b3NhZClbaHR0cHM6Ly9yLXRtYXAuZ2l0aHViLmlvL3RtYXAtYm9vay9pbnRyby5odG1sXQ0KLSBZb3UgY2FuIGNyZWF0ZSBzdGF0aWMgb3IgaW50ZXJhY3RpdmUgbWFwcw0KLSBDb25jZXB0dWFsbHksIGl0IGlzIHNpbWlsYXIgdG8gZ2dwbG90IGFuZCB0aGUgKmxheWVyZWQgZ3JhbW1hciBvZiBncmFwaGljcyoNCg0KIyBEYXRhDQoNCmBgYHtyfQ0KQWNjaWRlbnRzIDwtIHN0X3JlYWQoIk1vbnRldmlkZW9fRGF0YS9BY2NpZGVudGVzMjAwNi0yMDEwL2FjY2lkZW50ZXMyMDA2LTIwMTAuc2hwIikNCkFjY2lkZW50cyA9IHN0X3NldF9jcnMoQWNjaWRlbnRzLCAzMjcyMSkNCg0KU3RhdGlvbnMgPC0gc3RfcmVhZCgiTW9udGV2aWRlb19EYXRhL2FmZV9lc3RhY2lvbmVzL2FmZV9lc3RhY2lvbmVzLnNocCIpDQpTdGF0aW9ucyA8LSBzdF9zZXRfY3JzKFN0YXRpb25zLCAzMjcyMSkNCg0KVXJ1Z3VheSA8LSBzdF9yZWFkKCJNb250ZXZpZGVvX0RhdGEvVmVjdG9yaWFsZXNfMjAxMS9pbmVfZGVwdG8uc2hwIikNClVydWd1YXkgPC0gc3Rfc2V0X2NycyhVcnVndWF5LCAzMjcyMSkNCg0KQmljaWNpcmN1aXRvcyA8LSBzdF9yZWFkKA0KICAiTW9udGV2aWRlb19EYXRhL0N5Y2xpbmdNb250ZXZpZGVvRGF0YS92X2JpX2JpY2ljaXJjdWl0b3Mvdl9iaV9iaWNpY2lyY3VpdG9zLnNocCIpDQoNCmBgYA0KDQojIFJ1ZGltZW50cw0KDQpgYGB7cn0NCnRtX3NoYXBlKFVydWd1YXkpICsgDQogIHRtX3BvbHlnb25zKCkNCmBgYA0KDQpgYGB7cn0NCnRtX3NoYXBlKFVydWd1YXkpICsgDQogIHRtX2ZpbGwoKQ0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUoVXJ1Z3VheSkgKyANCiAgdG1fZmlsbCgpICsNCiAgdG1fbGF5b3V0KGJnLmNvbG9yID0gImxpZ2h0Ymx1ZSIpDQpgYGANCmBgYHtyfQ0KdG1fc2hhcGUoVXJ1Z3VheSkgKyANCiAgdG1fYm9yZGVycyhjb2wgPSAiYmxhY2siLCBsd2QgPSAwLjUsIGx0eSA9ICJkYXNoZWQiKQ0KYGBgDQoNCmBgYHtyfQ0KTW9udGV2aWRlbyA8LSBVcnVndWF5ICU+JSBmaWx0ZXIoTk9NQlJFID09ICJNT05URVZJREVPIikNCmBgYA0KDQpgYGB7cn0NCnRtX3NoYXBlKE1vbnRldmlkZW8pICsgDQogIHRtX3BvbHlnb25zKCkgKw0KICB0bV9zaGFwZShBY2NpZGVudHMpICsgdG1fYnViYmxlcygpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShNb250ZXZpZGVvKSArIA0KICB0bV9wb2x5Z29ucygpICsNCiAgdG1fc2hhcGUoQWNjaWRlbnRzKSArIHRtX2J1YmJsZXMoKSArDQogIHRtX2xheW91dChmcmFtZSA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KTXlNYXAgPC0gdG1fc2hhcGUoVXJ1Z3VheSkgKyANCiAgdG1fcG9seWdvbnMoY29sID0gImRlZXBza3libHVlMSIpIA0KDQpNeU1hcA0KYGBgDQoNCmBgYHtyfQ0Kc3RfYmJveChVcnVndWF5KQ0Kc3RfYmJveChNb250ZXZpZGVvKQ0KYGBgDQoNCg0KYGBge3J9DQp0bV9zaGFwZShVcnVndWF5LCBiYm94ID0gc3RfYmJveChNb250ZXZpZGVvKSkgKyANCiAgdG1fcG9seWdvbnMoY29sID0gImRlZXBza3libHVlMSIpIA0KYGBgDQoNCg0KW05vcnQgQ2Fyb2xpbmEgU0lEUyBkYXRhXShodHRwczovL25vd29zYWQuZ2l0aHViLmlvL3NwRGF0YS9yZWZlcmVuY2UvbmMuc2lkcy5odG1sKQ0KDQpgYGB7cn0NCm5jID0gc3RfcmVhZChzeXN0ZW0uZmlsZSgic2hhcGUvbmMuc2hwIiwgcGFja2FnZT0ic2YiKSkNCmBgYA0KDQpgYGB7cn0NCnRtX3NoYXBlKG5jKSArIHRtX3BvbHlnb25zKGNvbCA9ICJCSVI3OSIpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9maWxsKGNvbCA9ICJCSVI3OSIpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9maWxsKGNvbCA9ICJCSVI3OSIpICsgdG1fYm9yZGVycygpDQpgYGANCg0KPGJyLz4NCjxoci8+DQoNCiMgVGhlIHJvYWQgdG8gYSBtYXANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiKQ0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sID0gIkJJUjc5IikgKyANCiAgdG1fY29tcGFzcygpICsgdG1fc2NhbGVfYmFyKCkNCmBgYA0KDQojIyBMZWdlbmRzIC0gdG1fbGF5b3V0IGZ1bmN0aW9uDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sPSJCSVI3OSIpICsgDQogICB0bV9sYXlvdXQobGVnZW5kLm91dHNpZGUgPSBUUlVFLCBmcmFtZSA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sPSJCSVI3OSIpICsgDQogICAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLCAwLjAzKSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjgsDQogICAgICAgICAgICAgIGxlZ2VuZC5mcmFtZSA9IFQpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IikgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBUKQ0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sID0gIkJJUjc5IiwgdGl0bGUgPSAiQmlydGhzIDE5NzkgLSA4NCIpICsgDQogICAgdG1fbGF5b3V0KHRpdGxlID0gIlRISSBJUyBNWSBUSVRMRSIsDQogICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMSwgMC4wNSksDQogICAgICAgICAgICAgIHNjYWxlID0gMC45LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBUKSArIA0KICAgdG1fYWRkX2xlZ2VuZCh0eXBlID0gYygiZmlsbCIpLA0KICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlNvbWUgQXJlYSIsICJPdGhlciBBcmVhIiksIA0KICAgICAgICAgICAgICAgIGNvbCA9IGMoIiM2NmMyYTUiLCAiI2U1YzQ5NCIpLA0KICAgICAgICAgICAgICAgIHRpdGxlID0gIk90aGVyIEFyZWFzIikgKw0KICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAic3ltYm9sIiwgDQogICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQSBwb2ludCIsICJPdGhlciBQb2ludCIpLCANCiAgICAgICAgICAgICAgICBjb2wgPSBjKCIjZTc4YWMzIiwgImdyZXkiKSkNCmBgYA0KDQoNCiMjIFdoZXJlIGlzIHRoZSBzb3V0aD8NCg0KQnV0IGNvbWluZyBiYWNrIHRvIG91ciBtYXAsIGl0IG5lZWRzIGEgY29tcGFzcyBzbyB5b3VyIGF1ZGllbmNlIGtub3cgd2hlcmUgbm90aCBhbmQgZWFzdCBhcmUuDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sID0gIkJJUjc5IiwgdGl0bGUgPSAiQmlydGhzIDE5NzkgLSA4NCIpICsgDQogICAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMSwgMC42NSksDQogICAgICAgICAgICAgIHNjYWxlID0gMC42NSwNCiAgICAgICAgICAgICAgbGVnZW5kLmZyYW1lID0gVCkgKyANCiAgICB0bV9jb21wYXNzKCkgDQogIA0KYGBgDQoNCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IikgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBUKSArIA0KICAgIHRtX2NvbXBhc3Moc2l6ZSA9IDUsIHR5cGUgPSAiOHN0YXIiKSANCiAgDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IikgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBUKSArIA0KICAgIHRtX2NvbXBhc3Moc2l6ZSA9IDUsIHR5cGUgPSAicm9zZSIsIGx3ZCA9IDMsIHNob3cubGFiZWxzID0gMCkgDQpgYGANCg0KDQojIyBUaGUgc2NhbGUNCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IikgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBUKSArIA0KICAgIHRtX2NvbXBhc3Moc2l6ZSA9IDUsIHR5cGUgPSAicm9zZSIsIGx3ZCA9IDMpICsgDQogICAgdG1fc2NhbGVfYmFyKCkNCmBgYA0KDQpgYGB7cn0NCnRtX3NoYXBlKG5jKSArIHRtX3BvbHlnb25zKGNvbCA9ICJCSVI3OSIsIHRpdGxlID0gIkJpcnRocyAxOTc5IC0gODQiKSArIA0KICAgIHRtX2xheW91dChsZWdlbmQucG9zaXRpb24gPSBjKDAuMDEsIDAuNjUpLA0KICAgICAgICAgICAgICBzY2FsZSA9IDAuNjUsDQogICAgICAgICAgICAgIGxlZ2VuZC5mcmFtZSA9IFQpICsgDQogICAgdG1fY29tcGFzcyhzaXplID0gNSwgdHlwZSA9ICJyb3NlIiwgbHdkID0gMykgKyANCiAgICB0bV9zY2FsZV9iYXIoYnJlYWtzID0gYygwLCA1MCwgMTAwLCAxNTAsIDIwMCksIHRleHQuc2l6ZSA9IDIsIHBvc2l0aW9uID0gYygwLjA2LCAwKSkNCmBgYA0KDQpEb2N1bWVudGF0aW9uIGZvciB0aGUgW3RtX2xheW91dCgpIGZ1bmN0aW9uXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvdG1hcC92ZXJzaW9ucy8yLjIvdG9waWNzL3RtX2xheW91dCkNCkRvY3VtZW50YXRpb24gZm9yIHRoZSBbdG1fY29tcGFzcygpIGZ1bmN0aW9uXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvdG1hcC92ZXJzaW9ucy8yLjIvdG9waWNzL3RtX2NvbXBhc3MpDQpEb2N1bWVudGF0aW9uIGZvciB0aGUgW3RtX3NjYWxlX2JhcigpIGZ1bmN0aW9uXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvdG1hcC92ZXJzaW9ucy8yLjIvdG9waWNzL3RtX3NjYWxlX2JhcikNCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IikgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBUKSArIA0KICAgIHRtX2NvbXBhc3Moc2l6ZSA9IDUsIHR5cGUgPSAicm9zZSIsIGx3ZCA9IDMpICsgDQogICAgdG1fc2NhbGVfYmFyKGJyZWFrcyA9IGMoMCwgNTAsIDEwMCwgMTUwLCAyMDApLCB0ZXh0LnNpemUgPSAxLCBwb3NpdGlvbiA9IGMoMC4wNiwgMCksDQogICAgICAgICAgICAgICAgIGx3ZCA9IDUsIGNvbG9yLmRhcmsgPSAiZGFya2dyZWVuIiwgY29sb3IubGlnaHQgPSAiYmx1ZSIpDQpgYGANCg0KYGBge3J9DQpNeU1hcCA8LSB0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2wgPSAiQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IikgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBULA0KICAgICAgICAgICAgICBiZy5jb2xvciA9ICJsaWdodGJsdWUiKSArIA0KICAgIHRtX2NvbXBhc3Moc2l6ZSA9IDUsIHR5cGUgPSAicm9zZSIsIGx3ZCA9IDMpICsgDQogICAgdG1fc2NhbGVfYmFyKGJyZWFrcyA9IGMoMCwgNTAsIDEwMCwgMTUwLCAyMDApLCB0ZXh0LnNpemUgPSAxLCBwb3NpdGlvbiA9IGMoMC4wNiwgMCksDQogICAgICAgICAgICAgICAgIGx3ZCA9IDMpDQoNCmBgYA0KDQpgYGB7cn0NCk15TWFwDQpgYGANCg0KIyMgTW9kZTogVmlldw0KDQpgYGB7cn0NCnRtYXBfbW9kZSgidmlldyIpDQpNeU1hcA0KYGBgDQoNCg0KYGBge3J9DQp0bWFwX21vZGUoInBsb3QiKQ0KYGBgDQoNCiMjIENvbG9ycw0KDQpgYGB7cn0NCnRtX3NoYXBlKG5jKSArIHRtX3BvbHlnb25zKGNvbD0iQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IiwgcGFsZXR0ZSA9ICJCbHVlcyIpICsgDQogICAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMSwwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGU9MC42NSwNCiAgICAgICAgICAgICAgbGVnZW5kLmZyYW1lID0gVCwNCiAgICAgICAgICAgICAgYmcuY29sb3IgPSAibGlnaHRibHVlIikgKyANCiAgICB0bV9jb21wYXNzKHNpemUgPSA1LCB0eXBlID0gInJvc2UiLCBsd2QgPSAzKSArIA0KICAgIHRtX3NjYWxlX2JhcihicmVha3MgPSBjKDAsNTAsMTAwLDE1MCwyMDApLCB0ZXh0LnNpemUgPSAxLCBwb3NpdGlvbiA9IGMoMC4wNiwwKSwNCiAgICAgICAgICAgICAgICAgbHdkID0gMykNCg0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgDQogICAgdG1fcG9seWdvbnMoY29sPSJCSVI3OSIsIHRpdGxlID0gIkJpcnRocyAxOTc5IC0gODQiLCBwYWxldHRlID0gIkJsdWVzIiwgIG4gPSA0KSArIA0KICAgIHRtX2xheW91dChsZWdlbmQucG9zaXRpb24gPSBjKDAuMDEsMC42NSksDQogICAgICAgICAgICAgIHNjYWxlPTAuNjUsDQogICAgICAgICAgICAgIGxlZ2VuZC5mcmFtZSA9IFQsDQogICAgICAgICAgICAgIGJnLmNvbG9yID0gImxpZ2h0Ymx1ZSIpICsgDQogICAgdG1fY29tcGFzcyhzaXplID0gNSwgdHlwZSA9ICJyb3NlIiwgbHdkID0gMykgKyANCiAgICB0bV9zY2FsZV9iYXIoYnJlYWtzID0gYygwLDUwLDEwMCwxNTAsMjAwKSwgdGV4dC5zaXplID0gMSwgcG9zaXRpb24gPSBjKDAuMDYsMCksDQogICAgICAgICAgICAgICAgIGx3ZCA9IDMpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyANCiAgICB0bV9wb2x5Z29ucyhjb2w9IkJJUjc5IiwgdGl0bGUgPSAiQmlydGhzIDE5NzkgLSA4NCIsIHBhbGV0dGUgPSAiQmx1ZXMiLCANCiAgICAgICAgICAgICAgICBzdHlsZSA9ICJwcmV0dHkiKSArIA0KICAgIHRtX2xheW91dChsZWdlbmQucG9zaXRpb24gPSBjKDAuMDEsMC42NSksDQogICAgICAgICAgICAgIHNjYWxlPTAuNjUsDQogICAgICAgICAgICAgIGxlZ2VuZC5mcmFtZSA9IFQsDQogICAgICAgICAgICAgIGJnLmNvbG9yID0gImxpZ2h0Ymx1ZSIpICsgDQogICAgdG1fY29tcGFzcyhzaXplID0gNSwgdHlwZSA9ICJyb3NlIiwgbHdkID0gMykgKyANCiAgICB0bV9zY2FsZV9iYXIoYnJlYWtzID0gYygwLDUwLDEwMCwxNTAsMjAwKSwgdGV4dC5zaXplID0gMSwgcG9zaXRpb24gPSBjKDAuMDYsMCksDQogICAgICAgICAgICAgICAgIGx3ZCA9IDMpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmRpc3BsYXkuYnJld2VyLmFsbCgpDQpgYGANCg0KDQpgYGB7cn0NCnRtX3NoYXBlKG5jKSArIHRtX3BvbHlnb25zKGNvbD0iQklSNzkiLCB0aXRsZSA9ICJCaXJ0aHMgMTk3OSAtIDg0IiwgcGFsZXR0ZSA9ICJZbEduQnUiKSArIA0KICAgIHRtX2xheW91dChsZWdlbmQucG9zaXRpb24gPSBjKDAuMDEsMC42NSksDQogICAgICAgICAgICAgIHNjYWxlPTAuNjUsDQogICAgICAgICAgICAgIGxlZ2VuZC5mcmFtZSA9IFQsDQogICAgICAgICAgICAgIGJnLmNvbG9yID0gImxpZ2h0Ymx1ZSIpICsgDQogICAgdG1fY29tcGFzcyhzaXplID0gNSwgdHlwZSA9ICJyb3NlIiwgbHdkID0gMykgKyANCiAgICB0bV9zY2FsZV9iYXIoYnJlYWtzID0gYygwLDUwLDEwMCwxNTAsMjAwKSxzaXplID0gMSwgcG9zaXRpb24gPSBjKDAuMDYsMCksDQogICAgICAgICAgICAgICAgIGx3ZCA9IDMpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShuYykgKyB0bV9wb2x5Z29ucyhjb2w9IkJJUjc5IiwgdGl0bGUgPSAiQmlydGhzIDE5NzkgLSA4NCIsIHBhbGV0dGUgPSAiLVlsR25CdSIpICsgDQogICAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMSwwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGU9MC42NSwNCiAgICAgICAgICAgICAgbGVnZW5kLmZyYW1lID0gVCwNCiAgICAgICAgICAgICAgYmcuY29sb3IgPSAibGlnaHRibHVlIikgKyANCiAgICB0bV9jb21wYXNzKHNpemUgPSA1LCB0eXBlID0gInJvc2UiLCBsd2QgPSAzKSArIA0KICAgIHRtX3NjYWxlX2JhcihicmVha3MgPSBjKDAsNTAsMTAwLDE1MCwyMDApLCB0ZXh0LnNpemUgPSAxLCBwb3NpdGlvbiA9IGMoMC4wNiwwKSwNCiAgICAgICAgICAgICAgICAgbHdkID0gMykNCmBgYA0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShuYyRCSVI3OSkNCmBgYA0KDQoNCmBgYHtyfQ0KbmMgJT4lIGdncGxvdChhZXMoeD1CSVI3OSkpICsgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCkJyZWFrcyA8LSBjKDAsIDIwMDAsIDQwMDAsIDYwMDAsIDMxMDAwKQ0KTGFiZWxzIDwtIGMoIjAgLSAyMDAwIiwgIjIwMDAgLSA0MDAwIiwgIjQwMDAgLSA2MDAwIiwgIj42MDAwIikNCmBgYA0KDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sID0gIkJJUjc5IiwgdGl0bGUgPSAiQmlydGhzIDE5NzkgLSA4NCIsIHBhbGV0dGUgPSAiWWxHbkJ1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IEJyZWFrcywgbGFiZWxzID0gTGFiZWxzKSArIA0KICAgIHRtX2xheW91dChsZWdlbmQucG9zaXRpb24gPSBjKDAuMDEsIDAuNjUpLA0KICAgICAgICAgICAgICBzY2FsZSA9IDAuNjUsDQogICAgICAgICAgICAgIGxlZ2VuZC5mcmFtZSA9IFQsDQogICAgICAgICAgICAgIGJnLmNvbG9yID0gImxpZ2h0Ymx1ZSIpICsgDQogICAgdG1fY29tcGFzcyhzaXplID0gNSwgdHlwZSA9ICJyb3NlIiwgbHdkID0gMykgKyANCiAgICB0bV9zY2FsZV9iYXIoYnJlYWtzID0gYygwLCA1MCwgMTAwLCAxNTAsIDIwMCksIHRleHQuc2l6ZSA9IDEsIHBvc2l0aW9uID0gYygwLjA2LCAwKSwNCiAgICAgICAgICAgICAgICAgbHdkID0gMykNCmBgYA0KDQpgYGB7cn0NCk15UGFsZXR0ZSA8LSBjKCIjZjJmMGY3IiwgIiNjYmM5ZTIiLCAiIzllOWFjOCIsICIjNmE1MWEzIikNCmBgYA0KDQoNCmBgYHtyfQ0KdG1fc2hhcGUobmMpICsgdG1fcG9seWdvbnMoY29sID0gIkJJUjc5IiwgdGl0bGUgPSAiQmlydGhzIDE5NzkgLSA4NCIsIHBhbGV0dGUgPSBNeVBhbGV0dGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBCcmVha3MsIGxhYmVscyA9IExhYmVscykgKyANCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjAxLCAwLjY1KSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjY1LA0KICAgICAgICAgICAgICBsZWdlbmQuZnJhbWUgPSBULA0KICAgICAgICAgICAgICBiZy5jb2xvciA9ICJsaWdodGJsdWUiKSArIA0KICAgIHRtX2NvbXBhc3Moc2l6ZSA9IDUsIHR5cGUgPSAicm9zZSIsIGx3ZCA9IDMpICsgDQogICAgdG1fc2NhbGVfYmFyKGJyZWFrcyA9IGMoMCwgNTAsIDEwMCwgMTUwLCAyMDApLCB0ZXh0LnNpemUgPSAxLCBwb3NpdGlvbiA9IGMoMC4wNiwgMCksDQogICAgICAgICAgICAgICAgIGx3ZCA9IDMpDQpgYGANCg0KPGJyLz4NCjxoci8+DQoNCiMgQXV4aWxpYXIgbWFwDQoNCiMjIEFsdGVybmF0aXZlIEENCg0KYGBge3J9DQpNb250ZXZpZGVvTWFwIDwtIHRtX3NoYXBlKE1vbnRldmlkZW8pICsgDQogIHRtX3BvbHlnb25zKCkgKw0KICB0bV9zaGFwZShBY2NpZGVudHMpICsgdG1fYnViYmxlcygpDQoNClVydWd1YXlNYXAgPC0gdG1fc2hhcGUoVXJ1Z3VheSkgKyANCiAgdG1fcG9seWdvbnMoKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShncmlkKQ0KYGBgDQoNCmBgYHtyfQ0KTW9udGV2aWRlb01hcA0KcHJpbnQoVXJ1Z3VheU1hcCwgdnAgPSB2aWV3cG9ydCgwLjE3LCAwLjgsIHdpZHRoID0gMC4yNSwgaGVpZ2h0ID0gMC4yNSkpDQpgYGANCg0KYGBge3J9DQpNb250ZXZpZGVvTWFwIDwtIHRtX3NoYXBlKE1vbnRldmlkZW8pICsgDQogIHRtX3BvbHlnb25zKGNvbCA9ICJkZWVwc2t5Ymx1ZTEiKSANCg0KDQpNb250ZXZpZGVvTmVpZ2ggPC0gVXJ1Z3VheSAlPiUgZmlsdGVyKE5PTUJSRSAlaW4lIGMoIlNBTiBKT1NFIiwgIkNBTkVMT05FUyIsICJNT05URVZJREVPIikpDQoNCkF1eE1hcCA8LSB0bV9zaGFwZShNb250ZXZpZGVvTmVpZ2gpICsgdG1fcG9seWdvbnMoY29sID0gImdyZWVuIikNCmBgYA0KDQpgYGB7cn0NCk1vbnRldmlkZW9NYXANCnByaW50KEF1eE1hcCwgdnAgPSB2aWV3cG9ydCgwLjE3LCAwLjgsIHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwLjIpKQ0KYGBgDQoNCg0KT3VyIG1hcCBuZWVkcyBzb21lIGNvbnRleHQhDQoNCmBgYHtyfQ0KTW9udGV2aWRlb0JveCA8LSBzdF9iYm94KE1vbnRldmlkZW8pICU+JSBzdF9hc19zZmMoKSAjV2h5IG5vdCBzdF9hc19zZj8NCmBgYA0KDQpgYGB7cn0NCnRtX3NoYXBlKE1vbnRldmlkZW9Cb3gpICsgdG1fcG9seWdvbnMoY29sID0gIndoaXRlIiwgYm9yZGVyLmNvbCA9ICJibHVlIiwgbHdkID0gNCkNCiNwbG90KHN0X2dlb21ldHJ5KE1vbnRldmlkZW9Cb3gpKQ0KYGBgDQoNCg0KYGBge3J9DQoNCkF1eE1hcCA8LSB0bV9zaGFwZShNb250ZXZpZGVvTmVpZ2gpICsgdG1fcG9seWdvbnMoY29sID0gImdyZWVuIikgKyANCiAgdG1fc2hhcGUoTW9udGV2aWRlb0JveCkgKyB0bV9wb2x5Z29ucyhib3JkZXIuY29sID0gInJlZCIsIGx3ZCA9IDIuNSwgYWxwaGEgPSAwLjMpDQogIA0KDQpNb250ZXZpZGVvTWFwDQpwcmludChBdXhNYXAsIHZwID0gdmlld3BvcnQoMC4xNywgMC44LCB3aWR0aCA9IDAuMiwgaGVpZ2h0ID0gMC4yKSkNCmBgYA0KDQojIyBBbHRlcm5hdGl2ZSBCDQoNCltTZWUgdGhpcyBsaW5rXShodHRwczovL2dpdGh1Yi5jb20vci10bWFwL3RtYXAvaXNzdWVzLzU0MSkNCg0KDQo8YnIvPg0KPGhyLz4NCg0KIyBGYWNldGVkIG1hcHMNCg0KYGBge3J9DQp0bV9zaGFwZShNb250ZXZpZGVvKSArIHRtX3BvbHlnb25zKCkgKw0KICB0bV9zaGFwZShBY2NpZGVudHMpICsgdG1fZG90cyhhbHBoYSA9IDAuNSkgKyB0bV9mYWNldHMoYnkgPSAiQU5JTyIpDQpgYGANCg0KYGBge3J9DQp0bV9zaGFwZShNb250ZXZpZGVvKSArIHRtX3BvbHlnb25zKCkgKw0KICB0bV9zaGFwZShBY2NpZGVudHMpICsgdG1fZG90cyhhbHBoYSA9IDAuNSkgKyB0bV9mYWNldHMoYnkgPSAiQU5JTyIsIG5yb3cgPSAxKQ0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUoTW9udGV2aWRlbykgKyB0bV9wb2x5Z29ucygpICsNCiAgdG1fc2hhcGUoQWNjaWRlbnRzKSArIHRtX2RvdHMoYWxwaGEgPSAwLjUpICsgdG1fZmFjZXRzKGJ5ID0gIkFOSU8iLCBuY29sID0gMSkNCmBgYA0KDQpgYGB7cn0NCkFjY2lkZW50czIwMDYgPC0gQWNjaWRlbnRzICU+JSBmaWx0ZXIoQU5JTyA9PSAiMjAwNiIpDQpBY2NpZGVudHMyMDEwIDwtIEFjY2lkZW50cyAlPiUgZmlsdGVyKEFOSU8gPT0gIjIwMTAiKQ0KDQpDb21wbGVtZW50cyA8LSB0bV9jb21wYXNzKCkgKyB0bV9zY2FsZV9iYXIoKQ0KDQpNYXAyMDA2IDwtIHRtX3NoYXBlKEFjY2lkZW50czIwMDYpICsgdG1fZG90cygpICsgQ29tcGxlbWVudHMNCk1hcDIwMTAgPC0gdG1fc2hhcGUoQWNjaWRlbnRzMjAxMCkgKyB0bV9kb3RzKCkgKyBDb21wbGVtZW50cw0KYGBgDQoNCmBgYHtyfQ0KdG1hcF9hcnJhbmdlKE1hcDIwMDYsIE1hcDIwMTApDQpgYGANCg0KDQojIEV4cG9ydA0KDQojIEFuaW1hdGVkIG1hcHMNCg0KU2VlIFtzZWN0aW9uIDguNSBvZiB0aGUgR2Vjb21wdXRhdGlvbiB3aXRoIFIgYm9va10oaHR0cHM6Ly9ib29rZG93bi5vcmcvcm9iaW5sb3ZlbGFjZS9nZW9jb21wci9hZHYtbWFwLmh0bWwjc3RhdGljLW1hcHMpDQoNCiMgUmVjb21tZW5kZWQgcmVhZGluZ3MNCg0KLSB0bWFwIGJvb2s6IFtFbGVnYW50IGFuZCBpbmZvcm1hdGl2ZSBtYXBzIHdpdGggdG1hcF0oaHR0cHM6Ly9yLXRtYXAuZ2l0aHViLmlvL3RtYXAtYm9vay8pDQotIENoYXB0ZXIgOCBvZiBbR2VvY29tcHV0YXRpb24gd2l0aCBSIGJvb2tdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JvYmlubG92ZWxhY2UvZ2VvY29tcHIvKQ0KLSBbdG1hcDogZ2V0IHN0YXJ0ZWQgLSB2aWduZXR0ZXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90bWFwL3ZpZ25ldHRlcy90bWFwLWdldHN0YXJ0ZWQuaHRtbCkNCg0K