In this lab, we aim to perform spatial analyses on hawker centre rental prices in Singapore. First we proceed by reading in the .csv file.

Now with base data, we can answer several questions. For instance, is there a relation between area and price?

tenders %>%
  drop_na(bidNum) %>%
  drop_na(area) %>%
  ggplot(aes(x=area, y=bidNum)) + 
  geom_point() +
  ggtitle("Scatter plot of bid price against area") + 
  ylab("Bid Price") + 
  xlab("Area")

From the plot above, it seems there there isn’t really a linear correlation between area and bid price. Perhaps the bid price is more strongly related to where the hawker centre is located. Hawker centres located in places with more human traffic could see higher bid prices.

Next we could ask, does the number of bids change over time?

tenders %>%
  drop_na(date) %>%
  group_by(date) %>%
  summarise(count = n()) %>%
  ggplot(aes(x=date, y=count)) +
  geom_col() +
  scale_x_date(date_breaks = "1 year", labels = date_format(format = "%Y")) + 
  xlab("Time") + 
  ylab("Number of bids") + 
  ggtitle("Number of bids over time")

The above chart actually surprised me. There is a clear increase in number of bids over time. This suggests a higher demand for hawker centers in Singapore (more people want to have their own stalls). Considering that Singaporeans are getting more educated, I expected less people in Singapore to be willing to work as hawkers. Perhaps, the increase in number of bids is due to immigrants rather than Singaporeans.

Building charts from this data may seem boring. Why not we add spatial data and do a spatial analysis.

library(sf)
centres <- st_read("hawker-centres/hawker-centres-kml.kml")
Reading layer `HAWKERCENTRE' from data source `/Users/arroyo/Desktop/stuff/Term 5/02221/Week 10/02221-Lab9/hawker-centres/hawker-centres-kml.kml' using driver `KML'
Simple feature collection with 116 features and 2 fields
geometry type:  POINT
dimension:      XYZ
bbox:           xmin: 103.6974 ymin: 1.272716 xmax: 103.9882 ymax: 1.443882
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
plot(centres)

centres <- centres %>% 
  mutate(centreName = str_extract(Description, "(?<=<th>NAME<\\/th> <td>).*?(?=<\\/td>)")) %>%
  mutate(centreUpper = toupper(centreName)) %>%
  mutate(lon = st_coordinates(centres)[,1]) %>%
  mutate(lat = st_coordinates(centres)[,2])
tenders.sp <- left_join(tenders, centres, by = c("centre" = "centreUpper"))

This kinda worked. But after looking through tenders.sp, it is obvious that the joining didn’t go quite as well as we hoped. This is due to inconsistent spelling of the hawker centres’ names. We shall fix that using Google’s geocoding service.

centres.unique <- tenders %>% 
  group_by(centre) %>%
  summarise(count = n()) %>%
  mutate(location = paste0(centre, ", Singapore"))
# OVER_QUERY_LIMIT, boohoo
# g <- geocode(centres.unique$location, output = "latlon", source = "google", sensor = F) 
g <- read_csv("cleaned_centres-geocoded.csv")
g <- unique(g[c("centre","lat","lon")])
g <- g[-c(43),]
centres.unique <- centres.unique[centres.unique$centre %in% g$centre,]
centres <- bind_cols(centres.unique, g)
tenders.sp <- left_join(tenders, centres, by = c("centre" = "centre"))
ggplot(tenders.sp %>% drop_na(priceM2), aes(x=lon, y=lat, size=priceM2, color=type)) +
  geom_point() +
  geom_density2d() + 
  coord_fixed() +
  ggtitle("Number of bids in each area") +
  xlab("Longtitude") + 
  ylab("Latitude")

  
ggplot(tenders.sp, aes(x=lon, y=lat)) +
  geom_point() +
  geom_hex() + 
  coord_fixed() +
  ggtitle("Number of bids in each area") +
  xlab("Longitude") + 
  ylab("Latitude")

Now that we have the spatial distribution of hawker centres in Singapore, we could do further analysis on it.

