Recently, I was doing work with pgRouting and ran into the problem of my network having not nearly enough nodes to get an accurate calculation for what I was doing, which at the time was creating catchment areas. I found a very helpful post by Paul Ramsey where he shows different ways of breaking up linestrings into segments using PostGIS. I adapted his last example here with st_segmentize to add more vertices to the linestrings to ensure that the max length of the line segments was half a meter.

with routing as (
  select row_number() over (order by geometry) as id,  
  st_segmentize(geometry,.5) as geometry
  from streets
),
segments as (
  select id, 
  st_astext(st_makeline(lag((pt).geom, 1, null) over (partition by id order by id, (pt).path), (pt).geom)) as geom
  from (select id, st_dumppoints(geometry) as pt from routing) as dumps
  )
  select * from segments where geom is not null;

A recent update to sfnetworks has made spatial network analysis in R significantly more straightforward and while working with this package, I encountered the same problem as I did with pgRouting: my network needed more nodes. Because of this, I thought it would be fun to try to implement Paul Ramsey’s solution in R using sf and create a function called st_segments to do the job. There is an existing package named nngeo that has the functionst_segments, though I found it to be slow and I wanted to create my own version of the function based on the SQL. Before getting started on anything, it would be best to get a small road network to work with.

library(sf)
library(mapview)
library(dplyr)
library(tigris)

midd = read_sf("https://opendata.arcgis.com/datasets/625b92329bb841cf8295d14553b67e1f_2.geojson") %>% 
  filter(COLLEGE == "MIDDLEBURY COLLEGE") %>% 
  st_transform(32145) %>% # Vermont State Plane Meters
  st_union() %>% 
  st_centroid() %>% 
  st_buffer(5000) %>% 
  st_bbox %>% 
  st_as_sfc

streets = roads("vt", "addison county", progress_bar = F) %>% st_transform(32145) %>% st_intersection(midd) %>%
  filter(!grepl("MULTI", as.character(st_geometry_type(.))))

mapview(streets, color = "#4C4B4C")
 first_attempt = function(x, max_length){
  library(sf)
  library(dplyr)
  library(purrr)
   
  net = st_sf(x)
  
  if (!missing(max_length)) {
    net = st_segmentize(net, max_length)
  }
  
  net %>% 
    mutate(id = row_number()) %>%
    st_cast("POINT") %>%
    group_by(id) %>%
    mutate(geom2 = lead(geometry)) %>%
    filter(!st_is_empty(geom2)) %>%
    ungroup() %>% 
    mutate(geom = map2(
      geometry, geom2,
      ~ st_combine(c(.x, .y)) %>% st_cast("LINESTRING")
    )) %>%
      {
        do.call(c, .$geom)
      }  %>%
      st_set_crs(st_crs(x)) %>% 
    st_sf()
}

This was the first function I came up with and it is somewhat faithful to the original SQL. It utilizes dplyr and purrr and has similar steps and syntax. Just as in the SQL, the features are first given a unique ID and st_segmentize is used to add vertices. The linestrings are then cast to points and rather than using lag, lead is used is used to create a new geometry column with leading point geometries grouped by unique id. Either lag or lead could have been used here, but for whatever reason it was easier for me to wrap my head around using lead. The empty geometries in the new column are then removed, these being the last row of each group since lead was used. After this, map2 is used to create a third geometry column where the points of the two existing geometry columns are combined and cast to a linestring by row. I also tried using st_union rather than st_combine here, though st_combine seemed to work better, and excluding either from the function created unfavorable results such as a very long multilinestring geometry. The new column created with map2 was a not simple feature collection, but rather a list of geometry sets. Because of this, it was necessary to use do.call to combine the geometry sets into a single geometry set. This new set had no CRS, so it had to be reset and the set is subsequently converted into a simple feature collection.

bench::mark(
  nngeo  =  st_geometry(nngeo::st_segments(streets, progress = F)),
  "first attempt " = st_geometry(first_attempt(streets)),
  iterations = 5
) %>%
  mutate(name = as.character(expression),
         max = bench::as_bench_time(lapply(time, max))) %>%
  select(name, min, median, max, iterations = n_itr, total_time)

This new function is undoubtedly faster than nngeo::st_segments, though its result has no fields and it relies on dplyr and purrr.

second_attempt = function(x, max_length) {
  library(sf)
  library(dplyr)
  library(purrr)
  
  net = st_sf(x) %>% mutate(id = row_number())
  no_geo = st_drop_geometry(net)
  
  if (!missing(max_length)) {
    net = st_segmentize(net, max_length)
  }
  
  net = net %>%
    st_cast("POINT") %>%
    group_by(id) %>%
    mutate(geom2 = lead(geometry)) %>%
    filter(!st_is_empty(geom2)) %>%
    ungroup() %>%
    suppressWarnings()
  
  id = net$id
  
  net %>%
    mutate(geom = map2(geometry, geom2, ~ st_combine(c(.x, .y)) %>% st_cast("LINESTRING"))) %>%
    {
      do.call(c, .$geom)
    }  %>%
    st_set_crs(st_crs(x)) %>%
    st_sf() %>%
    mutate(fid = id) %>%
    left_join(no_geo, by = c("fid" = "id")) %>%
    select(-fid) 
}

This second attempt at st_segments solves the problem of the function not retaining the fields of the input and suppresses a message from st_cast, though it still relies heavily on dplyr and purrr.

bench::mark(
  nngeo  =  st_geometry(nngeo::st_segments(streets, progress = F)),
  "first attempt " = st_geometry(first_attempt(streets)),
  "second attempt" = st_geometry(second_attempt(streets)),
  iterations = 5
) %>%
  mutate(name = as.character(expression),
         max = bench::as_bench_time(lapply(time, max))) %>%
  select(name, min, median, max, iterations = n_itr, total_time)

It is marginally slower than the first attempt, though this is expected since more is done here than the first attempt. nngeo::st_segments demonstrates that that it is possible to implement the function using sf and base R alone, but does this have to come at the cost of the function’s speed?

st_segments = function(x, max_length) {
  # x: layer with linestrings/polygons
  # max_length: optional parameter, max length of segments, unit from crs
  library(sf)
  
  net = st_sf(x)
  net$fid = 1:nrow(net)
  
  if (!missing(max_length)) {
    net = st_segmentize(net, max_length)
  }
  
  coord = st_coordinates(net)
  col = ncol(coord)
  ids = coord[, col]
  feat = unique(ids)
  segments = list()
  
  for (i in feat) {
    coords = coord[ids == i,]
    for (j in 1:(nrow(coords) - 1)) {
      segments[[length(segments) + 1]] = st_as_binary(st_linestring(coords[j:(j + 1), 1:2]))
    }
  }
  
  fid = matrix(nrow = (length(ids) - length(feat)), ncol = 1)
  k = 0
  
  for (i in feat) {
    id = ids[ids == i]
    for (j in seq_along(id[1:(length(id) - 1)])) {
      fid[k + 1, ] = id[j]
      k = k + 1
    }
  }
  
  colnames(fid) = "fid"
  fid = as.data.frame(fid)
  fid = merge(fid,st_drop_geometry(net), all.x = T)[,-1, drop = F]
  fid$geometry = st_set_crs(st_as_sfc(segments), st_crs(net))
  st_sf(fid)
}

With st_segments, my final(?) attempt at the function, I quickly found that this is not the case. st_segments loops through a matrix of coordinates created by st_coordinates and makes linestrings in similar fashion to how lead was used in the first.st_coordinates adds a unique ID for each feature and I used this ID in the loop to first filter the matrix and then create linestrings using the points that make up each feature in a nested loop. With each iteration of this nested loop, a line is created from the coordinates in one row and the row below it. And just as is done with filter(!st_is_empty(geom2)), the last row of each group are excluded as they essentially have empty geometries. The linestrings created in the loop are converted to well-known binary and stored in a list. This list can be easily made to a simple feature collection using st_as_sfc. There were other versions of this where I made linestrings well-known text and stored them in either a matrix or a list, though WKB seemed be be the better choice and less was needed to be done to convert it to a simple feature collection. The second loop removes one value from each of the groups in a matrix of feature IDs taken from the coordinate matrix, which is also similar to what is done with filter(!st_is_empty(geom2)). This ID matrix is then used to join fields. This function solves the pseudo problem of depending on dplyr and purrr, and the result still has the original fields. st_segments is also able to break polygons into linestrings, something which my first two attempts at the function were unable to do.