ggplot(tenders.sp, aes(x=lon, y=lat)) +
  geom_point() +
  geom_hex() + 
  facet_wrap(~type, labeller = label_wrap_gen(width = 10, multi_line = TRUE)) +
  scale_x_continuous(breaks = pretty(tenders.sp$lon, n=5)) +
  theme(axis.text.x = element_text(angle = 90), strip.text = element_text(size = 6)) +
  xlab("Longitude") +
  ylab("Latitude") + 
  ggtitle("Spatial distribution of bids for different types")

The above chart shows the spatial distribution of bids for different types of stores. The spread of bids seem to be mostly the same except for an anomaly in the case of lockup stores in the southern tip of Singapore.

ggplot(tenders.sp, aes(x=lon, y=lat)) +
  geom_point(na.rm = T) +
  geom_hex(bins=20, na.rm = T) + 
  facet_wrap(~trade, labeller = label_wrap_gen(width = 10, multi_line = FALSE)) +
  scale_x_continuous(breaks = pretty(tenders.sp$lon, n=2)) +
  theme(axis.text.x = element_text(angle = 90), strip.text = element_text(size = 6)) +
  xlab("Longitude") +
  ylab("Latitude") + 
  ggtitle("Spatial distribution of bids for different trades")

The above plot shows the spatial distribution of bids for different trades. Again, the general distribution of the number of bids look around the same for most types of trades. It is interesting though, that bids for stalls selling mutton only show up on the south of Singapore.

centres.sp <- tenders.sp %>% 
  filter(lat > 0) %>%
  group_by(centre, lon, lat) %>%
  summarise(price = mean(priceM2, na.rm = T)) %>%
  replace_na(list(price=0))
coordinates(centres.sp) <- c('lon', 'lat')
centres.ppp <- unmark(as.ppp(centres.sp))
sg <- readOGR(".", "sg-all")
OGR data source with driver: ESRI Shapefile 
Source: "/Users/arroyo/Desktop/stuff/Term 5/02221/Week 10/02221-Lab9", layer: "sg-all"
with 1 features
It has 13 fields
sg.window <- as.owin(sg)
centres.ppp <- centres.ppp[sg.window]
plot(Kest(centres.ppp))

plot(density(centres.ppp, 0.02))

contour(density(centres.ppp, 0.02))

The Kest plot shows strong evidence for clustering of hawker centers. This agrees with the density plot, where we can see that hawker centers tend to cluster towards the south of Singapore. This makes sense, since that is where the city center is.

pop <- as.im(readGDAL("sg-pop.tif"))
sg-pop.tif has GDAL driver GTiff 
and has 37 rows and 58 columns
plot(rhohat(centres.ppp, pop))

plot(rhohat(centres.ppp, pop, weights=centres.sp$price))

The above rho plots show that there is strong correlation between the population size in the region and number of hawker centers there. However, there is a sudden dip at where the population equal 15000. This is a strange anomaly.

plot(pop)
plot(centres.ppp, add=T)

Looking at the plot of where the hawker centers are, we see that the places causing the anomaly, are in Choa Chu Kang, Seng Kang and Woodlands area. These places have high population, but seemingly low number of hawker centres. After doing a quick check on Google maps, it seems that there are actually many hawker centers in these area, much higher than the numbers recorded in the data given by NEA. Checking the NEA website, we can see that there are only 114 NEA managed hawker centres in Singapore, and this data does not include many of the hawker centers in Choa Chu Kang, Seng Kang and Woodlands. Hence, we can conclude that the dip in the rho plots, is due to incomplete data. There are hawker centers there, but just not recorded.