library(osmdata)

bbox = st_bbox(st_transform(midd,4326))

osm_bbox = matrix(data = bbox,nrow = 2,ncol = 2)

buildings= osmdata::opq(bbox) %>% 
  add_osm_feature(key = "building") %>% 
  osmdata_sf() %>% 
  .$osm_polygons %>% 
  select(1:5) %>% 
  filter(!is.na(name))

mapview(buildings, layer.name = "Polygons", col.regions =  "#0D395F") + mapview(st_segments(buildings),layer.name = "Linestrings", color = "#4C4B4C")

Although st_segments is an improvement in my books, I still need to to see if using only sf and base R comes at cost.

bench::mark(
  nngeo =  st_geometry(nngeo::st_segments(streets, progress = F)),
  "first attempt " = st_geometry(first_attempt(streets)),
  "second attempt" = st_geometry(second_attempt(streets)),
  final = st_geometry(st_segments(streets)),
  iterations = 5
) %>% 
  mutate(name = as.character(expression),
         max = bench::as_bench_time(lapply(time, max))) %>% 
  select(name, min, median, max, iterations = n_itr, total_time)

as it turns out, st_segements is significantly faster than the function found in nngeo as well as my first two attempts, and this increase in speed does not come at the cost of the fields as was seen in the first attempt. One issue, however, is that st_segments can only take geometries that are either polygons or linestrings, though this can easily be fixed by using something like st_multipart_to_singleparts beforehand or by incorporating it in st_segments. I could call it quits here for now. Although I said that I was striving toward only using base R and sf, the last loop and merge can easily be done with dplyr, data.table, or the function by, and possibly even done faster. Because of this, I went on to test if this was the case.

#### data.table ####
st_segments_dt = function(x, max_length) {
  library(data.table)
  
  net = x
  net$id = 1:nrow(net)
  
  if (!missing(max_length)) {
    net = st_segmentize(net, max_length)
  }
  
  coord = st_coordinates(net)
  col = ncol(coord)
  ids = coord[, col]
  feat = unique(ids)
  segments = list()
  
  for (i in feat) {
    coords = coord[ids == i, ]
    for (j in 1:(nrow(coords) - 1)) {
      segments[[length(segments) + 1]] = st_as_binary(st_linestring(coords[j:(j + 1), 1:2]))
    }
  }
  
  fid = as.data.table(ids)
  colnames(fid) = "id"
  setkey(fid, id)
  fid = fid[fid[, .I[-1], by = id]$V1][as.data.table(st_drop_geometry(net)), on = "id"]
  fid[, `:=`(geometry = st_as_sfc(segments, crs = st_crs(net)), id = NULL)]
  st_sf(fid)
}

#### dplyr ####
st_segments_tbl = function(x, max_length) {
  library(sf)
  library(dplyr)
  
  net = mutate(x, value = row_number())
  
  if (!missing(max_length)) {
    net = st_segmentize(net, max_length)
  }
  
  coord = st_coordinates(net)
  col = ncol(coord)
  ids = coord[, col]
  feat = unique(ids)
  segments = list()
  
  for (i in feat) {
    coords = coord[ids == i, ]
    for (j in 1:(nrow(coords) - 1)) {
      segments[[length(segments) + 1]] = st_as_binary(st_linestring(coords[j:(j + 1), 1:2]))
    }
  }
  
  as_tibble(ids) %>%
    group_by(value) %>%
    slice(-1) %>%
    ungroup %>%
    left_join(st_drop_geometry(net), by = "value") %>%
    select(-1) %>%
    mutate(geometry = st_as_sfc(segments, crs = st_crs(net))) %>% st_sf()
}

#### using by ####
st_segments_by = function(x, max_length) {
  library(sf)
  
  net = x
  net$fid = factor(1:nrow(net), levels = 1:nrow(net))
  
  if (!missing(max_length)) {
    net = st_segmentize(net, max_length)
  }
  
  coord = st_coordinates(net)
  col = ncol(coord)
  ids = coord[, col]
  feat = unique(ids)
  segments = list()
  
  for (i in feat) {
    coords = coord[ids == i, ]
    for (j in 1:(nrow(coords) - 1)) {
      segments[[length(segments) + 1]] = st_as_binary(st_linestring(coords[j:(j + 1), 1:2]))
    }
  }
  
 fid = as.data.frame(ids)
 colnames(fid) = "fid"
 fid$fid = factor(fid$fid, levels = unique(fid$fid))
 fid = do.call(rbind, by(fid, fid$fid, function(x) x[-1,, drop = F], simplify = F))
 fid = merge(fid, st_drop_geometry(net), all.x = T)[, -1, drop = F]
 fid$geometry = st_as_sfc(segments, crs = st_crs(net))
 st_sf(fid)
}

These three functions are essentially the same save for how they deal with the absence of the second loop. What is not the same, however, is their speed.

bench::mark(
  "base (st_segments)" = st_geometry(st_segments(streets,1)),
  "base (using by)" = st_geometry(st_segments_by(streets,1)),
  data.table = st_geometry(st_segments_dt(streets,1)),
  dplyr = st_geometry(st_segments_tbl(streets,1)),
  iterations = 5
) %>%
  mutate(name = as.character(expression),
         max = bench::as_bench_time(lapply(time, max))) %>%
  select(name, min, median, max, iterations = n_itr, total_time)

This time around I used the second parameter of these functions to see how they handle somewhat larger datasets. Rather than breaking 490 linestrings into 7,423 line segments, the max length of each line segment is set to one meter in this benchmark and the number of features increases from 490 to 225,118. What is interesting to see with this benchmark is that st_segments is comparable in speed to both the data.table and dplyr versions of the function and is slightly faster than when the last loop is replaced by by. It is also surprising that in this situation, dplyr is ever so slightly faster than data.table, though this may be due to my inexperience with the latter and what was being done. I could try to see what speed improvement could be had if the both loops were replaced with data.table, though that is for another time. I already saw with the first and second attempts that working completely within the tidyverse slowed things down. Given the results of the benchmarks, I could just use st_segments in its current state. In all, it was fun to take a break from things and spend time to solve a “simple yet tricky” problem in R.