LS0tCnRpdGxlOiAiTWFraW5nIE1hcHMgTGFiIDkiCmF1dGhvcjogIlJheXNvbiBMaW0iCmRhdGU6ICIyOSBNYXJjaCAyMDE4IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IFRSVUUKLS0tCgpJbiB0aGlzIGxhYiwgd2UgYWltIHRvIHBlcmZvcm0gc3BhdGlhbCBhbmFseXNlcyBvbiBoYXdrZXIgY2VudHJlIHJlbnRhbCBwcmljZXMgaW4gU2luZ2Fwb3JlLiBGaXJzdCB3ZSBwcm9jZWVkIGJ5IHJlYWRpbmcgaW4gdGhlIC5jc3YgZmlsZS4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoc2YpCmxpYnJhcnkoc3ApCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkoc3BhdHN0YXQpCmxpYnJhcnkoZ2dtYXApCmxpYnJhcnkobWFwdG9vbHMpCnRlbmRlcnMgPC0gcmVhZF9jc3YoInRhYnVsYS10ZW5kZXItYmlkcy1mcm9tLW1hci0yMDEyLXRvLWphbi0yMDE3LmNzdiIsIGNvbF9uYW1lcyA9IGMoImNlbnRyZSIsICJzdGFsbCIsICJhcmVhIiwgInRyYWRlIiwgImJpZCIsICJtb250aCIpKQp0ZW5kZXJzIDwtIHRlbmRlcnMgJT4lIAogIGRyb3BfbmEoY2VudHJlKSAlPiUgICMgZHJvcCBlbXB0eSByb3dzCiAgZmlsdGVyKHJvd19udW1iZXIoKSAhPSAxMTM1KSAlPiUgIyByZW1vdmUgJ2xvY2t1cCcgcm93CiAgbXV0YXRlKHR5cGUgPSBpZl9lbHNlKHJvd19udW1iZXIoKSA8IDExMzUsICJjb29rZWQiLCAibG9ja3VwIikpICU+JSAjIHNldCB0eXBlIHRvICdjb29rZWQnIGZvciBhbnl0aGluZyBwcmUtbG9ja3VwCiAgbXV0YXRlKGJpZE51bSA9IGFzLm51bWVyaWMoc3RyX3JlcGxhY2VfYWxsKGJpZCwgcGF0dGVybiA9ICJcXCR8LCIsIHJlcGxhY2VtZW50ID0gIiIpKSkgJT4lICMgY29udmVydCBwcmljZSB0byBudW1lcmljCiAgbXV0YXRlKGRhdGUgPSBkbXkocGFzdGUwKCIwMS0iLCBtb250aCkpKSAlPiUgIyBwYXJzZSBkYXRlCiAgbXV0YXRlKHByaWNlTTIgPSBiaWROdW0vYXJlYSkgIyBwcmljZSBwZXIgbTIKYGBgCgpOb3cgd2l0aCBiYXNlIGRhdGEsIHdlIGNhbiBhbnN3ZXIgc2V2ZXJhbCBxdWVzdGlvbnMuIEZvciBpbnN0YW5jZSwgaXMgdGhlcmUgYSByZWxhdGlvbiBiZXR3ZWVuIGFyZWEgYW5kIHByaWNlPwoKYGBge3J9CnRlbmRlcnMgJT4lCiAgZHJvcF9uYShiaWROdW0pICU+JQogIGRyb3BfbmEoYXJlYSkgJT4lCiAgZ2dwbG90KGFlcyh4PWFyZWEsIHk9YmlkTnVtKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdndGl0bGUoIlNjYXR0ZXIgcGxvdCBvZiBiaWQgcHJpY2UgYWdhaW5zdCBhcmVhIikgKyAKICB5bGFiKCJCaWQgUHJpY2UiKSArIAogIHhsYWIoIkFyZWEiKQpgYGAKCkZyb20gdGhlIHBsb3QgYWJvdmUsIGl0IHNlZW1zIHRoZXJlIHRoZXJlIGlzbid0IHJlYWxseSBhIGxpbmVhciBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFyZWEgYW5kIGJpZCBwcmljZS4gUGVyaGFwcyB0aGUgYmlkIHByaWNlIGlzIG1vcmUgc3Ryb25nbHkgcmVsYXRlZCB0byB3aGVyZSB0aGUgaGF3a2VyIGNlbnRyZSBpcyBsb2NhdGVkLiBIYXdrZXIgY2VudHJlcyBsb2NhdGVkIGluIHBsYWNlcyB3aXRoIG1vcmUgaHVtYW4gdHJhZmZpYyBjb3VsZCBzZWUgaGlnaGVyIGJpZCBwcmljZXMuIAoKTmV4dCB3ZSBjb3VsZCBhc2ssIGRvZXMgdGhlIG51bWJlciBvZiBiaWRzIGNoYW5nZSBvdmVyIHRpbWU/CgpgYGB7cn0KdGVuZGVycyAlPiUKICBkcm9wX25hKGRhdGUpICU+JQogIGdyb3VwX2J5KGRhdGUpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgeWVhciIsIGxhYmVscyA9IGRhdGVfZm9ybWF0KGZvcm1hdCA9ICIlWSIpKSArIAogIHhsYWIoIlRpbWUiKSArIAogIHlsYWIoIk51bWJlciBvZiBiaWRzIikgKyAKICBnZ3RpdGxlKCJOdW1iZXIgb2YgYmlkcyBvdmVyIHRpbWUiKQpgYGAKVGhlIGFib3ZlIGNoYXJ0IGFjdHVhbGx5IHN1cnByaXNlZCBtZS4gVGhlcmUgaXMgYSBjbGVhciBpbmNyZWFzZSBpbiBudW1iZXIgb2YgYmlkcyBvdmVyIHRpbWUuIFRoaXMgc3VnZ2VzdHMgYSBoaWdoZXIgZGVtYW5kIGZvciBoYXdrZXIgY2VudGVycyBpbiBTaW5nYXBvcmUgKG1vcmUgcGVvcGxlIHdhbnQgdG8gaGF2ZSB0aGVpciBvd24gc3RhbGxzKS4gQ29uc2lkZXJpbmcgdGhhdCBTaW5nYXBvcmVhbnMgYXJlIGdldHRpbmcgbW9yZSBlZHVjYXRlZCwgSSBleHBlY3RlZCBsZXNzIHBlb3BsZSBpbiBTaW5nYXBvcmUgdG8gYmUgd2lsbGluZyB0byB3b3JrIGFzIGhhd2tlcnMuIFBlcmhhcHMsIHRoZSBpbmNyZWFzZSBpbiBudW1iZXIgb2YgYmlkcyBpcyBkdWUgdG8gaW1taWdyYW50cyByYXRoZXIgdGhhbiBTaW5nYXBvcmVhbnMuIAoKQnVpbGRpbmcgY2hhcnRzIGZyb20gdGhpcyBkYXRhIG1heSBzZWVtIGJvcmluZy4gV2h5IG5vdCB3ZSBhZGQgc3BhdGlhbCBkYXRhIGFuZCBkbyBhIHNwYXRpYWwgYW5hbHlzaXMuIAoKYGBge3J9CmxpYnJhcnkoc2YpCmNlbnRyZXMgPC0gc3RfcmVhZCgiaGF3a2VyLWNlbnRyZXMvaGF3a2VyLWNlbnRyZXMta21sLmttbCIpCnBsb3QoY2VudHJlcykKY2VudHJlcyA8LSBjZW50cmVzICU+JSAKICBtdXRhdGUoY2VudHJlTmFtZSA9IHN0cl9leHRyYWN0KERlc2NyaXB0aW9uLCAiKD88PTx0aD5OQU1FPFxcL3RoPiA8dGQ+KS4qPyg/PTxcXC90ZD4pIikpICU+JQogIG11dGF0ZShjZW50cmVVcHBlciA9IHRvdXBwZXIoY2VudHJlTmFtZSkpICU+JQogIG11dGF0ZShsb24gPSBzdF9jb29yZGluYXRlcyhjZW50cmVzKVssMV0pICU+JQogIG11dGF0ZShsYXQgPSBzdF9jb29yZGluYXRlcyhjZW50cmVzKVssMl0pCgp0ZW5kZXJzLnNwIDwtIGxlZnRfam9pbih0ZW5kZXJzLCBjZW50cmVzLCBieSA9IGMoImNlbnRyZSIgPSAiY2VudHJlVXBwZXIiKSkKYGBgClRoaXMga2luZGEgd29ya2VkLiBCdXQgYWZ0ZXIgbG9va2luZyB0aHJvdWdoIHRlbmRlcnMuc3AsIGl0IGlzIG9idmlvdXMgdGhhdCB0aGUgam9pbmluZyBkaWRuJ3QgZ28gcXVpdGUgYXMgd2VsbCBhcyB3ZSBob3BlZC4gVGhpcyBpcyBkdWUgdG8gaW5jb25zaXN0ZW50IHNwZWxsaW5nIG9mIHRoZSBoYXdrZXIgY2VudHJlcycgbmFtZXMuIFdlIHNoYWxsIGZpeCB0aGF0IHVzaW5nIEdvb2dsZSdzIGdlb2NvZGluZyBzZXJ2aWNlLiAKCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQpjZW50cmVzLnVuaXF1ZSA8LSB0ZW5kZXJzICU+JSAKICBncm91cF9ieShjZW50cmUpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgbXV0YXRlKGxvY2F0aW9uID0gcGFzdGUwKGNlbnRyZSwgIiwgU2luZ2Fwb3JlIikpCgojIE9WRVJfUVVFUllfTElNSVQsIGJvb2hvbwojIGcgPC0gZ2VvY29kZShjZW50cmVzLnVuaXF1ZSRsb2NhdGlvbiwgb3V0cHV0ID0gImxhdGxvbiIsIHNvdXJjZSA9ICJnb29nbGUiLCBzZW5zb3IgPSBGKSAKZyA8LSByZWFkX2NzdigiY2xlYW5lZF9jZW50cmVzLWdlb2NvZGVkLmNzdiIpCmcgPC0gdW5pcXVlKGdbYygiY2VudHJlIiwibGF0IiwibG9uIildKQpnIDwtIGdbLWMoNDMpLF0KY2VudHJlcy51bmlxdWUgPC0gY2VudHJlcy51bmlxdWVbY2VudHJlcy51bmlxdWUkY2VudHJlICVpbiUgZyRjZW50cmUsXQpjZW50cmVzIDwtIGJpbmRfY29scyhjZW50cmVzLnVuaXF1ZSwgZykKdGVuZGVycy5zcCA8LSBsZWZ0X2pvaW4odGVuZGVycywgY2VudHJlcywgYnkgPSBjKCJjZW50cmUiID0gImNlbnRyZSIpKQoKZ2dwbG90KHRlbmRlcnMuc3AgJT4lIGRyb3BfbmEocHJpY2VNMiksIGFlcyh4PWxvbiwgeT1sYXQsIHNpemU9cHJpY2VNMiwgY29sb3I9dHlwZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fZGVuc2l0eTJkKCkgKyAKICBjb29yZF9maXhlZCgpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2YgYmlkcyBpbiBlYWNoIGFyZWEiKSArCiAgeGxhYigiTG9uZ3RpdHVkZSIpICsgCiAgeWxhYigiTGF0aXR1ZGUiKQogIApnZ3Bsb3QodGVuZGVycy5zcCwgYWVzKHg9bG9uLCB5PWxhdCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGV4KCkgKyAKICBjb29yZF9maXhlZCgpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2YgYmlkcyBpbiBlYWNoIGFyZWEiKSArCiAgeGxhYigiTG9uZ2l0dWRlIikgKyAKICB5bGFiKCJMYXRpdHVkZSIpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSB0aGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgaGF3a2VyIGNlbnRyZXMgaW4gU2luZ2Fwb3JlLCB3ZSBjb3VsZCBkbyBmdXJ0aGVyIGFuYWx5c2lzIG9uIGl0LiAKCmBgYHtyfQpnZ3Bsb3QodGVuZGVycy5zcCwgYWVzKHg9bG9uLCB5PWxhdCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGV4KCkgKyAKICBmYWNldF93cmFwKH50eXBlLCBsYWJlbGxlciA9IGxhYmVsX3dyYXBfZ2VuKHdpZHRoID0gMTAsIG11bHRpX2xpbmUgPSBUUlVFKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBwcmV0dHkodGVuZGVycy5zcCRsb24sIG49NSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpICsKICB4bGFiKCJMb25naXR1ZGUiKSArCiAgeWxhYigiTGF0aXR1ZGUiKSArIAogIGdndGl0bGUoIlNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGJpZHMgZm9yIGRpZmZlcmVudCB0eXBlcyIpCgpgYGAKVGhlIGFib3ZlIGNoYXJ0IHNob3dzIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBiaWRzIGZvciBkaWZmZXJlbnQgdHlwZXMgb2Ygc3RvcmVzLiBUaGUgc3ByZWFkIG9mIGJpZHMgc2VlbSB0byBiZSBtb3N0bHkgdGhlIHNhbWUgZXhjZXB0IGZvciBhbiBhbm9tYWx5IGluIHRoZSBjYXNlIG9mIGxvY2t1cCBzdG9yZXMgaW4gdGhlIHNvdXRoZXJuIHRpcCBvZiBTaW5nYXBvcmUuIAoKYGBge3IsIGZpZy5hc3A9MX0KZ2dwbG90KHRlbmRlcnMuc3AsIGFlcyh4PWxvbiwgeT1sYXQpKSArCiAgZ2VvbV9wb2ludChuYS5ybSA9IFQpICsKICBnZW9tX2hleChiaW5zPTIwLCBuYS5ybSA9IFQpICsgCiAgZmFjZXRfd3JhcCh+dHJhZGUsIGxhYmVsbGVyID0gbGFiZWxfd3JhcF9nZW4od2lkdGggPSAxMCwgbXVsdGlfbGluZSA9IEZBTFNFKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBwcmV0dHkodGVuZGVycy5zcCRsb24sIG49MikpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpICsKICB4bGFiKCJMb25naXR1ZGUiKSArCiAgeWxhYigiTGF0aXR1ZGUiKSArIAogIGdndGl0bGUoIlNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGJpZHMgZm9yIGRpZmZlcmVudCB0cmFkZXMiKQpgYGAKVGhlIGFib3ZlIHBsb3Qgc2hvd3MgdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGJpZHMgZm9yIGRpZmZlcmVudCB0cmFkZXMuIEFnYWluLCB0aGUgZ2VuZXJhbCBkaXN0cmlidXRpb24gb2YgdGhlIG51bWJlciBvZiBiaWRzIGxvb2sgYXJvdW5kIHRoZSBzYW1lIGZvciBtb3N0IHR5cGVzIG9mIHRyYWRlcy4gSXQgaXMgaW50ZXJlc3RpbmcgdGhvdWdoLCB0aGF0IGJpZHMgZm9yIHN0YWxscyBzZWxsaW5nIG11dHRvbiBvbmx5IHNob3cgdXAgb24gdGhlIHNvdXRoIG9mIFNpbmdhcG9yZS4gCgpgYGB7cn0KY2VudHJlcy5zcCA8LSB0ZW5kZXJzLnNwICU+JSAKICBmaWx0ZXIobGF0ID4gMCkgJT4lCiAgZ3JvdXBfYnkoY2VudHJlLCBsb24sIGxhdCkgJT4lCiAgc3VtbWFyaXNlKHByaWNlID0gbWVhbihwcmljZU0yLCBuYS5ybSA9IFQpKSAlPiUKICByZXBsYWNlX25hKGxpc3QocHJpY2U9MCkpCgpjb29yZGluYXRlcyhjZW50cmVzLnNwKSA8LSBjKCdsb24nLCAnbGF0JykKY2VudHJlcy5wcHAgPC0gdW5tYXJrKGFzLnBwcChjZW50cmVzLnNwKSkKCnNnIDwtIHJlYWRPR1IoIi4iLCAic2ctYWxsIikKc2cud2luZG93IDwtIGFzLm93aW4oc2cpCmNlbnRyZXMucHBwIDwtIGNlbnRyZXMucHBwW3NnLndpbmRvd10KCnBsb3QoS2VzdChjZW50cmVzLnBwcCkpCgpwbG90KGRlbnNpdHkoY2VudHJlcy5wcHAsIDAuMDIpKQpjb250b3VyKGRlbnNpdHkoY2VudHJlcy5wcHAsIDAuMDIpKQpgYGAKClRoZSBLZXN0IHBsb3Qgc2hvd3Mgc3Ryb25nIGV2aWRlbmNlIGZvciBjbHVzdGVyaW5nIG9mIGhhd2tlciBjZW50ZXJzLiBUaGlzIGFncmVlcyB3aXRoIHRoZSBkZW5zaXR5IHBsb3QsIHdoZXJlIHdlIGNhbiBzZWUgdGhhdCBoYXdrZXIgY2VudGVycyB0ZW5kIHRvIGNsdXN0ZXIgdG93YXJkcyB0aGUgc291dGggb2YgU2luZ2Fwb3JlLiBUaGlzIG1ha2VzIHNlbnNlLCBzaW5jZSB0aGF0IGlzIHdoZXJlIHRoZSBjaXR5IGNlbnRlciBpcy4gCgpgYGB7cn0KcG9wIDwtIGFzLmltKHJlYWRHREFMKCJzZy1wb3AudGlmIikpCnBsb3QocmhvaGF0KGNlbnRyZXMucHBwLCBwb3ApKQpgYGAKYGBge3J9CnBsb3QocmhvaGF0KGNlbnRyZXMucHBwLCBwb3AsIHdlaWdodHM9Y2VudHJlcy5zcCRwcmljZSkpCmBgYAoKVGhlIGFib3ZlIHJobyBwbG90cyBzaG93IHRoYXQgdGhlcmUgaXMgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHBvcHVsYXRpb24gc2l6ZSBpbiB0aGUgcmVnaW9uIGFuZCBudW1iZXIgb2YgaGF3a2VyIGNlbnRlcnMgdGhlcmUuIEhvd2V2ZXIsIHRoZXJlIGlzIGEgc3VkZGVuIGRpcCBhdCB3aGVyZSB0aGUgcG9wdWxhdGlvbiBlcXVhbCAxNTAwMC4gVGhpcyBpcyBhIHN0cmFuZ2UgYW5vbWFseS4KCmBgYHtyfQpwbG90KHBvcCkKcGxvdChjZW50cmVzLnBwcCwgYWRkPVQpCmBgYApMb29raW5nIGF0IHRoZSBwbG90IG9mIHdoZXJlIHRoZSBoYXdrZXIgY2VudGVycyBhcmUsIHdlIHNlZSB0aGF0IHRoZSBwbGFjZXMgY2F1c2luZyB0aGUgYW5vbWFseSwgYXJlIGluIENob2EgQ2h1IEthbmcsIFNlbmcgS2FuZyBhbmQgV29vZGxhbmRzIGFyZWEuIFRoZXNlIHBsYWNlcyBoYXZlIGhpZ2ggcG9wdWxhdGlvbiwgYnV0IHNlZW1pbmdseSBsb3cgbnVtYmVyIG9mIGhhd2tlciBjZW50cmVzLiBBZnRlciBkb2luZyBhIHF1aWNrIGNoZWNrIG9uIEdvb2dsZSBtYXBzLCBpdCBzZWVtcyB0aGF0IHRoZXJlIGFyZSBhY3R1YWxseSBtYW55IGhhd2tlciBjZW50ZXJzIGluIHRoZXNlIGFyZWEsIG11Y2ggaGlnaGVyIHRoYW4gdGhlIG51bWJlcnMgcmVjb3JkZWQgaW4gdGhlIGRhdGEgZ2l2ZW4gYnkgTkVBLiBDaGVja2luZyB0aGUgTkVBIFt3ZWJzaXRlXShodHRwOi8vd3d3Lm5lYS5nb3Yuc2cvZG9jcy9kZWZhdWx0LXNvdXJjZS9wdWJsaWMtaGVhbHRoL0hhd2tlci1DZW50cmVzLURpdmlzaW9uLS0tVGVuZGVycy9saXN0LW9mLW1hcmtldHMtYW5kLWhhd2tlci1jZW50cmVzLnBkZiksIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgb25seSAxMTQgTkVBIG1hbmFnZWQgaGF3a2VyIGNlbnRyZXMgaW4gU2luZ2Fwb3JlLCBhbmQgdGhpcyBkYXRhIGRvZXMgbm90IGluY2x1ZGUgbWFueSBvZiB0aGUgaGF3a2VyIGNlbnRlcnMgaW4gQ2hvYSBDaHUgS2FuZywgU2VuZyBLYW5nIGFuZCBXb29kbGFuZHMuIEhlbmNlLCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGUgZGlwIGluIHRoZSByaG8gcGxvdHMsIGlzIGR1ZSB0byBpbmNvbXBsZXRlIGRhdGEuIFRoZXJlIGFyZSBoYXdrZXIgY2VudGVycyB0aGVyZSwgYnV0IGp1c3Qgbm90IHJlY29yZGVkLiAKCgo=