LS0tDQp0aXRsZTogInN0X3NlZ21lbnRzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NClJlY2VudGx5LCBJIHdhcyBkb2luZyB3b3JrIHdpdGggcGdSb3V0aW5nIGFuZCByYW4gaW50byB0aGUgcHJvYmxlbSBvZiBteSBuZXR3b3JrIGhhdmluZyBub3QgbmVhcmx5IGVub3VnaCBub2RlcyB0byBnZXQgYW4gYWNjdXJhdGUgY2FsY3VsYXRpb24gZm9yIHdoYXQgSSB3YXMgZG9pbmcsIHdoaWNoIGF0IHRoZSB0aW1lIHdhcyBjcmVhdGluZyBjYXRjaG1lbnQgYXJlYXMuIEkgZm91bmQgYSB2ZXJ5IGhlbHBmdWwgcG9zdCBieSBbUGF1bCBSYW1zZXldKGh0dHA6Ly9ibG9nLmNsZXZlcmVsZXBoYW50LmNhLzIwMTUvMDIvYnJlYWtpbmctbGluZXN0cmluZy1pbnRvLXNlZ21lbnRzLmh0bWwpIHdoZXJlIGhlIHNob3dzIGRpZmZlcmVudCB3YXlzIG9mIGJyZWFraW5nIHVwIGxpbmVzdHJpbmdzIGludG8gc2VnbWVudHMgdXNpbmcgUG9zdEdJUy4gSSBhZGFwdGVkIGhpcyBsYXN0IGV4YW1wbGUgaGVyZSB3aXRoIGBzdF9zZWdtZW50aXplYCB0byBhZGQgbW9yZSB2ZXJ0aWNlcyB0byB0aGUgbGluZXN0cmluZ3MgdG8gZW5zdXJlIHRoYXQgdGhlIG1heCBsZW5ndGggb2YgdGhlIGxpbmUgc2VnbWVudHMgd2FzIGhhbGYgYSBtZXRlci4gDQoNCmBgYHtzcWwgYnJlYWtpbmcgdXAgbGluZXN0cmluZ3MgaW4gcG9zdGdpcywgZXZhbCA9IEZ9DQp3aXRoIHJvdXRpbmcgYXMgKA0KICBzZWxlY3Qgcm93X251bWJlcigpIG92ZXIgKG9yZGVyIGJ5IGdlb21ldHJ5KSBhcyBpZCwgIA0KICBzdF9zZWdtZW50aXplKGdlb21ldHJ5LC41KSBhcyBnZW9tZXRyeQ0KICBmcm9tIHN0cmVldHMNCiksDQpzZWdtZW50cyBhcyAoDQogIHNlbGVjdCBpZCwgDQogIHN0X2FzdGV4dChzdF9tYWtlbGluZShsYWcoKHB0KS5nZW9tLCAxLCBudWxsKSBvdmVyIChwYXJ0aXRpb24gYnkgaWQgb3JkZXIgYnkgaWQsIChwdCkucGF0aCksIChwdCkuZ2VvbSkpIGFzIGdlb20NCiAgZnJvbSAoc2VsZWN0IGlkLCBzdF9kdW1wcG9pbnRzKGdlb21ldHJ5KSBhcyBwdCBmcm9tIHJvdXRpbmcpIGFzIGR1bXBzDQogICkNCiAgc2VsZWN0ICogZnJvbSBzZWdtZW50cyB3aGVyZSBnZW9tIGlzIG5vdCBudWxsOw0KYGBgDQoNCkEgcmVjZW50IHVwZGF0ZSB0byBbYHNmbmV0d29ya3NgXShodHRwczovL2dpdGh1Yi5jb20vbHV1a3ZkbWVlci9zZm5ldHdvcmtzKSBoYXMgbWFkZSBzcGF0aWFsIG5ldHdvcmsgYW5hbHlzaXMgaW4gUiBzaWduaWZpY2FudGx5IG1vcmUgc3RyYWlnaHRmb3J3YXJkIGFuZCB3aGlsZSB3b3JraW5nIHdpdGggdGhpcyBwYWNrYWdlLCBJIGVuY291bnRlcmVkIHRoZSBzYW1lIHByb2JsZW0gYXMgSSBkaWQgd2l0aCBwZ1JvdXRpbmc6IG15IG5ldHdvcmsgbmVlZGVkIG1vcmUgbm9kZXMuIEJlY2F1c2Ugb2YgdGhpcywgSSB0aG91Z2h0IGl0IHdvdWxkIGJlIGZ1biB0byB0cnkgdG8gaW1wbGVtZW50IFBhdWwgUmFtc2V5J3Mgc29sdXRpb24gaW4gUiB1c2luZyBgc2ZgIGFuZCBjcmVhdGUgYSBmdW5jdGlvbiBjYWxsZWQgYHN0X3NlZ21lbnRzYCB0byBkbyB0aGUgam9iLiBUaGVyZSBpcyBhbiBleGlzdGluZyBwYWNrYWdlIG5hbWVkIFtgbm5nZW9gXShodHRwczovL2dpdGh1Yi5jb20vbWljaGFlbGRvcm1hbi9ubmdlbykgdGhhdCBoYXMgdGhlIGZ1bmN0aW9uYHN0X3NlZ21lbnRzYCwgdGhvdWdoIEkgZm91bmQgaXQgdG8gYmUgc2xvdyBhbmQgSSB3YW50ZWQgdG8gY3JlYXRlIG15IG93biB2ZXJzaW9uIG9mIHRoZSBmdW5jdGlvbiBiYXNlZCBvbiB0aGUgU1FMLiBCZWZvcmUgZ2V0dGluZyBzdGFydGVkIG9uIGFueXRoaW5nLCBpdCB3b3VsZCBiZSBiZXN0IHRvIGdldCBhIHNtYWxsIHJvYWQgbmV0d29yayB0byB3b3JrIHdpdGguIA0KDQpgYGB7ciBnZXR0aW5nIGEgcm9hZCBuZXR3b3JrLCBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OX0NCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KG1hcHZpZXcpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWdyaXMpDQoNCm1pZGQgPSByZWFkX3NmKCJodHRwczovL29wZW5kYXRhLmFyY2dpcy5jb20vZGF0YXNldHMvNjI1YjkyMzI5YmI4NDFjZjgyOTVkMTQ1NTNiNjdlMWZfMi5nZW9qc29uIikgJT4lIA0KICBmaWx0ZXIoQ09MTEVHRSA9PSAiTUlERExFQlVSWSBDT0xMRUdFIikgJT4lIA0KICBzdF90cmFuc2Zvcm0oMzIxNDUpICU+JSAjIFZlcm1vbnQgU3RhdGUgUGxhbmUgTWV0ZXJzDQogIHN0X3VuaW9uKCkgJT4lIA0KICBzdF9jZW50cm9pZCgpICU+JSANCiAgc3RfYnVmZmVyKDUwMDApICU+JSANCiAgc3RfYmJveCAlPiUgDQogIHN0X2FzX3NmYw0KDQpzdHJlZXRzID0gcm9hZHMoInZ0IiwgImFkZGlzb24gY291bnR5IiwgcHJvZ3Jlc3NfYmFyID0gRikgJT4lIHN0X3RyYW5zZm9ybSgzMjE0NSkgJT4lIHN0X2ludGVyc2VjdGlvbihtaWRkKSAlPiUNCiAgZmlsdGVyKCFncmVwbCgiTVVMVEkiLCBhcy5jaGFyYWN0ZXIoc3RfZ2VvbWV0cnlfdHlwZSguKSkpKQ0KDQptYXB2aWV3KHN0cmVldHMsIGNvbG9yID0gIiM0QzRCNEMiKQ0KYGBgDQogDQpgYGB7ciBmaXJzdCBhdHRlbXB0fQ0KIGZpcnN0X2F0dGVtcHQgPSBmdW5jdGlvbih4LCBtYXhfbGVuZ3RoKXsNCiAgbGlicmFyeShzZikNCiAgbGlicmFyeShkcGx5cikNCiAgbGlicmFyeShwdXJycikNCiAgIA0KICBuZXQgPSBzdF9zZih4KQ0KICANCiAgaWYgKCFtaXNzaW5nKG1heF9sZW5ndGgpKSB7DQogICAgbmV0ID0gc3Rfc2VnbWVudGl6ZShuZXQsIG1heF9sZW5ndGgpDQogIH0NCiAgDQogIG5ldCAlPiUgDQogICAgbXV0YXRlKGlkID0gcm93X251bWJlcigpKSAlPiUNCiAgICBzdF9jYXN0KCJQT0lOVCIpICU+JQ0KICAgIGdyb3VwX2J5KGlkKSAlPiUNCiAgICBtdXRhdGUoZ2VvbTIgPSBsZWFkKGdlb21ldHJ5KSkgJT4lDQogICAgZmlsdGVyKCFzdF9pc19lbXB0eShnZW9tMikpICU+JQ0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgbXV0YXRlKGdlb20gPSBtYXAyKA0KICAgICAgZ2VvbWV0cnksIGdlb20yLA0KICAgICAgfiBzdF9jb21iaW5lKGMoLngsIC55KSkgJT4lIHN0X2Nhc3QoIkxJTkVTVFJJTkciKQ0KICAgICkpICU+JQ0KICAgICAgew0KICAgICAgICBkby5jYWxsKGMsIC4kZ2VvbSkNCiAgICAgIH0gICU+JQ0KICAgICAgc3Rfc2V0X2NycyhzdF9jcnMoeCkpICU+JSANCiAgICBzdF9zZigpDQp9DQpgYGANClRoaXMgd2FzIHRoZSBmaXJzdCBmdW5jdGlvbiBJIGNhbWUgdXAgd2l0aCBhbmQgaXQgaXMgc29tZXdoYXQgZmFpdGhmdWwgdG8gdGhlIG9yaWdpbmFsIFNRTC4gSXQgdXRpbGl6ZXMgYGRwbHlyYCBhbmQgYHB1cnJyYCBhbmQgaGFzIHNpbWlsYXIgc3RlcHMgYW5kIHN5bnRheC4gSnVzdCBhcyBpbiB0aGUgU1FMLCB0aGUgZmVhdHVyZXMgYXJlIGZpcnN0IGdpdmVuIGEgdW5pcXVlIElEIGFuZCBgc3Rfc2VnbWVudGl6ZWAgaXMgdXNlZCB0byBhZGQgdmVydGljZXMuIFRoZSBsaW5lc3RyaW5ncyBhcmUgdGhlbiBjYXN0IHRvIHBvaW50cyBhbmQgcmF0aGVyIHRoYW4gdXNpbmcgYGxhZ2AsIGBsZWFkYCBpcyB1c2VkIGlzIHVzZWQgdG8gY3JlYXRlIGEgbmV3IGdlb21ldHJ5IGNvbHVtbiB3aXRoIGxlYWRpbmcgcG9pbnQgZ2VvbWV0cmllcyBncm91cGVkIGJ5IHVuaXF1ZSBpZC4gRWl0aGVyIGBsYWdgIG9yIGBsZWFkYCBjb3VsZCBoYXZlIGJlZW4gdXNlZCBoZXJlLCBidXQgZm9yIHdoYXRldmVyIHJlYXNvbiBpdCB3YXMgZWFzaWVyIGZvciBtZSB0byB3cmFwIG15IGhlYWQgYXJvdW5kIHVzaW5nIGBsZWFkYC4gVGhlIGVtcHR5IGdlb21ldHJpZXMgaW4gdGhlIG5ldyBjb2x1bW4gYXJlIHRoZW4gcmVtb3ZlZCwgdGhlc2UgYmVpbmcgdGhlIGxhc3Qgcm93IG9mIGVhY2ggZ3JvdXAgc2luY2UgYGxlYWRgIHdhcyB1c2VkLiBBZnRlciB0aGlzLCBgbWFwMmAgaXMgdXNlZCB0byBjcmVhdGUgYSB0aGlyZCBnZW9tZXRyeSBjb2x1bW4gd2hlcmUgdGhlIHBvaW50cyBvZiB0aGUgdHdvIGV4aXN0aW5nIGdlb21ldHJ5IGNvbHVtbnMgYXJlIGNvbWJpbmVkIGFuZCBjYXN0IHRvIGEgbGluZXN0cmluZyBieSByb3cuIEkgYWxzbyB0cmllZCB1c2luZyBgc3RfdW5pb25gIHJhdGhlciB0aGFuIGBzdF9jb21iaW5lYCBoZXJlLCB0aG91Z2ggYHN0X2NvbWJpbmVgIHNlZW1lZCB0byB3b3JrIGJldHRlciwgYW5kIGV4Y2x1ZGluZyBlaXRoZXIgZnJvbSB0aGUgZnVuY3Rpb24gY3JlYXRlZCB1bmZhdm9yYWJsZSByZXN1bHRzIHN1Y2ggYXMgYSB2ZXJ5IGxvbmcgbXVsdGlsaW5lc3RyaW5nIGdlb21ldHJ5LiBUaGUgbmV3IGNvbHVtbiBjcmVhdGVkIHdpdGggYG1hcDJgIHdhcyBhIG5vdCBzaW1wbGUgZmVhdHVyZSBjb2xsZWN0aW9uLCBidXQgcmF0aGVyIGEgbGlzdCBvZiBnZW9tZXRyeSBzZXRzLiBCZWNhdXNlIG9mIHRoaXMsIGl0IHdhcyBuZWNlc3NhcnkgdG8gdXNlIGBkby5jYWxsYCB0byBjb21iaW5lIHRoZSBnZW9tZXRyeSBzZXRzIGludG8gYSBzaW5nbGUgZ2VvbWV0cnkgc2V0LiBUaGlzIG5ldyBzZXQgaGFkIG5vIENSUywgc28gaXQgaGFkIHRvIGJlIHJlc2V0IGFuZCB0aGUgc2V0IGlzIHN1YnNlcXVlbnRseSBjb252ZXJ0ZWQgaW50byBhIHNpbXBsZSBmZWF0dXJlIGNvbGxlY3Rpb24uICAgDQoNCmBgYHtyIGZpcnN0IGJlbmNobWFyaywgd2FybmluZz1GLG1lc3NhZ2U9Rn0NCmJlbmNoOjptYXJrKA0KICBubmdlbyAgPSAgc3RfZ2VvbWV0cnkobm5nZW86OnN0X3NlZ21lbnRzKHN0cmVldHMsIHByb2dyZXNzID0gRikpLA0KICAiZmlyc3QgYXR0ZW1wdCAiID0gc3RfZ2VvbWV0cnkoZmlyc3RfYXR0ZW1wdChzdHJlZXRzKSksDQogIGl0ZXJhdGlvbnMgPSA1DQopICU+JQ0KICBtdXRhdGUobmFtZSA9IGFzLmNoYXJhY3RlcihleHByZXNzaW9uKSwNCiAgICAgICAgIG1heCA9IGJlbmNoOjphc19iZW5jaF90aW1lKGxhcHBseSh0aW1lLCBtYXgpKSkgJT4lDQogIHNlbGVjdChuYW1lLCBtaW4sIG1lZGlhbiwgbWF4LCBpdGVyYXRpb25zID0gbl9pdHIsIHRvdGFsX3RpbWUpDQpgYGANClRoaXMgbmV3IGZ1bmN0aW9uIGlzIHVuZG91YnRlZGx5IGZhc3RlciB0aGFuIGBubmdlbzo6c3Rfc2VnbWVudHNgLCB0aG91Z2ggaXRzIHJlc3VsdCBoYXMgbm8gZmllbGRzIGFuZCBpdCByZWxpZXMgb24gYGRwbHlyYCBhbmQgYHB1cnJyYC4gDQoNCmBgYHtyIHNlY29uZCBhdHRlbXB0fQ0Kc2Vjb25kX2F0dGVtcHQgPSBmdW5jdGlvbih4LCBtYXhfbGVuZ3RoKSB7DQogIGxpYnJhcnkoc2YpDQogIGxpYnJhcnkoZHBseXIpDQogIGxpYnJhcnkocHVycnIpDQogIA0KICBuZXQgPSBzdF9zZih4KSAlPiUgbXV0YXRlKGlkID0gcm93X251bWJlcigpKQ0KICBub19nZW8gPSBzdF9kcm9wX2dlb21ldHJ5KG5ldCkNCiAgDQogIGlmICghbWlzc2luZyhtYXhfbGVuZ3RoKSkgew0KICAgIG5ldCA9IHN0X3NlZ21lbnRpemUobmV0LCBtYXhfbGVuZ3RoKQ0KICB9DQogIA0KICBuZXQgPSBuZXQgJT4lDQogICAgc3RfY2FzdCgiUE9JTlQiKSAlPiUNCiAgICBncm91cF9ieShpZCkgJT4lDQogICAgbXV0YXRlKGdlb20yID0gbGVhZChnZW9tZXRyeSkpICU+JQ0KICAgIGZpbHRlcighc3RfaXNfZW1wdHkoZ2VvbTIpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgc3VwcHJlc3NXYXJuaW5ncygpDQogIA0KICBpZCA9IG5ldCRpZA0KICANCiAgbmV0ICU+JQ0KICAgIG11dGF0ZShnZW9tID0gbWFwMihnZW9tZXRyeSwgZ2VvbTIsIH4gc3RfY29tYmluZShjKC54LCAueSkpICU+JSBzdF9jYXN0KCJMSU5FU1RSSU5HIikpKSAlPiUNCiAgICB7DQogICAgICBkby5jYWxsKGMsIC4kZ2VvbSkNCiAgICB9ICAlPiUNCiAgICBzdF9zZXRfY3JzKHN0X2Nycyh4KSkgJT4lDQogICAgc3Rfc2YoKSAlPiUNCiAgICBtdXRhdGUoZmlkID0gaWQpICU+JQ0KICAgIGxlZnRfam9pbihub19nZW8sIGJ5ID0gYygiZmlkIiA9ICJpZCIpKSAlPiUNCiAgICBzZWxlY3QoLWZpZCkgDQp9DQpgYGANCg0KVGhpcyBzZWNvbmQgYXR0ZW1wdCBhdCBgc3Rfc2VnbWVudHNgIHNvbHZlcyB0aGUgcHJvYmxlbSBvZiB0aGUgZnVuY3Rpb24gbm90IHJldGFpbmluZyB0aGUgZmllbGRzIG9mIHRoZSBpbnB1dCBhbmQgc3VwcHJlc3NlcyBhIG1lc3NhZ2UgZnJvbSBgc3RfY2FzdGAsIHRob3VnaCBpdCBzdGlsbCByZWxpZXMgaGVhdmlseSBvbiBgZHBseXJgIGFuZCBgcHVycnJgLiANCg0KYGBge3Igc2Vjb25kIGJlbmNobWFyaywgd2FybmluZz1GLG1lc3NhZ2U9Rn0NCmJlbmNoOjptYXJrKA0KICBubmdlbyAgPSAgc3RfZ2VvbWV0cnkobm5nZW86OnN0X3NlZ21lbnRzKHN0cmVldHMsIHByb2dyZXNzID0gRikpLA0KICAiZmlyc3QgYXR0ZW1wdCAiID0gc3RfZ2VvbWV0cnkoZmlyc3RfYXR0ZW1wdChzdHJlZXRzKSksDQogICJzZWNvbmQgYXR0ZW1wdCIgPSBzdF9nZW9tZXRyeShzZWNvbmRfYXR0ZW1wdChzdHJlZXRzKSksDQogIGl0ZXJhdGlvbnMgPSA1DQopICU+JQ0KICBtdXRhdGUobmFtZSA9IGFzLmNoYXJhY3RlcihleHByZXNzaW9uKSwNCiAgICAgICAgIG1heCA9IGJlbmNoOjphc19iZW5jaF90aW1lKGxhcHBseSh0aW1lLCBtYXgpKSkgJT4lDQogIHNlbGVjdChuYW1lLCBtaW4sIG1lZGlhbiwgbWF4LCBpdGVyYXRpb25zID0gbl9pdHIsIHRvdGFsX3RpbWUpDQpgYGANCkl0IGlzIG1hcmdpbmFsbHkgc2xvd2VyIHRoYW4gdGhlIGZpcnN0IGF0dGVtcHQsIHRob3VnaCB0aGlzIGlzIGV4cGVjdGVkIHNpbmNlIG1vcmUgaXMgZG9uZSBoZXJlIHRoYW4gdGhlIGZpcnN0IGF0dGVtcHQuIGBubmdlbzo6c3Rfc2VnbWVudHNgIGRlbW9uc3RyYXRlcyB0aGF0IHRoYXQgaXQgaXMgcG9zc2libGUgdG8gaW1wbGVtZW50IHRoZSBmdW5jdGlvbiB1c2luZyBgc2ZgIGFuZCBiYXNlIFIgYWxvbmUsIGJ1dCBkb2VzIHRoaXMgaGF2ZSB0byBjb21lIGF0IHRoZSBjb3N0IG9mIHRoZSBmdW5jdGlvbidzIHNwZWVkPw0KDQpgYGB7ciB1c2luZyBzdF9zZWdtZW50c30NCnN0X3NlZ21lbnRzID0gZnVuY3Rpb24oeCwgbWF4X2xlbmd0aCkgew0KICAjIHg6IGxheWVyIHdpdGggbGluZXN0cmluZ3MvcG9seWdvbnMNCiAgIyBtYXhfbGVuZ3RoOiBvcHRpb25hbCBwYXJhbWV0ZXIsIG1heCBsZW5ndGggb2Ygc2VnbWVudHMsIHVuaXQgZnJvbSBjcnMNCiAgbGlicmFyeShzZikNCiAgDQogIG5ldCA9IHN0X3NmKHgpDQogIG5ldCRmaWQgPSAxOm5yb3cobmV0KQ0KICANCiAgaWYgKCFtaXNzaW5nKG1heF9sZW5ndGgpKSB7DQogICAgbmV0ID0gc3Rfc2VnbWVudGl6ZShuZXQsIG1heF9sZW5ndGgpDQogIH0NCiAgDQogIGNvb3JkID0gc3RfY29vcmRpbmF0ZXMobmV0KQ0KICBjb2wgPSBuY29sKGNvb3JkKQ0KICBpZHMgPSBjb29yZFssIGNvbF0NCiAgZmVhdCA9IHVuaXF1ZShpZHMpDQogIHNlZ21lbnRzID0gbGlzdCgpDQogIA0KICBmb3IgKGkgaW4gZmVhdCkgew0KICAgIGNvb3JkcyA9IGNvb3JkW2lkcyA9PSBpLF0NCiAgICBmb3IgKGogaW4gMToobnJvdyhjb29yZHMpIC0gMSkpIHsNCiAgICAgIHNlZ21lbnRzW1tsZW5ndGgoc2VnbWVudHMpICsgMV1dID0gc3RfYXNfYmluYXJ5KHN0X2xpbmVzdHJpbmcoY29vcmRzW2o6KGogKyAxKSwgMToyXSkpDQogICAgfQ0KICB9DQogIA0KICBmaWQgPSBtYXRyaXgobnJvdyA9IChsZW5ndGgoaWRzKSAtIGxlbmd0aChmZWF0KSksIG5jb2wgPSAxKQ0KICBrID0gMA0KICANCiAgZm9yIChpIGluIGZlYXQpIHsNCiAgICBpZCA9IGlkc1tpZHMgPT0gaV0NCiAgICBmb3IgKGogaW4gc2VxX2Fsb25nKGlkWzE6KGxlbmd0aChpZCkgLSAxKV0pKSB7DQogICAgICBmaWRbayArIDEsIF0gPSBpZFtqXQ0KICAgICAgayA9IGsgKyAxDQogICAgfQ0KICB9DQogIA0KICBjb2xuYW1lcyhmaWQpID0gImZpZCINCiAgZmlkID0gYXMuZGF0YS5mcmFtZShmaWQpDQogIGZpZCA9IG1lcmdlKGZpZCxzdF9kcm9wX2dlb21ldHJ5KG5ldCksIGFsbC54ID0gVClbLC0xLCBkcm9wID0gRl0NCiAgZmlkJGdlb21ldHJ5ID0gc3Rfc2V0X2NycyhzdF9hc19zZmMoc2VnbWVudHMpLCBzdF9jcnMobmV0KSkNCiAgc3Rfc2YoZmlkKQ0KfQ0KYGBgDQoNCldpdGggYHN0X3NlZ21lbnRzYCwgbXkgZmluYWwoPykgYXR0ZW1wdCBhdCB0aGUgZnVuY3Rpb24sIEkgcXVpY2tseSBmb3VuZCB0aGF0IHRoaXMgaXMgbm90IHRoZSBjYXNlLiBgc3Rfc2VnbWVudHNgIGxvb3BzIHRocm91Z2ggYSBtYXRyaXggb2YgY29vcmRpbmF0ZXMgY3JlYXRlZCBieSBgc3RfY29vcmRpbmF0ZXNgIGFuZCBtYWtlcyBsaW5lc3RyaW5ncyBpbiBzaW1pbGFyIGZhc2hpb24gdG8gaG93IGBsZWFkYCB3YXMgdXNlZCBpbiB0aGUgZmlyc3QuYHN0X2Nvb3JkaW5hdGVzYCBhZGRzIGEgdW5pcXVlIElEIGZvciBlYWNoIGZlYXR1cmUgYW5kIEkgdXNlZCB0aGlzIElEIGluIHRoZSBsb29wIHRvIGZpcnN0IGZpbHRlciB0aGUgbWF0cml4IGFuZCB0aGVuIGNyZWF0ZSBsaW5lc3RyaW5ncyB1c2luZyB0aGUgcG9pbnRzIHRoYXQgbWFrZSB1cCBlYWNoIGZlYXR1cmUgaW4gYSBuZXN0ZWQgbG9vcC4gV2l0aCBlYWNoIGl0ZXJhdGlvbiBvZiB0aGlzIG5lc3RlZCBsb29wLCBhIGxpbmUgaXMgY3JlYXRlZCBmcm9tIHRoZSBjb29yZGluYXRlcyBpbiBvbmUgcm93IGFuZCB0aGUgcm93IGJlbG93IGl0LiBBbmQganVzdCBhcyBpcyBkb25lIHdpdGggYGZpbHRlcighc3RfaXNfZW1wdHkoZ2VvbTIpKWAsIHRoZSBsYXN0IHJvdyBvZiBlYWNoIGdyb3VwIGFyZSBleGNsdWRlZCBhcyB0aGV5IGVzc2VudGlhbGx5IGhhdmUgZW1wdHkgZ2VvbWV0cmllcy4gVGhlIGxpbmVzdHJpbmdzIGNyZWF0ZWQgaW4gdGhlIGxvb3AgYXJlIGNvbnZlcnRlZCB0byB3ZWxsLWtub3duIGJpbmFyeSBhbmQgc3RvcmVkIGluIGEgbGlzdC4gVGhpcyBsaXN0IGNhbiBiZSBlYXNpbHkgbWFkZSB0byBhIHNpbXBsZSBmZWF0dXJlIGNvbGxlY3Rpb24gdXNpbmcgW2BzdF9hc19zZmNgXShodHRwczovL3Itc3BhdGlhbC5naXRodWIuaW8vc2YvcmVmZXJlbmNlL3N0X2FzX3NmYy5odG1sKS4gVGhlcmUgd2VyZSBvdGhlciB2ZXJzaW9ucyBvZiB0aGlzIHdoZXJlIEkgbWFkZSBsaW5lc3RyaW5ncyB3ZWxsLWtub3duIHRleHQgYW5kIHN0b3JlZCB0aGVtIGluIGVpdGhlciBhIG1hdHJpeCBvciBhIGxpc3QsIHRob3VnaCBXS0Igc2VlbWVkIGJlIGJlIHRoZSBiZXR0ZXIgY2hvaWNlIGFuZCBsZXNzIHdhcyBuZWVkZWQgdG8gYmUgZG9uZSB0byBjb252ZXJ0IGl0IHRvIGEgc2ltcGxlIGZlYXR1cmUgY29sbGVjdGlvbi4gVGhlIHNlY29uZCBsb29wIHJlbW92ZXMgb25lIHZhbHVlIGZyb20gZWFjaCBvZiB0aGUgZ3JvdXBzIGluIGEgbWF0cml4IG9mIGZlYXR1cmUgSURzIHRha2VuIGZyb20gdGhlIGNvb3JkaW5hdGUgbWF0cml4LCB3aGljaCBpcyBhbHNvICBzaW1pbGFyIHRvIHdoYXQgaXMgZG9uZSB3aXRoIGBmaWx0ZXIoIXN0X2lzX2VtcHR5KGdlb20yKSlgLiBUaGlzIElEIG1hdHJpeCBpcyB0aGVuIHVzZWQgdG8gam9pbiBmaWVsZHMuIFRoaXMgZnVuY3Rpb24gc29sdmVzIHRoZSBwc2V1ZG8gcHJvYmxlbSBvZiBkZXBlbmRpbmcgb24gYGRwbHlyYCAgYW5kIGBwdXJycmAsIGFuZCB0aGUgcmVzdWx0IHN0aWxsIGhhcyB0aGUgb3JpZ2luYWwgZmllbGRzLiBgc3Rfc2VnbWVudHNgIGlzIGFsc28gYWJsZSB0byBicmVhayBwb2x5Z29ucyBpbnRvIGxpbmVzdHJpbmdzLCBzb21ldGhpbmcgd2hpY2ggbXkgZmlyc3QgdHdvIGF0dGVtcHRzIGF0IHRoZSBmdW5jdGlvbiB3ZXJlIHVuYWJsZSB0byBkby4gDQoNCmBgYHtyIG9zbSBkZW1vbnN0cmF0aW9uLG1lc3NhZ2U9Riwgd2FybmluZz1GLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD05fQ0KbGlicmFyeShvc21kYXRhKQ0KDQpiYm94ID0gc3RfYmJveChzdF90cmFuc2Zvcm0obWlkZCw0MzI2KSkNCg0Kb3NtX2Jib3ggPSBtYXRyaXgoZGF0YSA9IGJib3gsbnJvdyA9IDIsbmNvbCA9IDIpDQoNCmJ1aWxkaW5ncz0gb3NtZGF0YTo6b3BxKGJib3gpICU+JSANCiAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICJidWlsZGluZyIpICU+JSANCiAgb3NtZGF0YV9zZigpICU+JSANCiAgLiRvc21fcG9seWdvbnMgJT4lIA0KICBzZWxlY3QoMTo1KSAlPiUgDQogIGZpbHRlcighaXMubmEobmFtZSkpDQoNCm1hcHZpZXcoYnVpbGRpbmdzLCBsYXllci5uYW1lID0gIlBvbHlnb25zIiwgY29sLnJlZ2lvbnMgPSAgIiMwRDM5NUYiKSArIG1hcHZpZXcoc3Rfc2VnbWVudHMoYnVpbGRpbmdzKSxsYXllci5uYW1lID0gIkxpbmVzdHJpbmdzIiwgY29sb3IgPSAiIzRDNEI0QyIpDQpgYGANCg0KQWx0aG91Z2ggYHN0X3NlZ21lbnRzYCBpcyBhbiBpbXByb3ZlbWVudCBpbiBteSBib29rcywgSSBzdGlsbCBuZWVkIHRvIHRvIHNlZSBpZiB1c2luZyBvbmx5IGBzZmAgYW5kIGJhc2UgUiBjb21lcyBhdCBjb3N0LiANCmBgYHtyIHN0X3NlZ2VtZW50cyBiZW5jaG1hcmssIG1lc3NhZ2U9Rix3YXJuaW5nPUZ9DQpiZW5jaDo6bWFyaygNCiAgbm5nZW8gPSAgc3RfZ2VvbWV0cnkobm5nZW86OnN0X3NlZ21lbnRzKHN0cmVldHMsIHByb2dyZXNzID0gRikpLA0KICAiZmlyc3QgYXR0ZW1wdCAiID0gc3RfZ2VvbWV0cnkoZmlyc3RfYXR0ZW1wdChzdHJlZXRzKSksDQogICJzZWNvbmQgYXR0ZW1wdCIgPSBzdF9nZW9tZXRyeShzZWNvbmRfYXR0ZW1wdChzdHJlZXRzKSksDQogIGZpbmFsID0gc3RfZ2VvbWV0cnkoc3Rfc2VnbWVudHMoc3RyZWV0cykpLA0KICBpdGVyYXRpb25zID0gNQ0KKSAlPiUgDQogIG11dGF0ZShuYW1lID0gYXMuY2hhcmFjdGVyKGV4cHJlc3Npb24pLA0KICAgICAgICAgbWF4ID0gYmVuY2g6OmFzX2JlbmNoX3RpbWUobGFwcGx5KHRpbWUsIG1heCkpKSAlPiUgDQogIHNlbGVjdChuYW1lLCBtaW4sIG1lZGlhbiwgbWF4LCBpdGVyYXRpb25zID0gbl9pdHIsIHRvdGFsX3RpbWUpDQpgYGANCmFzIGl0IHR1cm5zIG91dCwgYHN0X3NlZ2VtZW50c2AgaXMgc2lnbmlmaWNhbnRseSBmYXN0ZXIgdGhhbiB0aGUgZnVuY3Rpb24gZm91bmQgaW4gYG5uZ2VvYCBhcyB3ZWxsIGFzIG15IGZpcnN0IHR3byBhdHRlbXB0cywgYW5kIHRoaXMgaW5jcmVhc2UgaW4gc3BlZWQgZG9lcyBub3QgY29tZSBhdCB0aGUgY29zdCBvZiB0aGUgZmllbGRzIGFzIHdhcyBzZWVuIGluIHRoZSBmaXJzdCBhdHRlbXB0LiBPbmUgaXNzdWUsIGhvd2V2ZXIsIGlzIHRoYXQgYHN0X3NlZ21lbnRzYCBjYW4gb25seSB0YWtlIGdlb21ldHJpZXMgdGhhdCBhcmUgZWl0aGVyIHBvbHlnb25zIG9yIGxpbmVzdHJpbmdzLCB0aG91Z2ggdGhpcyBjYW4gZWFzaWx5IGJlIGZpeGVkIGJ5IHVzaW5nIHNvbWV0aGluZyBsaWtlIFtgc3RfbXVsdGlwYXJ0X3RvX3NpbmdsZXBhcnRzYF0oaHR0cHM6Ly9rdWZyZXUuZ2l0aHViLmlvL211c2luZ3Mvc3RfbXVsdGlwYXJ0X3RvX3NpbmdsZXBhcnRzL3N0X211bHRpcGFydF90b19zaW5nbGVwYXJ0cy5tZCkgYmVmb3JlaGFuZCBvciBieSBpbmNvcnBvcmF0aW5nIGl0IGluIGBzdF9zZWdtZW50c2AuIEkgY291bGQgY2FsbCBpdCBxdWl0cyBoZXJlIGZvciBub3cuIEFsdGhvdWdoIEkgc2FpZCB0aGF0IEkgd2FzIHN0cml2aW5nIHRvd2FyZCBvbmx5IHVzaW5nIGJhc2UgUiBhbmQgYHNmYCwgdGhlIGxhc3QgbG9vcCBhbmQgYG1lcmdlYCAgY2FuIGVhc2lseSBiZSBkb25lIHdpdGggYGRwbHlyYCwgYGRhdGEudGFibGVgLCBvciB0aGUgZnVuY3Rpb24gYGJ5YCwgYW5kIHBvc3NpYmx5IGV2ZW4gZG9uZSBmYXN0ZXIuIEJlY2F1c2Ugb2YgdGhpcywgSSB3ZW50IG9uIHRvIHRlc3QgaWYgdGhpcyB3YXMgdGhlIGNhc2UuIA0KDQpgYGB7ciB0aGUgYWx0ZXJuYXRpdmVzLCBtZXNzYWdlPUYsd2FybmluZz1GfQ0KIyMjIyBkYXRhLnRhYmxlICMjIyMNCnN0X3NlZ21lbnRzX2R0ID0gZnVuY3Rpb24oeCwgbWF4X2xlbmd0aCkgew0KICBsaWJyYXJ5KGRhdGEudGFibGUpDQogIA0KICBuZXQgPSB4DQogIG5ldCRpZCA9IDE6bnJvdyhuZXQpDQogIA0KICBpZiAoIW1pc3NpbmcobWF4X2xlbmd0aCkpIHsNCiAgICBuZXQgPSBzdF9zZWdtZW50aXplKG5ldCwgbWF4X2xlbmd0aCkNCiAgfQ0KICANCiAgY29vcmQgPSBzdF9jb29yZGluYXRlcyhuZXQpDQogIGNvbCA9IG5jb2woY29vcmQpDQogIGlkcyA9IGNvb3JkWywgY29sXQ0KICBmZWF0ID0gdW5pcXVlKGlkcykNCiAgc2VnbWVudHMgPSBsaXN0KCkNCiAgDQogIGZvciAoaSBpbiBmZWF0KSB7DQogICAgY29vcmRzID0gY29vcmRbaWRzID09IGksIF0NCiAgICBmb3IgKGogaW4gMToobnJvdyhjb29yZHMpIC0gMSkpIHsNCiAgICAgIHNlZ21lbnRzW1tsZW5ndGgoc2VnbWVudHMpICsgMV1dID0gc3RfYXNfYmluYXJ5KHN0X2xpbmVzdHJpbmcoY29vcmRzW2o6KGogKyAxKSwgMToyXSkpDQogICAgfQ0KICB9DQogIA0KICBmaWQgPSBhcy5kYXRhLnRhYmxlKGlkcykNCiAgY29sbmFtZXMoZmlkKSA9ICJpZCINCiAgc2V0a2V5KGZpZCwgaWQpDQogIGZpZCA9IGZpZFtmaWRbLCAuSVstMV0sIGJ5ID0gaWRdJFYxXVthcy5kYXRhLnRhYmxlKHN0X2Ryb3BfZ2VvbWV0cnkobmV0KSksIG9uID0gImlkIl0NCiAgZmlkWywgYDo9YChnZW9tZXRyeSA9IHN0X2FzX3NmYyhzZWdtZW50cywgY3JzID0gc3RfY3JzKG5ldCkpLCBpZCA9IE5VTEwpXQ0KICBzdF9zZihmaWQpDQp9DQoNCiMjIyMgZHBseXIgIyMjIw0Kc3Rfc2VnbWVudHNfdGJsID0gZnVuY3Rpb24oeCwgbWF4X2xlbmd0aCkgew0KICBsaWJyYXJ5KHNmKQ0KICBsaWJyYXJ5KGRwbHlyKQ0KICANCiAgbmV0ID0gbXV0YXRlKHgsIHZhbHVlID0gcm93X251bWJlcigpKQ0KICANCiAgaWYgKCFtaXNzaW5nKG1heF9sZW5ndGgpKSB7DQogICAgbmV0ID0gc3Rfc2VnbWVudGl6ZShuZXQsIG1heF9sZW5ndGgpDQogIH0NCiAgDQogIGNvb3JkID0gc3RfY29vcmRpbmF0ZXMobmV0KQ0KICBjb2wgPSBuY29sKGNvb3JkKQ0KICBpZHMgPSBjb29yZFssIGNvbF0NCiAgZmVhdCA9IHVuaXF1ZShpZHMpDQogIHNlZ21lbnRzID0gbGlzdCgpDQogIA0KICBmb3IgKGkgaW4gZmVhdCkgew0KICAgIGNvb3JkcyA9IGNvb3JkW2lkcyA9PSBpLCBdDQogICAgZm9yIChqIGluIDE6KG5yb3coY29vcmRzKSAtIDEpKSB7DQogICAgICBzZWdtZW50c1tbbGVuZ3RoKHNlZ21lbnRzKSArIDFdXSA9IHN0X2FzX2JpbmFyeShzdF9saW5lc3RyaW5nKGNvb3Jkc1tqOihqICsgMSksIDE6Ml0pKQ0KICAgIH0NCiAgfQ0KICANCiAgYXNfdGliYmxlKGlkcykgJT4lDQogICAgZ3JvdXBfYnkodmFsdWUpICU+JQ0KICAgIHNsaWNlKC0xKSAlPiUNCiAgICB1bmdyb3VwICU+JQ0KICAgIGxlZnRfam9pbihzdF9kcm9wX2dlb21ldHJ5KG5ldCksIGJ5ID0gInZhbHVlIikgJT4lDQogICAgc2VsZWN0KC0xKSAlPiUNCiAgICBtdXRhdGUoZ2VvbWV0cnkgPSBzdF9hc19zZmMoc2VnbWVudHMsIGNycyA9IHN0X2NycyhuZXQpKSkgJT4lIHN0X3NmKCkNCn0NCg0KIyMjIyB1c2luZyBieSAjIyMjDQpzdF9zZWdtZW50c19ieSA9IGZ1bmN0aW9uKHgsIG1heF9sZW5ndGgpIHsNCiAgbGlicmFyeShzZikNCiAgDQogIG5ldCA9IHgNCiAgbmV0JGZpZCA9IGZhY3RvcigxOm5yb3cobmV0KSwgbGV2ZWxzID0gMTpucm93KG5ldCkpDQogIA0KICBpZiAoIW1pc3NpbmcobWF4X2xlbmd0aCkpIHsNCiAgICBuZXQgPSBzdF9zZWdtZW50aXplKG5ldCwgbWF4X2xlbmd0aCkNCiAgfQ0KICANCiAgY29vcmQgPSBzdF9jb29yZGluYXRlcyhuZXQpDQogIGNvbCA9IG5jb2woY29vcmQpDQogIGlkcyA9IGNvb3JkWywgY29sXQ0KICBmZWF0ID0gdW5pcXVlKGlkcykNCiAgc2VnbWVudHMgPSBsaXN0KCkNCiAgDQogIGZvciAoaSBpbiBmZWF0KSB7DQogICAgY29vcmRzID0gY29vcmRbaWRzID09IGksIF0NCiAgICBmb3IgKGogaW4gMToobnJvdyhjb29yZHMpIC0gMSkpIHsNCiAgICAgIHNlZ21lbnRzW1tsZW5ndGgoc2VnbWVudHMpICsgMV1dID0gc3RfYXNfYmluYXJ5KHN0X2xpbmVzdHJpbmcoY29vcmRzW2o6KGogKyAxKSwgMToyXSkpDQogICAgfQ0KICB9DQogIA0KIGZpZCA9IGFzLmRhdGEuZnJhbWUoaWRzKQ0KIGNvbG5hbWVzKGZpZCkgPSAiZmlkIg0KIGZpZCRmaWQgPSBmYWN0b3IoZmlkJGZpZCwgbGV2ZWxzID0gdW5pcXVlKGZpZCRmaWQpKQ0KIGZpZCA9IGRvLmNhbGwocmJpbmQsIGJ5KGZpZCwgZmlkJGZpZCwgZnVuY3Rpb24oeCkgeFstMSwsIGRyb3AgPSBGXSwgc2ltcGxpZnkgPSBGKSkNCiBmaWQgPSBtZXJnZShmaWQsIHN0X2Ryb3BfZ2VvbWV0cnkobmV0KSwgYWxsLnggPSBUKVssIC0xLCBkcm9wID0gRl0NCiBmaWQkZ2VvbWV0cnkgPSBzdF9hc19zZmMoc2VnbWVudHMsIGNycyA9IHN0X2NycyhuZXQpKQ0KIHN0X3NmKGZpZCkNCn0NCmBgYA0KVGhlc2UgdGhyZWUgZnVuY3Rpb25zIGFyZSBlc3NlbnRpYWxseSB0aGUgc2FtZSBzYXZlIGZvciBob3cgdGhleSBkZWFsIHdpdGggdGhlIGFic2VuY2Ugb2YgdGhlIHNlY29uZCBsb29wLiBXaGF0IGlzIG5vdCB0aGUgc2FtZSwgaG93ZXZlciwgaXMgdGhlaXIgc3BlZWQuDQpgYGB7ciBhbHRzIGJlbmNobWFyayxtZXNzYWdlPUYsd2FybmluZz1GfQ0KYmVuY2g6Om1hcmsoDQogICJiYXNlIChzdF9zZWdtZW50cykiID0gc3RfZ2VvbWV0cnkoc3Rfc2VnbWVudHMoc3RyZWV0cywxKSksDQogICJiYXNlICh1c2luZyBieSkiID0gc3RfZ2VvbWV0cnkoc3Rfc2VnbWVudHNfYnkoc3RyZWV0cywxKSksDQogIGRhdGEudGFibGUgPSBzdF9nZW9tZXRyeShzdF9zZWdtZW50c19kdChzdHJlZXRzLDEpKSwNCiAgZHBseXIgPSBzdF9nZW9tZXRyeShzdF9zZWdtZW50c190Ymwoc3RyZWV0cywxKSksDQogIGl0ZXJhdGlvbnMgPSA1DQopICU+JQ0KICBtdXRhdGUobmFtZSA9IGFzLmNoYXJhY3RlcihleHByZXNzaW9uKSwNCiAgICAgICAgIG1heCA9IGJlbmNoOjphc19iZW5jaF90aW1lKGxhcHBseSh0aW1lLCBtYXgpKSkgJT4lDQogIHNlbGVjdChuYW1lLCBtaW4sIG1lZGlhbiwgbWF4LCBpdGVyYXRpb25zID0gbl9pdHIsIHRvdGFsX3RpbWUpDQpgYGANCg0KVGhpcyB0aW1lIGFyb3VuZCBJIHVzZWQgdGhlIHNlY29uZCBwYXJhbWV0ZXIgb2YgdGhlc2UgZnVuY3Rpb25zIHRvIHNlZSBob3cgdGhleSBoYW5kbGUgc29tZXdoYXQgbGFyZ2VyIGRhdGFzZXRzLiBSYXRoZXIgdGhhbiBicmVha2luZyA0OTAgbGluZXN0cmluZ3MgaW50byA3LDQyMyBsaW5lIHNlZ21lbnRzLCB0aGUgbWF4IGxlbmd0aCBvZiBlYWNoIGxpbmUgc2VnbWVudCBpcyBzZXQgdG8gb25lIG1ldGVyIGluIHRoaXMgYmVuY2htYXJrIGFuZCB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIGluY3JlYXNlcyBmcm9tIDQ5MCB0byAyMjUsMTE4LiBXaGF0IGlzIGludGVyZXN0aW5nIHRvIHNlZSB3aXRoIHRoaXMgYmVuY2htYXJrIGlzIHRoYXQgYHN0X3NlZ21lbnRzYCBpcyBjb21wYXJhYmxlIGluIHNwZWVkIHRvIGJvdGggdGhlIGBkYXRhLnRhYmxlYCBhbmQgYGRwbHlyYCB2ZXJzaW9ucyBvZiB0aGUgZnVuY3Rpb24gYW5kIGlzIHNsaWdodGx5IGZhc3RlciB0aGFuIHdoZW4gdGhlIGxhc3QgbG9vcCBpcyByZXBsYWNlZCBieSBgYnlgLiBJdCBpcyBhbHNvIHN1cnByaXNpbmcgIHRoYXQgaW4gdGhpcyBzaXR1YXRpb24sIGBkcGx5cmAgaXMgZXZlciBzbyBzbGlnaHRseSBmYXN0ZXIgdGhhbiBgZGF0YS50YWJsZWAsIHRob3VnaCB0aGlzIG1heSBiZSBkdWUgdG8gbXkgaW5leHBlcmllbmNlIHdpdGggdGhlIGxhdHRlciBhbmQgd2hhdCB3YXMgYmVpbmcgZG9uZS4gSSBjb3VsZCB0cnkgdG8gc2VlIHdoYXQgc3BlZWQgaW1wcm92ZW1lbnQgY291bGQgYmUgaGFkIGlmIHRoZSBib3RoIGxvb3BzIHdlcmUgcmVwbGFjZWQgd2l0aCBgZGF0YS50YWJsZWAsIHRob3VnaCB0aGF0IGlzIGZvciBhbm90aGVyIHRpbWUuIEkgYWxyZWFkeSBzYXcgd2l0aCB0aGUgZmlyc3QgYW5kIHNlY29uZCBhdHRlbXB0cyB0aGF0IHdvcmtpbmcgY29tcGxldGVseSB3aXRoaW4gdGhlIGB0aWR5dmVyc2VgIHNsb3dlZCB0aGluZ3MgZG93bi4gR2l2ZW4gdGhlIHJlc3VsdHMgb2YgdGhlIGJlbmNobWFya3MsIEkgY291bGQganVzdCB1c2UgYHN0X3NlZ21lbnRzYCBpbiBpdHMgY3VycmVudCBzdGF0ZS4gSW4gYWxsLCBpdCB3YXMgZnVuIHRvIHRha2UgYSBicmVhayBmcm9tIHRoaW5ncyBhbmQgc3BlbmQgdGltZSB0byBzb2x2ZSBhIFsic2ltcGxlIHlldCB0cmlja3kiXShodHRwOi8vYmxvZy5jbGV2ZXJlbGVwaGFudC5jYS8yMDE1LzAyL2JyZWFraW5nLWxpbmVzdHJpbmctaW50by1zZWdtZW50cy5odG1sKSBwcm9ibGVtIGluIFIuICAgIA0KDQo=